[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