[U-Boot] [PATCH v7 1/7] cmd: env: add "-e" option for handling UEFI variables

Heinrich Schuchardt xypron.glpk at gmx.de
Fri Feb 22 02:34:03 UTC 2019


On 2/22/19 1:21 AM, AKASHI Takahiro wrote:
> On Thu, Feb 21, 2019 at 08:42:37PM +0100, Heinrich Schuchardt wrote:
>> On 2/21/19 7:26 AM, AKASHI Takahiro wrote:
>>> "env [print|set] -e" allows for handling uefi variables without
>>> knowing details about mapping to corresponding u-boot variables.
>>>
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi at linaro.org>
>>> ---
>>>  MAINTAINERS       |   1 +
>>>  cmd/Kconfig       |  10 ++
>>>  cmd/Makefile      |   1 +
>>>  cmd/nvedit.c      |  28 +++-
>>>  cmd/nvedit_efi.c  | 395 ++++++++++++++++++++++++++++++++++++++++++++++
>>>  include/command.h |   8 +
>>>  6 files changed, 442 insertions(+), 1 deletion(-)
>>>  create mode 100644 cmd/nvedit_efi.c
>>>
>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>> index f1f8818d6ba8..0cce9db2660e 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -471,6 +471,7 @@ F:	lib/efi*/
>>>  F:	test/py/tests/test_efi*
>>>  F:	test/unicode_ut.c
>>>  F:	cmd/bootefi.c
>>> +F:	cmd/nvedit_efi.c
>>>  F:	tools/file2include.c
>>>  
>>>  FPGA
>>> diff --git a/cmd/Kconfig b/cmd/Kconfig
>>> index 3ea42e425611..ddcdee44538d 100644
>>> --- a/cmd/Kconfig
>>> +++ b/cmd/Kconfig
>>> @@ -420,6 +420,16 @@ config CMD_ENV_FLAGS
>>>  	  be deleted. This command shows the variables that have special
>>>  	  flags.
>>>  
>>> +config CMD_NVEDIT_EFI
>>> +	bool "env [set|print] -e - set/print UEFI variables"
>>> +	depends on EFI_LOADER
>>> +	default y
>>> +	imply HEXDUMP
>>> +	help
>>> +	  UEFI variables are encoded as some form of U-Boot variables.
>>> +	  If enabled, we are allowed to set/print UEFI variables using
>>> +	  "env" command with "-e" option without knowing details.
>>> +
>>>  endmenu
>>>  
>>>  menu "Memory commands"
>>> diff --git a/cmd/Makefile b/cmd/Makefile
>>> index a127a995394f..b9ee51869d48 100644
>>> --- a/cmd/Makefile
>>> +++ b/cmd/Makefile
>>> @@ -98,6 +98,7 @@ obj-$(CONFIG_CMD_MTD) += mtd.o
>>>  obj-$(CONFIG_CMD_MTDPARTS) += mtdparts.o
>>>  obj-$(CONFIG_CMD_NAND) += nand.o
>>>  obj-$(CONFIG_CMD_NET) += net.o
>>> +obj-$(CONFIG_CMD_NVEDIT_EFI) += nvedit_efi.o
>>>  obj-$(CONFIG_CMD_ONENAND) += onenand.o
>>>  obj-$(CONFIG_CMD_OSD) += osd.o
>>>  obj-$(CONFIG_CMD_PART) += part.o
>>> diff --git a/cmd/nvedit.c b/cmd/nvedit.c
>>> index ebaa16b75459..f798e5137d26 100644
>>> --- a/cmd/nvedit.c
>>> +++ b/cmd/nvedit.c
>>> @@ -119,6 +119,11 @@ static int do_env_print(cmd_tbl_t *cmdtp, int flag, int argc,
>>>  	int rcode = 0;
>>>  	int env_flag = H_HIDE_DOT;
>>>  
>>> +#if defined(CONFIG_CMD_NVEDIT_EFI)
>>> +	if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'e')
>>> +		return do_env_print_efi(cmdtp, flag, --argc, ++argv);
>>> +#endif
>>> +
>>>  	if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'a') {
>>>  		argc--;
>>>  		argv++;
>>> @@ -216,6 +221,12 @@ static int _do_env_set(int flag, int argc, char * const argv[], int env_flag)
>>>  	ENTRY e, *ep;
>>>  
>>>  	debug("Initial value for argc=%d\n", argc);
>>> +
>>> +#if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_CMD_NVEDIT_EFI)
>>
>> In the Makefile you already have 'ifndef CONFIG_SPL_BUILD'.
> 
> No.
> Since nvedit.c is always compiled in, we need this protection.

You are right.

One day this nvedit.c should be split into two files. One with SPL
relevant functions and one for the rest. That would save us a lot of
ifdef's.

Regards

Heinrich

> 
>>> +	if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'e')
>>> +		return do_env_set_efi(NULL, flag, --argc, ++argv);
>>> +#endif
>>> +
>>>  	while (argc > 1 && **(argv + 1) == '-') {
>>>  		char *arg = *++argv;
>>>  
>>> @@ -1263,11 +1274,17 @@ static char env_help_text[] =
>>>  	"env import [-d] [-t [-r] | -b | -c] addr [size] [var ...] - import environment\n"
>>>  #endif
>>>  	"env print [-a | name ...] - print environment\n"
>>> +#if defined(CONFIG_CMD_NVEDIT_EFI)
>>> +	"env print -e [name ...] - print UEFI environment\n"
>>> +#endif
>>>  #if defined(CONFIG_CMD_RUN)
>>>  	"env run var [...] - run commands in an environment variable\n"
>>>  #endif
>>>  #if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_ENV_IS_NOWHERE)
>>>  	"env save - save environment\n"
>>> +#endif
>>> +#if defined(CONFIG_CMD_NVEDIT_EFI)
>>> +	"env set -e name [arg ...] - set UEFI variable; unset if 'arg' not specified\n"
>>>  #endif
>>>  	"env set [-f] name [arg ...]\n";
>>>  #endif
>>> @@ -1295,6 +1312,10 @@ U_BOOT_CMD_COMPLETE(
>>>  	printenv, CONFIG_SYS_MAXARGS, 1,	do_env_print,
>>>  	"print environment variables",
>>>  	"[-a]\n    - print [all] values of all environment variables\n"
>>> +#if defined(CONFIG_CMD_NVEDIT_EFI)
>>> +	"printenv -e [name ...]\n"
>>> +	"    - print UEFI variable 'name' or all the variables\n"
>>> +#endif
>>>  	"printenv name ...\n"
>>>  	"    - print value of environment variable 'name'",
>>>  	var_complete
>>> @@ -1322,7 +1343,12 @@ U_BOOT_CMD_COMPLETE(
>>>  U_BOOT_CMD_COMPLETE(
>>>  	setenv, CONFIG_SYS_MAXARGS, 0,	do_env_set,
>>>  	"set environment variables",
>>> -	"[-f] name value ...\n"
>>> +#if defined(CONFIG_CMD_NVEDIT_EFI)
>>> +	"-e name [value ...]\n"
>>> +	"    - set UEFI variable 'name' to 'value' ...'\n"
>>> +	"    - delete UEFI variable 'name' if 'value' not specified\n"
>>> +#endif
>>> +	"setenv [-f] name value ...\n"
>>>  	"    - [forcibly] set environment variable 'name' to 'value ...'\n"
>>>  	"setenv [-f] name\n"
>>>  	"    - [forcibly] delete environment variable 'name'",
>>> diff --git a/cmd/nvedit_efi.c b/cmd/nvedit_efi.c
>>> new file mode 100644
>>> index 000000000000..aaff9f51d672
>>> --- /dev/null
>>> +++ b/cmd/nvedit_efi.c
>>> @@ -0,0 +1,395 @@
>>> +// SPDX-License-Identifier: GPL-2.0+
>>> +/*
>>> + *  Integrate UEFI variables to u-boot env interface
>>> + *
>>> + *  Copyright (c) 2018 AKASHI Takahiro, Linaro Limited
>>> + */
>>> +
>>> +#include <charset.h>
>>> +#include <common.h>
>>> +#include <command.h>
>>> +#include <efi_loader.h>
>>> +#include <exports.h>
>>> +#include <hexdump.h>
>>> +#include <malloc.h>
>>> +#include <linux/kernel.h>
>>> +
>>> +/*
>>> + * From efi_variable.c,
>>> + *
>>> + * Mapping between UEFI variables and u-boot variables:
>>> + *
>>> + *   efi_$guid_$varname = {attributes}(type)value
>>> + */
>>> +
>>> +static const struct {
>>> +	u32 mask;
>>> +	char *text;
>>> +} efi_var_attrs[] = {
>>> +	{EFI_VARIABLE_NON_VOLATILE, "NV"},
>>> +	{EFI_VARIABLE_BOOTSERVICE_ACCESS, "BS"},
>>> +	{EFI_VARIABLE_RUNTIME_ACCESS, "RT"},
>>> +	{EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, "AW"},
>>> +	{EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, "AT"},
>>> +};
>>> +
>>> +/**
>>> + * efi_dump_single_var() - show information about a UEFI variable
>>> + *
>>> + * @name:	Name of the variable
>>> + * @guid:	Vendor GUID
>>> + *
>>> + * Show information encoded in one UEFI variable
>>> + */
>>> +static void efi_dump_single_var(u16 *name, efi_guid_t *guid)
>>> +{
>>> +	u32 attributes;
>>> +	u8 *data;
>>> +	efi_uintn_t size;
>>> +	int count, i;
>>> +	efi_status_t ret;
>>> +
>>> +	data = NULL;
>>> +	size = 0;
>>> +	ret = efi_get_variable(name, guid, &attributes, &size, data);
>>
>> Please, use EFI_CALL() when calling function which themselves call
>> EFI_ENTRY().
> 
> Oops, okay.
> 
> -Takahiro Akashi
> 
>> Best regards
>>
>> Heinrich
>>
>>> +	if (ret == EFI_BUFFER_TOO_SMALL) {
>>> +		data = malloc(size);
>>> +		if (!data)
>>> +			goto out;
>>> +
>>> +		ret = efi_get_variable(name, guid, &attributes, &size, data);
>>> +	}
>>> +	if (ret == EFI_NOT_FOUND) {
>>> +		printf("Error: \"%ls\" not defined\n", name);
>>> +		goto out;
>>> +	}
>>> +	if (ret != EFI_SUCCESS)
>>> +		goto out;
>>> +
>>> +	printf("%ls:", name);
>>> +	for (count = 0, i = 0; i < ARRAY_SIZE(efi_var_attrs); i++)
>>> +		if (attributes & efi_var_attrs[i].mask) {
>>> +			if (count)
>>> +				putc('|');
>>> +			else
>>> +				putc(' ');
>>> +			count++;
>>> +			puts(efi_var_attrs[i].text);
>>> +		}
>>> +	printf(", DataSize = 0x%zx\n", size);
>>> +	print_hex_dump("    ", DUMP_PREFIX_OFFSET, 16, 1, data, size, true);
>>> +
>>> +	return;
>>> +out:
>>> +	free(data);
>>> +}
>>> +
>>> +/**
>>> + * efi_dump_vars() - show information about named UEFI variables
>>> + *
>>> + * @argc:	Number of arguments (variables)
>>> + * @argv:	Argument (variable name) array
>>> + * Return:	CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE
>>> + *
>>> + * Show information encoded in named UEFI variables
>>> + */
>>> +static int efi_dump_vars(int argc,  char * const argv[])
>>> +{
>>> +	u16 *var_name16, *p;
>>> +	efi_uintn_t buf_size, size;
>>> +
>>> +	buf_size = 128;
>>> +	var_name16 = malloc(buf_size);
>>> +	if (!var_name16)
>>> +		return CMD_RET_FAILURE;
>>> +
>>> +	for (; argc > 0; argc--, argv++) {
>>> +		size = (utf8_utf16_strlen(argv[0]) + 1) * sizeof(u16);
>>> +		if (buf_size < size) {
>>> +			buf_size = size;
>>> +			p = realloc(var_name16, buf_size);
>>> +			if (!p) {
>>> +				free(var_name16);
>>> +				return CMD_RET_FAILURE;
>>> +			}
>>> +			var_name16 = p;
>>> +		}
>>> +
>>> +		p = var_name16;
>>> +		utf8_utf16_strcpy(&p, argv[0]);
>>> +
>>> +		efi_dump_single_var(var_name16,
>>> +				    (efi_guid_t *)&efi_global_variable_guid);
>>> +	}
>>> +
>>> +	free(var_name16);
>>> +
>>> +	return CMD_RET_SUCCESS;
>>> +}
>>> +
>>> +/**
>>> + * efi_dump_vars() - show information about all the UEFI variables
>>> + *
>>> + * Return:	CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE
>>> + *
>>> + * Show information encoded in all the UEFI variables
>>> + */
>>> +static int efi_dump_var_all(void)
>>> +{
>>> +	u16 *var_name16, *p;
>>> +	efi_uintn_t buf_size, size;
>>> +	efi_guid_t guid;
>>> +	efi_status_t ret;
>>> +
>>> +	buf_size = 128;
>>> +	var_name16 = malloc(buf_size);
>>> +	if (!var_name16)
>>> +		return CMD_RET_FAILURE;
>>> +
>>> +	var_name16[0] = 0;
>>> +	for (;;) {
>>> +		size = buf_size;
>>> +		ret = efi_get_next_variable_name(&size, var_name16, &guid);
>>> +		if (ret == EFI_NOT_FOUND)
>>> +			break;
>>> +		if (ret == EFI_BUFFER_TOO_SMALL) {
>>> +			buf_size = size;
>>> +			p = realloc(var_name16, buf_size);
>>> +			if (!p) {
>>> +				free(var_name16);
>>> +				return CMD_RET_FAILURE;
>>> +			}
>>> +			var_name16 = p;
>>> +			ret = efi_get_next_variable_name(&size, var_name16,
>>> +							 &guid);
>>> +		}
>>> +		if (ret != EFI_SUCCESS) {
>>> +			free(var_name16);
>>> +			return CMD_RET_FAILURE;
>>> +		}
>>> +
>>> +		efi_dump_single_var(var_name16, &guid);
>>> +	}
>>> +
>>> +	free(var_name16);
>>> +
>>> +	return CMD_RET_SUCCESS;
>>> +}
>>> +
>>> +/**
>>> + * do_env_print_efi() - show information about UEFI variables
>>> + *
>>> + * @cmdtp:	Command table
>>> + * @flag:	Command flag
>>> + * @argc:	Number of arguments
>>> + * @argv:	Argument array
>>> + * Return:	CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE
>>> + *
>>> + * This function is for "env print -e" or "printenv -e" command:
>>> + *   => env print -e [var [...]]
>>> + * If one or more variable names are specified, show information
>>> + * named UEFI variables, otherwise show all the UEFI variables.
>>> + */
>>> +int do_env_print_efi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>>> +{
>>> +	efi_status_t ret;
>>> +
>>> +	/* Initialize EFI drivers */
>>> +	ret = efi_init_obj_list();
>>> +	if (ret != EFI_SUCCESS) {
>>> +		printf("Error: Cannot initialize UEFI sub-system, r = %lu\n",
>>> +		       ret & ~EFI_ERROR_MASK);
>>> +		return CMD_RET_FAILURE;
>>> +	}
>>> +
>>> +	if (argc > 1)
>>> +		/* show specified UEFI variables */
>>> +		return efi_dump_vars(--argc, ++argv);
>>> +
>>> +	/* enumerate and show all UEFI variables */
>>> +	return efi_dump_var_all();
>>> +}
>>> +
>>> +/**
>>> + * append_value() - encode UEFI variable's value
>>> + * @bufp:	Buffer of encoded UEFI variable's value
>>> + * @sizep:	Size of buffer
>>> + * @data:	data to be encoded into the value
>>> + * Return:	0 on success, -1 otherwise
>>> + *
>>> + * Interpret a given data string and append it to buffer.
>>> + * Buffer will be realloc'ed if necessary.
>>> + *
>>> + * Currently supported formats are:
>>> + *   =0x0123...:		Hexadecimal number
>>> + *   =H0123...:			Hexadecimal-byte array
>>> + *   ="...", =S"..." or <string>:
>>> + *				String
>>> + */
>>> +static int append_value(char **bufp, size_t *sizep, char *data)
>>> +{
>>> +	char *tmp_buf = NULL, *new_buf = NULL, *value;
>>> +	unsigned long len = 0;
>>> +
>>> +	if (!strncmp(data, "=0x", 2)) { /* hexadecimal number */
>>> +		union {
>>> +			u8 u8;
>>> +			u16 u16;
>>> +			u32 u32;
>>> +			u64 u64;
>>> +		} tmp_data;
>>> +		unsigned long hex_value;
>>> +		void *hex_ptr;
>>> +
>>> +		data += 3;
>>> +		len = strlen(data);
>>> +		if ((len & 0x1)) /* not multiple of two */
>>> +			return -1;
>>> +
>>> +		len /= 2;
>>> +		if (len > 8)
>>> +			return -1;
>>> +		else if (len > 4)
>>> +			len = 8;
>>> +		else if (len > 2)
>>> +			len = 4;
>>> +
>>> +		/* convert hex hexadecimal number */
>>> +		if (strict_strtoul(data, 16, &hex_value) < 0)
>>> +			return -1;
>>> +
>>> +		tmp_buf = malloc(len);
>>> +		if (!tmp_buf)
>>> +			return -1;
>>> +
>>> +		if (len == 1) {
>>> +			tmp_data.u8 = hex_value;
>>> +			hex_ptr = &tmp_data.u8;
>>> +		} else if (len == 2) {
>>> +			tmp_data.u16 = hex_value;
>>> +			hex_ptr = &tmp_data.u16;
>>> +		} else if (len == 4) {
>>> +			tmp_data.u32 = hex_value;
>>> +			hex_ptr = &tmp_data.u32;
>>> +		} else {
>>> +			tmp_data.u64 = hex_value;
>>> +			hex_ptr = &tmp_data.u64;
>>> +		}
>>> +		memcpy(tmp_buf, hex_ptr, len);
>>> +		value = tmp_buf;
>>> +
>>> +	} else if (!strncmp(data, "=H", 2)) { /* hexadecimal-byte array */
>>> +		data += 2;
>>> +		len = strlen(data);
>>> +		if (len & 0x1) /* not multiple of two */
>>> +			return -1;
>>> +
>>> +		len /= 2;
>>> +		tmp_buf = malloc(len);
>>> +		if (!tmp_buf)
>>> +			return -1;
>>> +
>>> +		if (hex2bin((u8 *)tmp_buf, data, len) < 0)
>>> +			return -1;
>>> +
>>> +		value = tmp_buf;
>>> +	} else { /* string */
>>> +		if (!strncmp(data, "=\"", 2) || !strncmp(data, "=S\"", 3)) {
>>> +			if (data[1] == '"')
>>> +				data += 2;
>>> +			else
>>> +				data += 3;
>>> +			value = data;
>>> +			len = strlen(data) - 1;
>>> +			if (data[len] != '"')
>>> +				return -1;
>>> +		} else {
>>> +			value = data;
>>> +			len = strlen(data);
>>> +		}
>>> +	}
>>> +
>>> +	new_buf = realloc(*bufp, *sizep + len);
>>> +	if (!new_buf)
>>> +		goto out;
>>> +
>>> +	memcpy(new_buf + *sizep, value, len);
>>> +	*bufp = new_buf;
>>> +	*sizep += len;
>>> +
>>> +out:
>>> +	free(tmp_buf);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/**
>>> + * do_env_print_efi() - set UEFI variable
>>> + *
>>> + * @cmdtp:	Command table
>>> + * @flag:	Command flag
>>> + * @argc:	Number of arguments
>>> + * @argv:	Argument array
>>> + * Return:	CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE
>>> + *
>>> + * This function is for "env set -e" or "setenv -e" command:
>>> + *   => env set -e var [value ...]]
>>> + * Encode values specified and set given UEFI variable.
>>> + * If no value is specified, delete the variable.
>>> + */
>>> +int do_env_set_efi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>>> +{
>>> +	char *var_name, *value = NULL;
>>> +	efi_uintn_t size = 0;
>>> +	u16 *var_name16 = NULL, *p;
>>> +	size_t len;
>>> +	efi_guid_t guid;
>>> +	efi_status_t ret;
>>> +
>>> +	if (argc == 1)
>>> +		return CMD_RET_USAGE;
>>> +
>>> +	/* Initialize EFI drivers */
>>> +	ret = efi_init_obj_list();
>>> +	if (ret != EFI_SUCCESS) {
>>> +		printf("Error: Cannot initialize UEFI sub-system, r = %lu\n",
>>> +		       ret & ~EFI_ERROR_MASK);
>>> +		return CMD_RET_FAILURE;
>>> +	}
>>> +
>>> +	var_name = argv[1];
>>> +	if (argc == 2) {
>>> +		/* delete */
>>> +		value = NULL;
>>> +		size = 0;
>>> +	} else { /* set */
>>> +		argc -= 2;
>>> +		argv += 2;
>>> +
>>> +		for ( ; argc > 0; argc--, argv++)
>>> +			if (append_value(&value, &size, argv[0]) < 0) {
>>> +				ret = CMD_RET_FAILURE;
>>> +				goto out;
>>> +			}
>>> +	}
>>> +
>>> +	len = utf8_utf16_strnlen(var_name, strlen(var_name));
>>> +	var_name16 = malloc((len + 1) * 2);
>>> +	if (!var_name16) {
>>> +		ret = CMD_RET_FAILURE;
>>> +		goto out;
>>> +	}
>>> +	p = var_name16;
>>> +	utf8_utf16_strncpy(&p, var_name, len + 1);
>>> +
>>> +	guid = efi_global_variable_guid;
>>> +	ret = efi_set_variable(var_name16, &guid,
>>> +			       EFI_VARIABLE_BOOTSERVICE_ACCESS |
>>> +			       EFI_VARIABLE_RUNTIME_ACCESS, size, value);
>>> +	ret = (ret == EFI_SUCCESS ? CMD_RET_SUCCESS : CMD_RET_FAILURE);
>>> +out:
>>> +	free(value);
>>> +	free(var_name16);
>>> +
>>> +	return ret;
>>> +}
>>> diff --git a/include/command.h b/include/command.h
>>> index 461b17447c0d..2e24e8ad3eef 100644
>>> --- a/include/command.h
>>> +++ b/include/command.h
>>> @@ -139,6 +139,14 @@ extern int do_poweroff(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]
>>>  
>>>  extern unsigned long do_go_exec(ulong (*entry)(int, char * const []), int argc,
>>>  				char * const argv[]);
>>> +
>>> +#if defined(CONFIG_CMD_NVEDIT_EFI)
>>> +extern int do_env_print_efi(cmd_tbl_t *cmdtp, int flag, int argc,
>>> +			    char * const argv[]);
>>> +extern int do_env_set_efi(cmd_tbl_t *cmdtp, int flag, int argc,
>>> +			  char * const argv[]);
>>> +#endif
>>> +
>>>  /*
>>>   * Error codes that commands return to cmd_process(). We use the standard 0
>>>   * and 1 for success and failure, but add one more case - failure with a
>>>
>>
> 



More information about the U-Boot mailing list