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

Simon Glass sjg at chromium.org
Sun Jan 31 16:20:44 CET 2016


Hi Alexander,

On 14 January 2016 at 22:06, Alexander Graf <agraf at 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 at 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 at 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


More information about the U-Boot mailing list