[U-Boot] [PATCH 06/14] efi_loader: Add runtime services

Simon Glass
2016-01-31T15:20:44Z

Hi Alexander,

On 14 January 2016 at 22:06, Alexander Graf <agraf@suse.de> wrote:
> After booting has finished, EFI allows firmware to still interact with the OS
> using the "runtime services". These callbacks live in a separate address space,
> since they are available long after U-Boot has been overwritten by the OS.
>
> This patch adds enough framework for arbitrary code inside of U-Boot to become
> a runtime service with the right section attributes set. For now, we don't make
> use of it yet though.
>
> We could maybe in the future map U-boot environment variables to EFI variables
> here.
>
> Signed-off-by: Alexander Graf <agraf@suse.de>
>
> ---
>
> v1 -> v2:
>
> - Fix runtime service sections
> - Add runtime detach
> - Enable runtime relocations
> - Add get_time
> - Fix relocation
> - Fix 32bit
> - Add am335x support
> - Move section definition to header
> - Add systab to runtime section
> - Add self-relocation hook table
> - Fix self-relocation
> - Relocate efi_runtime section early during bootup
> - Fix return values for a number of callbacks to be more UEFI compliant
> - Move to GPLv2+
> ---
> arch/arm/config.mk | 4 +
> arch/arm/cpu/armv8/u-boot.lds | 16 +++
> arch/arm/cpu/u-boot.lds | 30 +++++
> arch/arm/lib/sections.c | 4 +
> board/ti/am335x/u-boot.lds | 30 +++++
> common/board_r.c | 4 +
> include/efi_loader.h | 10 ++
> lib/efi_loader/efi_boottime.c | 6 +-
> lib/efi_loader/efi_runtime.c | 300 ++++++++++++++++++++++++++++++++++++++++++
> 9 files changed, 401 insertions(+), 3 deletions(-)
> create mode 100644 lib/efi_loader/efi_runtime.c

Reviewed-by: Simon Glass <sjg@chromium.org>

A few nits.

>
> diff --git a/arch/arm/config.mk b/arch/arm/config.mk
> index 0550225..ecb956d 100644
> --- a/arch/arm/config.mk
> +++ b/arch/arm/config.mk
> @@ -120,6 +120,10 @@ ifdef CONFIG_OF_EMBED
> OBJCOPYFLAGS += -j .dtb.init.rodata
> endif
>
> +ifdef CONFIG_EFI_LOADER
> +OBJCOPYFLAGS += -j .efi_runtime -j .efi_runtime_rel
> +endif
> +
> ifneq ($(CONFIG_IMX_CONFIG),)
> ifdef CONFIG_SPL
> ifndef CONFIG_SPL_BUILD
> diff --git a/arch/arm/cpu/armv8/u-boot.lds b/arch/arm/cpu/armv8/u-boot.lds
> index 4c12222..fd15ad5 100644
> --- a/arch/arm/cpu/armv8/u-boot.lds
> +++ b/arch/arm/cpu/armv8/u-boot.lds
> @@ -42,6 +42,22 @@ SECTIONS
>
> . = ALIGN(8);
>
> + .efi_runtime : {
> + __efi_runtime_start = .;
> + *(efi_runtime_text)
> + *(efi_runtime_data)
> + __efi_runtime_stop = .;
> + }
> +
> + .efi_runtime_rel : {
> + __efi_runtime_rel_start = .;
> + *(.relaefi_runtime_text)
> + *(.relaefi_runtime_data)
> + __efi_runtime_rel_stop = .;
> + }
> +
> + . = ALIGN(8);
> +
> .image_copy_end :
> {
> *(.__image_copy_end)
> diff --git a/arch/arm/cpu/u-boot.lds b/arch/arm/cpu/u-boot.lds
> index d48a905..596a17d 100644
> --- a/arch/arm/cpu/u-boot.lds
> +++ b/arch/arm/cpu/u-boot.lds
> @@ -89,6 +89,36 @@ SECTIONS
>
> . = ALIGN(4);
>
> + .__efi_runtime_start : {
> + *(.__efi_runtime_start)
> + }
> +
> + .efi_runtime : {
> + *(efi_runtime_text)
> + *(efi_runtime_data)
> + }
> +
> + .__efi_runtime_stop : {
> + *(.__efi_runtime_stop)
> + }
> +
> + .efi_runtime_rel_start :
> + {
> + *(.__efi_runtime_rel_start)
> + }
> +
> + .efi_runtime_rel : {
> + *(.relefi_runtime_text)
> + *(.relefi_runtime_data)
> + }
> +
> + .efi_runtime_rel_stop :
> + {
> + *(.__efi_runtime_rel_stop)
> + }
> +
> + . = ALIGN(4);
> +
> .image_copy_end :
> {
> *(.__image_copy_end)
> diff --git a/arch/arm/lib/sections.c b/arch/arm/lib/sections.c
> index a1205c3..6a94522 100644
> --- a/arch/arm/lib/sections.c
> +++ b/arch/arm/lib/sections.c
> @@ -27,4 +27,8 @@ char __rel_dyn_start[0] __attribute__((section(".__rel_dyn_start")));
> char __rel_dyn_end[0] __attribute__((section(".__rel_dyn_end")));
> char __secure_start[0] __attribute__((section(".__secure_start")));
> char __secure_end[0] __attribute__((section(".__secure_end")));
> +char __efi_runtime_start[0] __attribute__((section(".__efi_runtime_start")));
> +char __efi_runtime_stop[0] __attribute__((section(".__efi_runtime_stop")));
> +char __efi_runtime_rel_start[0] __attribute__((section(".__efi_runtime_rel_start")));
> +char __efi_runtime_rel_stop[0] __attribute__((section(".__efi_runtime_rel_stop")));
> char _end[0] __attribute__((section(".__end")));
> diff --git a/board/ti/am335x/u-boot.lds b/board/ti/am335x/u-boot.lds
> index 78f294a..a56cc82 100644
> --- a/board/ti/am335x/u-boot.lds
> +++ b/board/ti/am335x/u-boot.lds
> @@ -59,6 +59,36 @@ SECTIONS
>
> . = ALIGN(4);
>
> + .__efi_runtime_start : {
> + *(.__efi_runtime_start)
> + }
> +
> + .efi_runtime : {
> + *(efi_runtime_text)
> + *(efi_runtime_data)
> + }
> +
> + .__efi_runtime_stop : {
> + *(.__efi_runtime_stop)
> + }
> +
> + .efi_runtime_rel_start :
> + {
> + *(.__efi_runtime_rel_start)
> + }
> +
> + .efi_runtime_rel : {
> + *(.relefi_runtime_text)
> + *(.relefi_runtime_data)
> + }
> +
> + .efi_runtime_rel_stop :
> + {
> + *(.__efi_runtime_rel_stop)
> + }
> +
> + . = ALIGN(4);
> +
> .image_copy_end :
> {
> *(.__image_copy_end)
> diff --git a/common/board_r.c b/common/board_r.c
> index 75ee43e..420e2c8 100644
> --- a/common/board_r.c
> +++ b/common/board_r.c
> @@ -64,6 +64,7 @@
> #ifdef CONFIG_AVR32
> #include <asm/arch/mmu.h>
> #endif
> +#include <efi_loader.h>
>
> DECLARE_GLOBAL_DATA_PTR;
>
> @@ -176,6 +177,9 @@ static int initr_reloc_global_data(void)
> */
> gd->fdt_blob += gd->reloc_off;
> #endif
> +#ifdef CONFIG_EFI_LOADER
> + efi_runtime_relocate(gd->relocaddr, NULL);
> +#endif
>
> return 0;
> }
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index d314002..0f821ff 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -29,6 +29,7 @@
>
> #define EFI_EXIT(ret) efi_exit_func(ret);
>
> +extern struct efi_runtime_services efi_runtime_services;
> extern struct efi_system_table systab;
>
> extern const struct efi_simple_text_output_protocol efi_con_out;
> @@ -39,6 +40,9 @@ extern const efi_guid_t efi_guid_console_control;
> extern const efi_guid_t efi_guid_device_path;
> extern const efi_guid_t efi_guid_loaded_image;
>
> +extern unsigned int __efi_runtime_start, __efi_runtime_stop;
> +extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
> +
> struct efi_class_map {
> const efi_guid_t *guid;
> const void *interface;
> @@ -68,12 +72,18 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info);
> void efi_save_gd(void);
> void efi_restore_gd(void);
> efi_status_t efi_exit_func(efi_status_t ret);
> +void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map);

Function comment please.

>
> #define EFI_LOADER_POOL_SIZE (128 * 1024 * 1024)
> void *efi_loader_alloc(uint64_t len);
>
> +#define EFI_RUNTIME_DATA __attribute__ ((section ("efi_runtime_data")))
> +#define EFI_RUNTIME_TEXT __attribute__ ((section ("efi_runtime_text")))
> +
> #else /* defined(EFI_LOADER) */
>
> +#define EFI_RUNTIME_DATA
> +#define EFI_RUNTIME_TEXT
> static inline void efi_restore_gd(void) { }
>
> #endif
> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> index 5756c9c..45217ef 100644
> --- a/lib/efi_loader/efi_boottime.c
> +++ b/lib/efi_loader/efi_boottime.c
> @@ -36,7 +36,7 @@ static bool efi_is_direct_boot = true;
> * In most cases we want to pass an FDT to the payload, so reserve one slot of
> * config table space for it. The pointer gets populated by do_bootefi_exec().
> */
> -static struct efi_configuration_table efi_conf_table[] = {
> +static struct efi_configuration_table EFI_RUNTIME_DATA efi_conf_table[] = {
> {
> .guid = EFI_FDT_GUID,
> },
> @@ -741,10 +741,10 @@ static const struct efi_boot_services efi_boot_services = {
> };
>
>
> -static uint16_t firmware_vendor[] =
> +static uint16_t EFI_RUNTIME_DATA firmware_vendor[] =
> { 'D','a','s',' ','U','-','b','o','o','t',0 };
>
> -struct efi_system_table systab = {
> +struct efi_system_table EFI_RUNTIME_DATA systab = {
> .hdr = {
> .signature = EFI_SYSTEM_TABLE_SIGNATURE,
> .revision = 0x20005, /* 2.5 */
> diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
> new file mode 100644
> index 0000000..b7aa1e9
> --- /dev/null
> +++ b/lib/efi_loader/efi_runtime.c
> @@ -0,0 +1,300 @@
> +/*
> + * EFI application runtime services
> + *
> + * Copyright (c) 2016 Alexander Graf
> + *
> + * SPDX-License-Identifier: GPL-2.0+
> + */
> +
> +#include <common.h>

You use driver model so need <dm.h> too.

> +#include <efi_loader.h>
> +#include <command.h>
> +#include <asm/global_data.h>
> +#include <rtc.h>
> +
> +/* For manual relocation support */
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +static efi_status_t EFI_RUNTIME_TEXT efi_unimplemented(void);
> +static efi_status_t EFI_RUNTIME_TEXT efi_device_error(void);
> +static efi_status_t EFI_RUNTIME_TEXT efi_invalid_parameter(void);
> +
> +#if defined(CONFIG_ARM64)
> +#define R_RELATIVE 1027
> +#define R_MASK 0xffffffffULL
> +#define IS_RELA 1
> +#elif defined(CONFIG_ARM)
> +#define R_RELATIVE 23
> +#define R_MASK 0xffULL
> +#else
> +#error Need to add relocation awareness
> +#endif
> +
> +struct elf_rel {
> + ulong *offset;
> + ulong info;
> +};
> +
> +struct elf_rela {
> + ulong *offset;
> + ulong info;
> + long addend;
> +};

Can you use elf.h?

> +
> +/*
> + * EFI Runtime code lives in 2 stages. In the first stage, U-Boot and an EFI
> + * payload are running concurrently at the same time. In this mode, we can
> + * handle a good number of runtime callbacks
> + */
> +
> +static void efi_reset_system(enum efi_reset_type reset_type,
> + efi_status_t reset_status, unsigned long data_size,
> + void *reset_data)
> +{
> + EFI_ENTRY("%d %lx %lx %p", reset_type, reset_status, data_size, reset_data);
> +
> + switch (reset_type) {
> + case EFI_RESET_COLD:
> + case EFI_RESET_WARM:
> + do_reset(NULL, 0, 0, NULL);
> + break;
> + case EFI_RESET_SHUTDOWN:
> + /* We don't have anything to map this to */
> + break;
> + }
> +
> + EFI_EXIT(EFI_SUCCESS);
> +}
> +
> +static efi_status_t efi_get_time(struct efi_time *time,
> + struct efi_time_cap *capabilities)
> +{
> +#ifdef CONFIG_CMD_DATE
> +
> + struct rtc_time tm;
> + int r;
> +#ifdef CONFIG_DM_RTC

Since this is a new service I don't think you need to support the old RTC API.

> + struct udevice *dev;
> +#endif
> +
> + EFI_ENTRY("%p %p", time, capabilities);
> +
> +#ifdef CONFIG_DM_RTC
> + r = uclass_get_device(UCLASS_RTC, 0, &dev);
> + if (r)
> + return EFI_EXIT(EFI_UNSUPPORTED);
> +#endif
> +
> +#ifdef CONFIG_DM_RTC
> + r = dm_rtc_get(dev, &tm);
> +#else
> + r = rtc_get(&tm);
> +#endif
> + if (r)
> + return EFI_EXIT(EFI_UNSUPPORTED);
> +
> + memset(time, 0, sizeof(*time));
> + time->year = tm.tm_year;
> + time->month = tm.tm_mon;
> + time->day = tm.tm_mday;
> + time->hour = tm.tm_hour;
> + time->minute = tm.tm_min;
> + time->daylight = tm.tm_isdst;
> +
> + return EFI_EXIT(EFI_SUCCESS);
> +
> +#else /* CONFIG_CMD_DATE */
> +
> + return EFI_DEVICE_ERROR;
> +
> +#endif /* CONFIG_CMD_DATE */
> +}
> +
> +struct efi_runtime_detach_list_struct {
> + void *ptr;
> + void *patchto;
> +};
> +
> +static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
> + {
> + /* do_reset is gone */
> + .ptr = &efi_runtime_services.reset_system,
> + .patchto = &efi_unimplemented,
> + }, {
> + /* invalidate_*cache_all are gone */
> + .ptr = &efi_runtime_services.set_virtual_address_map,
> + .patchto = &efi_invalid_parameter,
> + }, {
> + /* RTC accessors are gone */
> + .ptr = &efi_runtime_services.get_time,
> + .patchto = &efi_device_error,
> + },
> +};
> +
> +static bool efi_runtime_tobedetached(void *p)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++)
> + if (efi_runtime_detach_list[i].ptr == p)
> + return true;
> +
> + return false;
> +}
> +
> +static void efi_runtime_detach(ulong offset)
> +{
> + int i;
> + ulong patchoff = offset - (ulong)gd->relocaddr;
> +
> + for (i = 0; i < ARRAY_SIZE(efi_runtime_detach_list); i++) {
> + ulong patchto = (ulong)efi_runtime_detach_list[i].patchto;
> + ulong *p = efi_runtime_detach_list[i].ptr;
> + ulong newaddr = patchto + patchoff;
> +
> +#ifdef DEBUG_EFI
> + printf("%s: Setting %p to %lx\n", __func__, p, newaddr);
> +#endif
> + *p = newaddr;
> + }
> +}
> +
> +/* Relocate EFI runtime to uboot_reloc_base = offset */
> +void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
> +{
> +#ifdef IS_RELA
> + struct elf_rela *rel = (void*)&__efi_runtime_rel_start;
> +#else
> + struct elf_rel *rel = (void*)&__efi_runtime_rel_start;
> + static ulong lastoff = CONFIG_SYS_TEXT_BASE;
> +#endif
> +
> +#ifdef DEBUG_EFI
> + printf("%s: Relocating to offset=%lx\n", __func__, offset);
> +#endif
> +
> + for (; (ulong)rel < (ulong)&__efi_runtime_rel_stop; rel++) {
> + ulong base = CONFIG_SYS_TEXT_BASE;
> + ulong *p;
> + ulong newaddr;
> +
> + p = (void*)((ulong)rel->offset - base) + gd->relocaddr;
> +
> + if ((rel->info & R_MASK) != R_RELATIVE) {
> + continue;
> + }
> +
> +#ifdef IS_RELA
> + newaddr = rel->addend + offset - CONFIG_SYS_TEXT_BASE;
> +#else
> + newaddr = *p - lastoff + offset;
> +#endif
> +
> + /* Check if the relocation is inside bounds */
> + if (map && ((newaddr < map->virtual_start) ||
> + newaddr > (map->virtual_start + (map->num_pages << 12)))) {
> + if (!efi_runtime_tobedetached(p))
> + printf("U-Boot EFI: Relocation at %p is out of "
> + "range (%lx)\n", p, newaddr);
> + continue;
> + }
> +
> +#ifdef DEBUG_EFI
> + printf("%s: Setting %p to %lx\n", __func__, p, newaddr);
> +#endif
> +
> + *p = newaddr;
> + flush_dcache_range((ulong)p, (ulong)&p[1]);
> + }
> +
> +#ifndef IS_RELA
> + lastoff = offset;
> +#endif
> +
> + invalidate_icache_all();
> +}
> +
> +static efi_status_t efi_set_virtual_address_map(
> + unsigned long memory_map_size,
> + unsigned long descriptor_size,
> + uint32_t descriptor_version,
> + struct efi_mem_desc *virtmap)
> +{
> + ulong runtime_start = (ulong)&__efi_runtime_start & ~0xfffULL;
> + int n = memory_map_size / descriptor_size;
> + int i;
> +
> + EFI_ENTRY("%lx %lx %x %p", memory_map_size, descriptor_size,
> + descriptor_version, virtmap);
> +
> + for (i = 0; i < n; i++) {
> + struct efi_mem_desc *map;
> +
> + map = (void*)virtmap + (descriptor_size * i);
> + if (map->type == EFI_RUNTIME_SERVICES_CODE) {
> + ulong new_offset = map->virtual_start - (runtime_start - gd->relocaddr);
> +
> + efi_runtime_relocate(new_offset, map);
> + /* Once we're virtual, we can no longer handle
> + complex callbacks */
> + efi_runtime_detach(new_offset);
> + return EFI_EXIT(EFI_SUCCESS);
> + }
> + }
> +
> + return EFI_EXIT(EFI_INVALID_PARAMETER);
> +}
> +
> +/*
> + * In the second stage, U-Boot has disappeared. To isolate our runtime code
> + * that at this point still exists from the rest, we put it into a special
> + * section.
> + *
> + * !!WARNING!!
> + *
> + * This means that we can not rely on any code outside of this file in any
> + * function or variable below this line.
> + *
> + * Please keep everything fully self-contained and annotated with
> + * EFI_RUNTIME_TEXT and EFI_RUNTIME_DATA markers.
> + */
> +
> +/*
> + * Relocate the EFI runtime stub to a different place. We need to call this
> + * the first time we expose the runtime interface to a user and on set virtual
> + * address map calls.
> + */
> +
> +static efi_status_t EFI_RUNTIME_TEXT efi_unimplemented(void)
> +{
> + return EFI_UNSUPPORTED;
> +}
> +
> +static efi_status_t EFI_RUNTIME_TEXT efi_device_error(void)
> +{
> + return EFI_DEVICE_ERROR;
> +}
> +
> +static efi_status_t EFI_RUNTIME_TEXT efi_invalid_parameter(void)
> +{
> + return EFI_INVALID_PARAMETER;
> +}
> +
> +struct efi_runtime_services EFI_RUNTIME_DATA efi_runtime_services = {
> + .hdr = {
> + .signature = EFI_RUNTIME_SERVICES_SIGNATURE,
> + .revision = EFI_RUNTIME_SERVICES_REVISION,
> + .headersize = sizeof(struct efi_table_hdr),
> + },
> + .get_time = &efi_get_time,
> + .set_time = (void *)&efi_device_error,
> + .get_wakeup_time = (void *)&efi_unimplemented,
> + .set_wakeup_time = (void *)&efi_unimplemented,
> + .set_virtual_address_map = &efi_set_virtual_address_map,
> + .convert_pointer = (void *)&efi_invalid_parameter,
> + .get_variable = (void *)&efi_device_error,
> + .get_next_variable = (void *)&efi_device_error,
> + .set_variable = (void *)&efi_device_error,
> + .get_next_high_mono_count = (void *)&efi_device_error,
> + .reset_system = &efi_reset_system,
> +};
> --
> 2.1.4

Regards,
Simon