[U-Boot] [PATCH v0 20/20] efi_loader: add bootmgr
Heinrich Schuchardt
xypron.glpk at gmx.de
Fri Aug 4 20:06:56 UTC 2017
On 08/04/2017 09:32 PM, Rob Clark wrote:
> Similar to a "real" UEFI implementation, the bootmgr looks at the
> BootOrder and BootXXXX variables to try to find an EFI payload to load
> and boot. This is added as a sub-command of bootefi.
>
> The idea is that the distro bootcmd would first try loading a payload
> via the bootmgr, and then if that fails (ie. first boot or corrupted
> EFI variables) it would fallback to loading bootaa64.efi. (Which
> would then load fallback.efi which would look for \EFI\*\boot.csv and
> populate BootOrder and BootXXXX based on what it found.)
I wonder if this implementation is Fedora specific.
For Debian I could not find any reference to boot.csv.
And it is not mentioned in the UEFI 2.7 spec.
Please, provide the specification that your work is based on.
Best regards
Heinrich
>
> Signed-off-by: Rob Clark <robdclark at gmail.com>
> ---
> cmd/bootefi.c | 48 ++++++++++-
> include/config_distro_bootcmd.h | 5 ++
> include/efi_api.h | 4 +
> include/efi_loader.h | 6 ++
> lib/efi_loader/Makefile | 2 +-
> lib/efi_loader/efi_bootmgr.c | 169 ++++++++++++++++++++++++++++++++++++++
> lib/efi_loader/efi_boottime.c | 6 +-
> lib/efi_loader/efi_image_loader.c | 1 +
> 8 files changed, 235 insertions(+), 6 deletions(-)
> create mode 100644 lib/efi_loader/efi_bootmgr.c
>
> diff --git a/cmd/bootefi.c b/cmd/bootefi.c
> index 80f52e9e35..02a0dd159b 100644
> --- a/cmd/bootefi.c
> +++ b/cmd/bootefi.c
> @@ -219,6 +219,36 @@ exit:
> return ret;
> }
>
> +static int do_bootefi_bootmgr_exec(unsigned long fdt_addr)
> +{
> + struct efi_device_path *device_path, *file_path;
> + void *addr;
> + efi_status_t r;
> +
> + /* Initialize and populate EFI object list */
> + if (!efi_obj_list_initalized)
> + efi_init_obj_list();
> +
> + /*
> + * gd lives in a fixed register which may get clobbered while we execute
> + * the payload. So save it here and restore it on every callback entry
> + */
> + efi_save_gd();
> +
> + addr = efi_bootmgr_load(&device_path, &file_path);
> + if (!addr)
> + return 1;
> +
> + printf("## Starting EFI application at %p ...\n", addr);
> + r = do_bootefi_exec(addr, (void*)fdt_addr, device_path, file_path);
> + printf("## Application terminated, r = %lu\n",
> + r & ~EFI_ERROR_MASK);
> +
> + if (r != EFI_SUCCESS)
> + return 1;
> +
> + return 0;
> +}
>
> /* Interpreter command to boot an arbitrary EFI image from memory */
> static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> @@ -237,7 +267,14 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> memcpy((char *)addr, __efi_hello_world_begin, size);
> } else
> #endif
> - {
> + if (!strcmp(argv[1], "bootmgr")) {
> + unsigned long fdt_addr = 0;
> +
> + if (argc > 2)
> + fdt_addr = simple_strtoul(argv[2], NULL, 16);
> +
> + return do_bootefi_bootmgr_exec(fdt_addr);
> + } else {
> saddr = argv[1];
>
> addr = simple_strtoul(saddr, NULL, 16);
> @@ -270,7 +307,11 @@ static char bootefi_help_text[] =
> "hello\n"
> " - boot a sample Hello World application stored within U-Boot"
> #endif
> - ;
> + "bootmgr [fdt addr]\n"
> + " - load and boot EFI payload based on BootOrder/BootXXXX variables.\n"
> + "\n"
> + " If specified, the device tree located at <fdt address> gets\n"
> + " exposed as EFI configuration table.\n";
> #endif
>
> U_BOOT_CMD(
> @@ -308,6 +349,9 @@ void efi_set_bootdev(const char *dev, const char *devnr, const char *path)
> #endif
> }
>
> + if (!path)
> + return;
> +
> if (strcmp(dev, "Net")) {
> /* Add leading / to fs paths, because they're absolute */
> snprintf(filename, sizeof(filename), "/%s", path);
> diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h
> index d8dab8e46a..94ccab02d2 100644
> --- a/include/config_distro_bootcmd.h
> +++ b/include/config_distro_bootcmd.h
> @@ -112,6 +112,11 @@
>
> #define BOOTENV_SHARED_EFI \
> "boot_efi_binary=" \
> + "if fdt addr ${fdt_addr_r}; then " \
> + "bootefi bootmgr ${fdt_addr_r};" \
> + "else " \
> + "bootefi bootmgr ${fdtcontroladdr};" \
> + "fi;" \
> "load ${devtype} ${devnum}:${distro_bootpart} " \
> "${kernel_addr_r} efi/boot/"BOOTEFI_NAME"; " \
> "if fdt addr ${fdt_addr_r}; then " \
> diff --git a/include/efi_api.h b/include/efi_api.h
> index 1a542846b3..ef91e34c7b 100644
> --- a/include/efi_api.h
> +++ b/include/efi_api.h
> @@ -211,6 +211,10 @@ struct efi_runtime_services {
> EFI_GUID(0x00000000, 0x0000, 0x0000, 0x00, 0x00, \
> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
>
> +#define EFI_GLOBAL_VARIABLE_GUID \
> + EFI_GUID(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, \
> + 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c)
> +
> #define LOADED_IMAGE_PROTOCOL_GUID \
> EFI_GUID(0x5b1b31a1, 0x9562, 0x11d2, 0x8e, 0x3f, \
> 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index f14fdfa58e..e3eb932f54 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -61,6 +61,7 @@ extern const struct efi_device_path_to_text_protocol efi_device_path_to_text;
>
> uint16_t *efi_dp_str(struct efi_device_path *dp);
>
> +extern const efi_guid_t efi_global_variable_guid;
> 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;
> @@ -219,6 +220,8 @@ efi_status_t efi_get_protocol(struct efi_object *efiobj,
> void efi_setup_loaded_image(struct efi_loaded_image *info, struct efi_object *obj,
> struct efi_device_path *device_path,
> struct efi_device_path *file_path);
> +efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
> + void **buffer);
>
> #ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
> extern void *efi_bounce_buffer;
> @@ -306,6 +309,9 @@ efi_status_t EFIAPI efi_set_variable(s16 *variable_name,
> efi_guid_t *vendor, u32 attributes,
> unsigned long data_size, void *data);
>
> +void *efi_bootmgr_load(struct efi_device_path **device_path,
> + struct efi_device_path **file_path);
> +
> #else /* defined(EFI_LOADER) && !defined(CONFIG_SPL_BUILD) */
>
> /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
> diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
> index f58cb13337..930c0e218e 100644
> --- a/lib/efi_loader/Makefile
> +++ b/lib/efi_loader/Makefile
> @@ -16,7 +16,7 @@ always := $(efiprogs-y)
> obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
> obj-y += efi_image_loader.o efi_boottime.o efi_runtime.o efi_console.o
> obj-y += efi_memory.o efi_device_path_to_text.o efi_device_path.o
> -obj-y += efi_file.o efi_variable.o
> +obj-y += efi_file.o efi_variable.o efi_bootmgr.o
> obj-$(CONFIG_LCD) += efi_gop.o
> obj-$(CONFIG_DM_VIDEO) += efi_gop.o
> obj-$(CONFIG_PARTITIONS) += efi_disk.o
> diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
> new file mode 100644
> index 0000000000..8246ddd48f
> --- /dev/null
> +++ b/lib/efi_loader/efi_bootmgr.c
> @@ -0,0 +1,169 @@
> +/*
> + * EFI utils
> + *
> + * Copyright (c) 2017 Rob Clark
> + *
> + * SPDX-License-Identifier: GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <charset.h>
> +#include <malloc.h>
> +#include <efi_loader.h>
> +
> +static const struct efi_boot_services *bs;
> +static const struct efi_runtime_services *rs;
> +
> +#define LOAD_OPTION_ACTIVE 0x00000001
> +#define LOAD_OPTION_FORCE_RECONNECT 0x00000002
> +#define LOAD_OPTION_HIDDEN 0x00000008
> +
> +/*
> + * bootmgr implements the logic of trying to find a payload to boot
> + * based on the BootOrder + BootXXXX variables, and then loading it.
> + *
> + * TODO detecting a special key held (f9?) and displaying a boot menu
> + * like you would get on a PC would be clever.
> + *
> + * TODO if we had a way to write and persist variables after the OS
> + * has started, we'd also want to check OsIndications to see if we
> + * should do normal or recovery boot.
> + */
> +
> +
> +/*
> + * See section 3.1.3 in the v2.7 UEFI spec for more details on
> + * the layout of EFI_LOAD_OPTION. In short it is:
> + *
> + * typedef struct _EFI_LOAD_OPTION {
> + * UINT32 Attributes;
> + * UINT16 FilePathListLength;
> + * // CHAR16 Description[]; <-- variable length, NULL terminated
> + * // EFI_DEVICE_PATH_PROTOCOL FilePathList[]; <-- FilePathListLength bytes
> + * // UINT8 OptionalData[];
> + * } EFI_LOAD_OPTION;
> + */
> +struct load_option {
> + u32 attributes;
> + u16 file_path_length;
> + u16 *label;
> + struct efi_device_path *file_path;
> + u8 *optional_data;
> +};
> +
> +static void parse_load_option(struct load_option *lo, void *ptr)
> +{
> + lo->attributes = *(u32 *)ptr;
> + ptr += sizeof(u32);
> +
> + lo->file_path_length = *(u16 *)ptr;
> + ptr += sizeof(u16);
> +
> + lo->label = ptr;
> + ptr += (utf16_strlen(lo->label) + 1) * 2;
> +
> + lo->file_path = ptr;
> + ptr += lo->file_path_length;
> +
> + lo->optional_data = ptr;
> +}
> +
> +/* free() the result */
> +static void *get_var(u16 *name, const efi_guid_t *vendor,
> + unsigned long *size)
> +{
> + efi_guid_t *v = (efi_guid_t *)vendor;
> + efi_status_t ret;
> + void *buf = NULL;
> +
> + *size = 0;
> + EFI_CALL(ret = rs->get_variable((s16 *)name, v, NULL, size, buf));
> + if (ret == EFI_BUFFER_TOO_SMALL) {
> + buf = malloc(*size);
> + EFI_CALL(ret = rs->get_variable((s16 *)name, v, NULL, size, buf));
> + }
> +
> + if (ret != EFI_SUCCESS) {
> + free(buf);
> + *size = 0;
> + return NULL;
> + }
> +
> + return buf;
> +}
> +
> +static void *try_load_entry(uint16_t n, struct efi_device_path **device_path,
> + struct efi_device_path **file_path)
> +{
> + struct load_option lo;
> + u16 varname[] = L"Boot0000";
> + u16 hexmap[] = L"0123456789ABCDEF";
> + void *load_option, *image = NULL;
> + unsigned long size;
> +
> + varname[4] = hexmap[(n & 0xf000) >> 12];
> + varname[5] = hexmap[(n & 0x0f00) >> 8];
> + varname[6] = hexmap[(n & 0x00f0) >> 4];
> + varname[7] = hexmap[(n & 0x000f) >> 0];
> +
> + load_option = get_var(varname, &efi_global_variable_guid, &size);
> + if (!load_option)
> + return NULL;
> +
> + parse_load_option(&lo, load_option);
> +
> + if (lo.attributes & LOAD_OPTION_ACTIVE) {
> + efi_status_t ret;
> + u16 *str = NULL;
> +
> + debug("%s: trying to load \"%ls\" from: %ls\n", __func__,
> + lo.label, (str = efi_dp_str(lo.file_path)));
> + efi_free_pool(str);
> +
> + ret = efi_load_image_from_path(lo.file_path, &image);
> +
> + if (ret != EFI_SUCCESS)
> + goto error;
> +
> + printf("Booting: %ls\n", lo.label);
> + efi_dp_split_file_path(lo.file_path, device_path, file_path);
> + }
> +
> +error:
> + free(load_option);
> +
> + return image;
> +}
> +
> +void *efi_bootmgr_load(struct efi_device_path **device_path,
> + struct efi_device_path **file_path)
> +{
> + uint16_t *bootorder;
> + unsigned long size;
> + void *image = NULL;
> + int i, num;
> +
> + __efi_entry_check();
> +
> + bs = systab.boottime;
> + rs = systab.runtime;
> +
> + bootorder = get_var(L"BootOrder", &efi_global_variable_guid, &size);
> + if (!bootorder)
> + goto error;
> +
> + num = size / sizeof(uint16_t);
> + for (i = 0; i < num; i++) {
> + debug("%s: trying to load Boot%04X\n", __func__, bootorder[i]);
> + image = try_load_entry(bootorder[i], device_path, file_path);
> + if (image)
> + break;
> + }
> +
> + free(bootorder);
> +
> +error:
> + __efi_exit_check();
> +
> + return image;
> +}
> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> index 19eaea8baf..5ff2d2d4b0 100644
> --- a/lib/efi_loader/efi_boottime.c
> +++ b/lib/efi_loader/efi_boottime.c
> @@ -749,8 +749,8 @@ void efi_setup_loaded_image(struct efi_loaded_image *info, struct efi_object *ob
> list_add_tail(&obj->link, &efi_obj_list);
> }
>
> -static efi_status_t load_image_from_path(struct efi_device_path *file_path,
> - void **buffer)
> +efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
> + void **buffer)
> {
> struct efi_file_info *info = NULL;
> struct efi_file_handle *f;
> @@ -808,7 +808,7 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy,
> struct efi_device_path *dp, *fp;
> efi_status_t ret;
>
> - ret = load_image_from_path(file_path, &source_buffer);
> + ret = efi_load_image_from_path(file_path, &source_buffer);
> if (ret != EFI_SUCCESS) {
> free(info);
> free(obj);
> diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
> index 469acae082..242e6a504b 100644
> --- a/lib/efi_loader/efi_image_loader.c
> +++ b/lib/efi_loader/efi_image_loader.c
> @@ -15,6 +15,7 @@
>
> DECLARE_GLOBAL_DATA_PTR;
>
> +const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
> const efi_guid_t efi_guid_device_path = DEVICE_PATH_GUID;
> const efi_guid_t efi_guid_loaded_image = LOADED_IMAGE_GUID;
> const efi_guid_t efi_simple_file_system_protocol_guid =
>
More information about the U-Boot
mailing list