[U-Boot] [PATCH v1 14/15] efi_loader: efi variable support

Alexander Graf agraf at suse.de
Sat Aug 12 13:01:52 UTC 2017



On 10.08.17 20:29, Rob Clark wrote:
> Add EFI variable support, mapping to u-boot environment variables.
> Variables are pretty important for setting up boot order, among other
> things.  If the board supports saveenv, then it will be called in
> ExitBootServices() to persist variables set by the efi payload.  (For
> example, fallback.efi configuring BootOrder and BootXXXX load-option
> variables.)
> 
> Variables are *not* currently exposed at runtime, post ExitBootServices.
> On boards without a dedicated device for storage, which the loaded OS
> is not trying to also use, this is rather tricky.  One idea, at least
> for boards that can persist RAM across reboot, is to keep a "journal"
> of modified variables in RAM, and then turn halt into a reboot into
> u-boot, plus store variables, plus halt.  Whatever the solution, it
> likely involves some per-board support.
> 
> Mapping between EFI variables and u-boot variables:
> 
>    efi_$guid_$varname = {attributes}(type)value
> 
> For example:
> 
>    efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported=
>       "{ro,boot,run}(blob)0000000000000000"
>    efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_BootOrder=
>       "(blob)00010000"
> 
> The attributes are a comma separated list of these possible
> attributes:
> 
>    + ro   - read-only
>    + boot - boot-services access
>    + run  - runtime access
> 
> NOTE: with current implementation, no variables are available after
> ExitBootServices, and all are persisted (if possible).
> 
> If not specified, the attributes default to "{boot}".
> 
> The required type is one of:
> 
>    + utf8 - raw utf8 string
>    + blob - arbitrary length hex string
> 
> Signed-off-by: Rob Clark <robdclark at gmail.com>
> ---
>   cmd/bootefi.c                 |   4 +
>   include/efi.h                 |  19 +++
>   include/efi_loader.h          |  10 ++
>   lib/efi_loader/Makefile       |   2 +-
>   lib/efi_loader/efi_boottime.c |   5 +
>   lib/efi_loader/efi_runtime.c  |  17 ++-
>   lib/efi_loader/efi_variable.c | 342 ++++++++++++++++++++++++++++++++++++++++++
>   7 files changed, 394 insertions(+), 5 deletions(-)
>   create mode 100644 lib/efi_loader/efi_variable.c
> 
> diff --git a/cmd/bootefi.c b/cmd/bootefi.c
> index b9e1e5e131..80f52e9e35 100644
> --- a/cmd/bootefi.c
> +++ b/cmd/bootefi.c
> @@ -181,6 +181,10 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt,
>   		goto exit;
>   	}
>   
> +	/* we don't support much: */
> +	setenv("efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported",
> +			"{ro,boot}(blob)0000000000000000");
> +
>   	/* Call our payload! */
>   	debug("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry);
>   
> diff --git a/include/efi.h b/include/efi.h
> index ddd2b96417..dbd482a5db 100644
> --- a/include/efi.h
> +++ b/include/efi.h
> @@ -324,6 +324,25 @@ extern char image_base[];
>   /* Start and end of U-Boot image (for payload) */
>   extern char _binary_u_boot_bin_start[], _binary_u_boot_bin_end[];
>   
> +/*
> + * Variable Attributes
> + */
> +#define EFI_VARIABLE_NON_VOLATILE       0x0000000000000001

Shouldn't we honor this one too? A UEFI application may set runtime 
variables that should not get persisted across boots (think the UEFI 
shell setting some internal state thing, then booting Linux).

> +#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002
> +#define EFI_VARIABLE_RUNTIME_ACCESS     0x0000000000000004
> +#define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x0000000000000008
> +#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x0000000000000010
> +#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 0x0000000000000020
> +#define EFI_VARIABLE_APPEND_WRITE	0x0000000000000040
> +
> +#define EFI_VARIABLE_MASK 	(EFI_VARIABLE_NON_VOLATILE | \
> +				EFI_VARIABLE_BOOTSERVICE_ACCESS | \
> +				EFI_VARIABLE_RUNTIME_ACCESS | \
> +				EFI_VARIABLE_HARDWARE_ERROR_RECORD | \
> +				EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | \
> +				EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | \
> +				EFI_VARIABLE_APPEND_WRITE)
> +
>   /**
>    * efi_get_sys_table() - Get access to the main EFI system table
>    *
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index efb93f31f7..9cb568e615 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -271,6 +271,16 @@ efi_status_t __efi_runtime EFIAPI efi_get_time(
>   			struct efi_time_cap *capabilities);
>   void efi_get_time_init(void);
>   
> +efi_status_t EFIAPI efi_get_variable(s16 *variable_name,
> +		efi_guid_t *vendor, u32 *attributes,
> +		unsigned long *data_size, void *data);
> +efi_status_t EFIAPI efi_get_next_variable(
> +		unsigned long *variable_name_size,
> +		s16 *variable_name, efi_guid_t *vendor);
> +efi_status_t EFIAPI efi_set_variable(s16 *variable_name,
> +		efi_guid_t *vendor, u32 attributes,
> +		unsigned long data_size, void *data);
> +
>   #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 cce92cfeb5..f58cb13337 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
> +obj-y += efi_file.o efi_variable.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_boottime.c b/lib/efi_loader/efi_boottime.c
> index 7e44ba56e2..0bb64f0351 100644
> --- a/lib/efi_loader/efi_boottime.c
> +++ b/lib/efi_loader/efi_boottime.c
> @@ -927,6 +927,11 @@ static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle,
>   {
>   	EFI_ENTRY("%p, %ld", image_handle, map_key);
>   
> +#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_ENV_IS_NOWHERE)
> +	/* save any EFI variables that have been written: */
> +	saveenv();
> +#endif
> +
>   	board_quiesce_devices();
>   
>   	/* Fix up caches for EFI payloads if necessary */
> diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
> index dd52755d1d..7615090ba3 100644
> --- a/lib/efi_loader/efi_runtime.c
> +++ b/lib/efi_loader/efi_runtime.c
> @@ -184,7 +184,16 @@ static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
>   		/* Clean up system table */
>   		.ptr = &systab.boottime,
>   		.patchto = NULL,
> -	},
> +	}, {
> +		.ptr = &efi_runtime_services.get_variable,
> +		.patchto = &efi_device_error,
> +	}, {
> +		.ptr = &efi_runtime_services.get_next_variable,
> +		.patchto = &efi_device_error,
> +	}, {
> +		.ptr = &efi_runtime_services.set_variable,
> +		.patchto = &efi_device_error,
> +	}
>   };
>   
>   static bool efi_runtime_tobedetached(void *p)
> @@ -382,9 +391,9 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
>   	.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_variable = efi_get_variable,
> +	.get_next_variable = efi_get_next_variable,
> +	.set_variable = efi_set_variable,
>   	.get_next_high_mono_count = (void *)&efi_device_error,
>   	.reset_system = &efi_reset_system_boottime,
>   };
> diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
> new file mode 100644
> index 0000000000..745514e4fa
> --- /dev/null
> +++ b/lib/efi_loader/efi_variable.c
> @@ -0,0 +1,342 @@
> +/*
> + *  EFI utils
> + *
> + *  Copyright (c) 2017 Rob Clark
> + *
> + *  SPDX-License-Identifier:     GPL-2.0+
> + */
> +
> +#include <malloc.h>
> +#include <charset.h>
> +#include <efi_loader.h>
> +
> +#define READ_ONLY BIT(31)
> +
> +/*
> + * Mapping between EFI variables and u-boot variables:
> + *
> + *   efi_$guid_$varname = {attributes}(type)value
> + *
> + * For example:
> + *
> + *   efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported=
> + *      "{ro,boot,run}(blob)0000000000000000"
> + *   efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_BootOrder=
> + *      "(blob)00010000"
> + *
> + * The attributes are a comma separated list of these possible
> + * attributes:
> + *
> + *   + ro   - read-only
> + *   + boot - boot-services access
> + *   + run  - runtime access
> + *
> + * NOTE: with current implementation, no variables are available after
> + * ExitBootServices, and all are persisted (if possible).
> + *
> + * If not specified, the attributes default to "{boot}".
> + *
> + * The required type is one of:
> + *
> + *   + utf8 - raw utf8 string
> + *   + blob - arbitrary length hex string
> + *
> + * Maybe a utf16 type would be useful to for a string value to be auto
> + * converted to utf16?
> + */
> +
> +#define MAX_VAR_NAME 31
> +#define MAX_NATIVE_VAR_NAME \
> +	(strlen("efi_xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx_") + \
> +		(MAX_VAR_NAME * MAX_UTF8_PER_UTF16))
> +
> +static int hex(unsigned char ch)
> +{
> +	if (ch >= 'a' && ch <= 'f')
> +		return ch-'a'+10;
> +	if (ch >= '0' && ch <= '9')
> +		return ch-'0';
> +	if (ch >= 'A' && ch <= 'F')
> +		return ch-'A'+10;
> +	return -1;
> +}
> +
> +static const char * hex2mem(u8 *mem, const char *hexstr, int count)
> +{
> +	memset(mem, 0, count/2);
> +
> +	do {
> +		int nibble;
> +
> +		*mem = 0;
> +
> +		if (!count || !*hexstr)
> +			break;
> +
> +		nibble = hex(*hexstr);
> +		if (nibble < 0)
> +			break;
> +
> +		*mem = nibble;
> +		count--;
> +		hexstr++;
> +
> +		if (!count || !*hexstr)
> +			break;
> +
> +		nibble = hex(*hexstr);
> +		if (nibble < 0)
> +			break;
> +
> +		*mem = (*mem << 4) | nibble;
> +		count--;
> +		hexstr++;
> +		mem++;
> +
> +	} while (1);
> +
> +	if (*hexstr)
> +		return hexstr;
> +
> +	return NULL;
> +}
> +
> +static char * mem2hex(char *hexstr, const u8 *mem, int count)
> +{
> +	static const char hexchars[] = "0123456789abcdef";
> +
> +	while (count-- > 0) {
> +		u8 ch = *mem++;
> +		*hexstr++ = hexchars[ch >> 4];
> +		*hexstr++ = hexchars[ch & 0xf];
> +	}
> +
> +	return hexstr;
> +}
> +
> +static efi_status_t efi_to_native(char *native, s16 *variable_name,
> +		efi_guid_t *vendor)
> +{
> +	size_t len;
> +
> +	len = utf16_strlen((u16 *)variable_name);
> +	if (len >= MAX_VAR_NAME)
> +		return EFI_DEVICE_ERROR;
> +
> +	native += sprintf(native, "efi_%pUl_", vendor);
> +	native  = (char *)utf16_to_utf8((u8 *)native, (u16 *)variable_name, len);
> +	*native = '\0';
> +
> +	return EFI_SUCCESS;
> +}
> +
> +static const char * prefix(const char *str, const char *prefix)

Isn't this just strncmp(prefix, str, strlen(prefix));?


Alex


More information about the U-Boot mailing list