Message ID | 1452834380-164453-7-git-send-email-agraf@suse.de |
---|---|
State | Superseded |
Delegated to: | Tom Rini |
Headers | show |
On Fri, Jan 15, 2016 at 06:06:12AM +0100, Alexander Graf 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> Just a couple of return value issues: > --- > > 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 > ... > 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> > +#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; > +}; > + > +/* > + * 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 > + 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); EFI_DEVICE_ERROR? > +#endif > + > +#ifdef CONFIG_DM_RTC > + r = dm_rtc_get(dev, &tm); > +#else > + r = rtc_get(&tm); > +#endif > + if (r) > + return EFI_EXIT(EFI_UNSUPPORTED); EFI_DEVICE_ERROR? > + > + 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 */ > +}
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
On 01/31/2016 04:20 PM, Simon Glass wrote: > 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? I could, but elf.h has a hard distinction between 32bit and 64bit binaries while I really just want the one the binary is actually using at this specific moment. So I could add #ifdefs on 32bit vs 64bit and then use the elf.h defines instead, but I'm not sure the code would look any more readable than it is now after that ;). > >> + >> +/* >> + * 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. ok Alex
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); #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> +#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; +}; + +/* + * 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 + 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, +};
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