u-boot/cmd/nvedit_efi.c
Heinrich Schuchardt c70f44817d efi_loader: simplify 'printenv -e'
Currently default output of 'printenv -e' is restricted to variables with
GUID EFI_GLOBAL_VARIABLE. This excludes db and dbx. As the number of
variables is small there is no need for this restriction.

If no GUID is provided, print all matching variables irrespective of GUID.

Always show the numeric value of the GUID.

If the GUID provided to 'setenv -e' is invalid, return CMD_RET_USAGE.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
2020-07-16 12:37:03 +02:00

575 lines
13 KiB
C

// 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 <efi_variable.h>
#include <env.h>
#include <exports.h>
#include <hexdump.h>
#include <malloc.h>
#include <mapmem.h>
#include <rtc.h>
#include <uuid.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_VARIABLE_READ_ONLY, "RO"},
};
static const struct {
efi_guid_t guid;
char *text;
} efi_guid_text[] = {
/* signature database */
{EFI_GLOBAL_VARIABLE_GUID, "EFI_GLOBAL_VARIABLE_GUID"},
{EFI_IMAGE_SECURITY_DATABASE_GUID, "EFI_IMAGE_SECURITY_DATABASE_GUID"},
/* certificate type */
{EFI_CERT_SHA256_GUID, "EFI_CERT_SHA256_GUID"},
{EFI_CERT_X509_GUID, "EFI_CERT_X509_GUID"},
{EFI_CERT_TYPE_PKCS7_GUID, "EFI_CERT_TYPE_PKCS7_GUID"},
};
static const char unknown_guid[] = "";
/**
* efi_guid_to_str() - convert guid to readable name
*
* @guid: GUID
* Return: string for GUID
*
* convert guid to readable name
*/
static const char *efi_guid_to_str(const efi_guid_t *guid)
{
int i;
for (i = 0; i < ARRAY_SIZE(efi_guid_text); i++)
if (!guidcmp(guid, &efi_guid_text[i].guid))
return efi_guid_text[i].text;
return unknown_guid;
}
/**
* efi_dump_single_var() - show information about a UEFI variable
*
* @name: Name of the variable
* @guid: Vendor GUID
* @verbose: if true, dump data
*
* Show information encoded in one UEFI variable
*/
static void efi_dump_single_var(u16 *name, const efi_guid_t *guid, bool verbose)
{
u32 attributes;
u8 *data;
u64 time;
struct rtc_time tm;
efi_uintn_t size;
int count, i;
efi_status_t ret;
data = NULL;
size = 0;
ret = efi_get_variable_int(name, guid, &attributes, &size, data, &time);
if (ret == EFI_BUFFER_TOO_SMALL) {
data = malloc(size);
if (!data)
goto out;
ret = efi_get_variable_int(name, guid, &attributes, &size,
data, &time);
}
if (ret == EFI_NOT_FOUND) {
printf("Error: \"%ls\" not defined\n", name);
goto out;
}
if (ret != EFI_SUCCESS)
goto out;
rtc_to_tm(time, &tm);
printf("%ls:\n %pUl %s\n", name, guid, efi_guid_to_str(guid));
if (attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)
printf(" %04d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year,
tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
printf(" ");
for (count = 0, i = 0; i < ARRAY_SIZE(efi_var_attrs); i++)
if (attributes & efi_var_attrs[i].mask) {
if (count)
putc('|');
count++;
puts(efi_var_attrs[i].text);
}
printf(", DataSize = 0x%zx\n", size);
if (verbose)
print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1,
data, size, true);
out:
free(data);
}
static bool match_name(int argc, char *const argv[], u16 *var_name16)
{
char *buf, *p;
size_t buflen;
int i;
bool result = false;
buflen = utf16_utf8_strlen(var_name16) + 1;
buf = calloc(1, buflen);
if (!buf)
return result;
p = buf;
utf16_utf8_strcpy(&p, var_name16);
for (i = 0; i < argc; argc--, argv++) {
if (!strcmp(buf, argv[i])) {
result = true;
goto out;
}
}
out:
free(buf);
return result;
}
/**
* efi_dump_var_all() - show information about all the UEFI variables
*
* @argc: Number of arguments (variables)
* @argv: Argument (variable name) array
* @verbose: if true, dump data
* 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(int argc, char *const argv[],
const efi_guid_t *guid_p, bool verbose)
{
u16 *var_name16, *p;
efi_uintn_t buf_size, size;
efi_guid_t guid;
efi_status_t ret;
bool match = false;
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_CALL(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_CALL(efi_get_next_variable_name(&size,
var_name16,
&guid));
}
if (ret != EFI_SUCCESS) {
free(var_name16);
return CMD_RET_FAILURE;
}
if (guid_p && guidcmp(guid_p, &guid))
continue;
if (!argc || match_name(argc, argv, var_name16)) {
match = true;
efi_dump_single_var(var_name16, &guid, verbose);
}
}
free(var_name16);
if (!match && argc == 1)
printf("Error: \"%s\" not defined\n", argv[0]);
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 [-n] [-guid <guid> | -all] [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(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
const efi_guid_t *guid_p = NULL;
bool verbose = true;
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;
}
for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) {
if (!strcmp(argv[0], "-guid")) {
efi_guid_t guid;
if (argc == 1)
return CMD_RET_USAGE;
argc--;
argv++;
if (uuid_str_to_bin(argv[0], guid.b,
UUID_STR_FORMAT_GUID))
return CMD_RET_USAGE;
guid_p = (const efi_guid_t *)guid.b;
} else if (!strcmp(argv[0], "-n")) {
verbose = false;
} else {
return CMD_RET_USAGE;
}
}
/* enumerate and show all UEFI variables */
return efi_dump_var_all(argc, argv, guid_p, verbose);
}
/**
* 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) {
printf("Error: illegal hexadecimal string\n");
free(tmp_buf);
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_set_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 [-guid guid][-nv][-bs][-rt][-at][-a][-v]
* [-i address,size] var, or
* var [value ...]
* Encode values specified and set given UEFI variable.
* If no value is specified, delete the variable.
*/
int do_env_set_efi(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
char *var_name, *value, *ep;
ulong addr;
efi_uintn_t size;
efi_guid_t guid;
u32 attributes;
bool default_guid, verbose, value_on_memory;
u16 *var_name16 = NULL, *p;
size_t len;
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;
}
/*
* attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS |
* EFI_VARIABLE_RUNTIME_ACCESS;
*/
value = NULL;
size = 0;
attributes = 0;
guid = efi_global_variable_guid;
default_guid = true;
verbose = false;
value_on_memory = false;
for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) {
if (!strcmp(argv[0], "-guid")) {
if (argc == 1)
return CMD_RET_USAGE;
argc--;
argv++;
if (uuid_str_to_bin(argv[0], guid.b,
UUID_STR_FORMAT_GUID)) {
return CMD_RET_USAGE;
}
default_guid = false;
} else if (!strcmp(argv[0], "-bs")) {
attributes |= EFI_VARIABLE_BOOTSERVICE_ACCESS;
} else if (!strcmp(argv[0], "-rt")) {
attributes |= EFI_VARIABLE_RUNTIME_ACCESS;
} else if (!strcmp(argv[0], "-nv")) {
attributes |= EFI_VARIABLE_NON_VOLATILE;
} else if (!strcmp(argv[0], "-at")) {
attributes |=
EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
} else if (!strcmp(argv[0], "-a")) {
attributes |= EFI_VARIABLE_APPEND_WRITE;
} else if (!strcmp(argv[0], "-i")) {
/* data comes from memory */
if (argc == 1)
return CMD_RET_USAGE;
argc--;
argv++;
addr = simple_strtoul(argv[0], &ep, 16);
if (*ep != ',')
return CMD_RET_USAGE;
/* 0 should be allowed for delete */
size = simple_strtoul(++ep, NULL, 16);
value_on_memory = true;
} else if (!strcmp(argv[0], "-v")) {
verbose = true;
} else {
return CMD_RET_USAGE;
}
}
if (!argc)
return CMD_RET_USAGE;
var_name = argv[0];
if (default_guid) {
if (!strcmp(var_name, "db") || !strcmp(var_name, "dbx") ||
!strcmp(var_name, "dbt"))
guid = efi_guid_image_security_database;
else
guid = efi_global_variable_guid;
}
if (verbose) {
printf("GUID: %pUl %s\n", &guid,
efi_guid_to_str((const efi_guid_t *)&guid));
printf("Attributes: 0x%x\n", attributes);
}
/* for value */
if (value_on_memory)
value = map_sysmem(addr, 0);
else if (argc > 1)
for (argc--, argv++; argc > 0; argc--, argv++)
if (append_value(&value, &size, argv[0]) < 0) {
printf("## Failed to process an argument, %s\n",
argv[0]);
ret = CMD_RET_FAILURE;
goto out;
}
if (size && verbose) {
printf("Value:\n");
print_hex_dump(" ", DUMP_PREFIX_OFFSET,
16, 1, value, size, true);
}
len = utf8_utf16_strnlen(var_name, strlen(var_name));
var_name16 = malloc((len + 1) * 2);
if (!var_name16) {
printf("## Out of memory\n");
ret = CMD_RET_FAILURE;
goto out;
}
p = var_name16;
utf8_utf16_strncpy(&p, var_name, len + 1);
ret = efi_set_variable_int(var_name16, &guid, attributes, size, value,
true);
unmap_sysmem(value);
if (ret == EFI_SUCCESS) {
ret = CMD_RET_SUCCESS;
} else {
const char *msg;
switch (ret) {
case EFI_NOT_FOUND:
msg = " (not found)";
break;
case EFI_WRITE_PROTECTED:
msg = " (read only)";
break;
case EFI_INVALID_PARAMETER:
msg = " (invalid parameter)";
break;
case EFI_SECURITY_VIOLATION:
msg = " (validation failed)";
break;
case EFI_OUT_OF_RESOURCES:
msg = " (out of memory)";
break;
default:
msg = "";
break;
}
printf("## Failed to set EFI variable%s\n", msg);
ret = CMD_RET_FAILURE;
}
out:
if (value_on_memory)
unmap_sysmem(value);
else
free(value);
free(var_name16);
return ret;
}