[U-Boot] [PATCH 0/2] efi_loader: Implement exit

In some environments it makes sense to try different boot paths in order, for example boot from network, then determine that network boot is not for you based on a configuration file, then continue descending the list to SD boot.
The usual way this works in an EFI world is to just exit th EFI payload. So far we didn't implement the exit call though. This patch set does it.
Alexander Graf (2): arm: Introduce setjmp/longjmp efi_loader: Add exit support
arch/arm/include/asm/setjmp.h | 99 +++++++++++++++++++++++++++++++++++++++++++ cmd/bootefi.c | 6 +++ include/efi_api.h | 10 +++++ lib/efi_loader/efi_boottime.c | 21 ++++++--- 4 files changed, 131 insertions(+), 5 deletions(-) create mode 100644 arch/arm/include/asm/setjmp.h

To quit an EFI application we will need logic to jump to the caller of a function without returning from the function we called into, so we need setjmp/longjmp functionality.
This patch introduces a trivial implementation of these that I verified works on armv7, thumb2 and aarch64.
Signed-off-by: Alexander Graf agraf@suse.de --- arch/arm/include/asm/setjmp.h | 99 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 arch/arm/include/asm/setjmp.h
diff --git a/arch/arm/include/asm/setjmp.h b/arch/arm/include/asm/setjmp.h new file mode 100644 index 0000000..b8b85b7 --- /dev/null +++ b/arch/arm/include/asm/setjmp.h @@ -0,0 +1,99 @@ +/* + * (C) Copyright 2016 + * Alexander Graf agraf@suse.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _SETJMP_H_ +#define _SETJMP_H_ 1 + +struct jmp_buf_data { + ulong target; + ulong regs[5]; +}; + +typedef struct jmp_buf_data jmp_buf[1]; + +static inline int setjmp(jmp_buf jmp) +{ + long r = 0; + +#ifdef CONFIG_ARM64 + asm volatile( + "adr x1, jmp_target\n" + "str x1, %1\n" + "stp x26, x27, %2\n" + "stp x28, x29, %3\n" + "mov x1, sp\n" + "str x1, %4\n" + "b 2f\n" + "jmp_target: " + "mov %0, #1\n" + "2:\n" + : "+r" (r), "=m" (jmp->target), + "=m" (jmp->regs[0]), "=m" (jmp->regs[2]), + "=m" (jmp->regs[4]) + : + : "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", + "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", + "x16", "x17", "x18", "x19", "x20", "x21", "x22", + "x23", "x24", "x25", /* x26, x27, x28, x29, sp */ + "x30", "cc", "memory"); +#else + asm volatile( +#ifdef CONFIG_SYS_THUMB_BUILD + "adr r0, jmp_target + 1\n" +#else + "adr r0, jmp_target\n" +#endif + "mov r1, %1\n" + "mov r2, sp\n" + "stm r1, {r0, r2, r4, r5, r6, r7}\n" + "b 2f\n" + "jmp_target: " + "mov %0, #1\n" + "2:\n" + : "+l" (r) + : "l" (&jmp->target) + : "r0", "r1", "r2", "r3", /* "r4", "r5", "r6", "r7", */ + "r8", "r9", "r10", "r11", /* sp, */ "ip", "lr", + "cc", "memory"); +#endif + +printf("%s:%d target=%#lx\n", __func__, __LINE__, jmp->target); + + return r; +} + +static inline __noreturn void longjmp(jmp_buf jmp) +{ +#ifdef CONFIG_ARM64 + asm volatile( + "ldr x0, %0\n" + "ldr x1, %3\n" + "mov sp, x1\n" + "ldp x26, x27, %1\n" + "ldp x28, x25, %2\n" + "mov x29, x25\n" + "br x0\n" + : + : "m" (jmp->target), "m" (jmp->regs[0]), "m" (jmp->regs[2]), + "m" (jmp->regs[4]) + : "x0", "x1", "x25", "x26", "x27", "x28"); +#else + asm volatile( + "mov r1, %0\n" + "ldm r1, {r0, r2, r4, r5, r6, r7}\n" + "mov sp, r2\n" + "bx r0\n" + : + : "l" (&jmp->target) + : "r1"); +#endif + + while (1) { } +} + + +#endif /* _SETJMP_H_ */

On Fri, May 20, 2016 at 11:28:22PM +0200, Alexander Graf wrote:
To quit an EFI application we will need logic to jump to the caller of a function without returning from the function we called into, so we need setjmp/longjmp functionality.
This patch introduces a trivial implementation of these that I verified works on armv7, thumb2 and aarch64.
Signed-off-by: Alexander Graf agraf@suse.de
Applied to u-boot/master, thanks!

Some times you may want to exit an EFI payload again, for example to default boot into a PXE installation and decide that you would rather want to boot from the local disk instead.
This patch adds exit functionality to the EFI implementation, allowing EFI payloads to exit.
Signed-off-by: Alexander Graf agraf@suse.de --- cmd/bootefi.c | 6 ++++++ include/efi_api.h | 10 ++++++++++ lib/efi_loader/efi_boottime.c | 21 ++++++++++++++++----- 3 files changed, 32 insertions(+), 5 deletions(-)
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index d3a2331..2a62dce 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -209,6 +209,12 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt) #ifdef DEBUG_EFI printf("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry); #endif + + if (setjmp(&loaded_image_info.exit_jmp)) { + efi_status_t status = loaded_image_info.exit_status; + return status == EFI_SUCCESS ? 0 : -EINVAL; + } + return entry(&loaded_image_info, &systab); }
diff --git a/include/efi_api.h b/include/efi_api.h index 20035d7..f572b88 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -17,6 +17,10 @@
#include <efi.h>
+#ifdef CONFIG_EFI_LOADER +#include <asm/setjmp.h> +#endif + /* Types and defines for EFI CreateEvent */ enum efi_event_type { EFI_TIMER_STOP = 0, @@ -239,6 +243,12 @@ struct efi_loaded_image { unsigned int image_code_type; unsigned int image_data_type; unsigned long unload; + + /* Below are efi loader private fields */ +#ifdef CONFIG_EFI_LOADER + efi_status_t exit_status; + struct jmp_buf_data exit_jmp; +#endif };
#define DEVICE_PATH_GUID \ diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 9daca50..15a1b90 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -458,19 +458,30 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, efi_is_direct_boot = false;
/* call the image! */ + if (setjmp(&info->exit_jmp)) { + /* We returned from the child image */ + return EFI_EXIT(info->exit_status); + } + entry(image_handle, &systab);
/* Should usually never get here */ return EFI_EXIT(EFI_SUCCESS); }
-static efi_status_t EFIAPI efi_exit(void *image_handle, long exit_status, - unsigned long exit_data_size, - uint16_t *exit_data) +static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle, + efi_status_t exit_status, unsigned long exit_data_size, + int16_t *exit_data) { + struct efi_loaded_image *loaded_image_info = (void*)image_handle; + EFI_ENTRY("%p, %ld, %ld, %p", image_handle, exit_status, exit_data_size, exit_data); - return EFI_EXIT(efi_unsupported(__func__)); + + loaded_image_info->exit_status = exit_status; + longjmp(&loaded_image_info->exit_jmp); + + panic("EFI application exited"); }
static struct efi_object *efi_search_obj(void *handle) @@ -746,7 +757,7 @@ static const struct efi_boot_services efi_boot_services = { .install_configuration_table = efi_install_configuration_table, .load_image = efi_load_image, .start_image = efi_start_image, - .exit = (void*)efi_exit, + .exit = efi_exit, .unload_image = efi_unload_image, .exit_boot_services = efi_exit_boot_services, .get_next_monotonic_count = efi_get_next_monotonic_count,

On Fri, May 20, 2016 at 11:28:23PM +0200, Alexander Graf wrote:
Some times you may want to exit an EFI payload again, for example to default boot into a PXE installation and decide that you would rather want to boot from the local disk instead.
This patch adds exit functionality to the EFI implementation, allowing EFI payloads to exit.
Signed-off-by: Alexander Graf agraf@suse.de
Applied to u-boot/master, thanks!
participants (2)
-
Alexander Graf
-
Tom Rini