diff mbox

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

Message ID 1452834380-164453-7-git-send-email-agraf@suse.de
State Superseded
Delegated to: Tom Rini
Headers show

Commit Message

Alexander Graf Jan. 15, 2016, 5:06 a.m. UTC
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

Comments

Leif Lindholm Jan. 21, 2016, 5:20 p.m. UTC | #1
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 */
> +}
Simon Glass Jan. 31, 2016, 3:20 p.m. UTC | #2
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
Alexander Graf Feb. 1, 2016, 11:57 p.m. UTC | #3
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 mbox

Patch

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,
+};