aboutsummaryrefslogtreecommitdiff
path: root/lib/efi_loader/efi_variable.c
diff options
context:
space:
mode:
authorthead_admin <occ_thead@service.alibaba.com>2022-09-13 11:04:33 +0800
committerthead_admin <occ_thead@service.alibaba.com>2022-09-13 11:04:33 +0800
commit43db9e00d5837c100c0b2fbbee64a08ab807d1e0 (patch)
treeb40c0eed02935b6682e8c5c975e3016b6b2f55fe /lib/efi_loader/efi_variable.c
Linux_SDK_V0.9.5Linux_SDK_V0.9.5
Diffstat (limited to 'lib/efi_loader/efi_variable.c')
-rw-r--r--lib/efi_loader/efi_variable.c643
1 files changed, 643 insertions, 0 deletions
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
new file mode 100644
index 00000000..c316bdfe
--- /dev/null
+++ b/lib/efi_loader/efi_variable.c
@@ -0,0 +1,643 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI utils
+ *
+ * Copyright (c) 2017 Rob Clark
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <env_internal.h>
+#include <hexdump.h>
+#include <malloc.h>
+#include <search.h>
+#include <u-boot/crc.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 PREFIX_LEN (strlen("efi_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_"))
+
+/**
+ * efi_to_native() - convert the UEFI variable name and vendor GUID to U-Boot
+ * variable name
+ *
+ * The U-Boot variable name is a concatenation of prefix 'efi', the hexstring
+ * encoded vendor GUID, and the UTF-8 encoded UEFI variable name separated by
+ * underscores, e.g. 'efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_BootOrder'.
+ *
+ * @native: pointer to pointer to U-Boot variable name
+ * @variable_name: UEFI variable name
+ * @vendor: vendor GUID
+ * Return: status code
+ */
+static efi_status_t efi_to_native(char **native, const u16 *variable_name,
+ const efi_guid_t *vendor)
+{
+ size_t len;
+ char *pos;
+
+ len = PREFIX_LEN + utf16_utf8_strlen(variable_name) + 1;
+ *native = malloc(len);
+ if (!*native)
+ return EFI_OUT_OF_RESOURCES;
+
+ pos = *native;
+ pos += sprintf(pos, "efi_%pUl_", vendor);
+ utf16_utf8_strcpy(&pos, variable_name);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * prefix() - skip over prefix
+ *
+ * Skip over a prefix string.
+ *
+ * @str: string with prefix
+ * @prefix: prefix string
+ * Return: string without prefix, or NULL if prefix not found
+ */
+static const char *prefix(const char *str, const char *prefix)
+{
+ size_t n = strlen(prefix);
+ if (!strncmp(prefix, str, n))
+ return str + n;
+ return NULL;
+}
+
+/**
+ * parse_attr() - decode attributes part of variable value
+ *
+ * Convert the string encoded attributes of a UEFI variable to a bit mask.
+ * TODO: Several attributes are not supported.
+ *
+ * @str: value of U-Boot variable
+ * @attrp: pointer to UEFI attributes
+ * Return: pointer to remainder of U-Boot variable value
+ */
+static const char *parse_attr(const char *str, u32 *attrp)
+{
+ u32 attr = 0;
+ char sep = '{';
+
+ if (*str != '{') {
+ *attrp = EFI_VARIABLE_BOOTSERVICE_ACCESS;
+ return str;
+ }
+
+ while (*str == sep) {
+ const char *s;
+
+ str++;
+
+ if ((s = prefix(str, "ro"))) {
+ attr |= READ_ONLY;
+ } else if ((s = prefix(str, "nv"))) {
+ attr |= EFI_VARIABLE_NON_VOLATILE;
+ } else if ((s = prefix(str, "boot"))) {
+ attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS;
+ } else if ((s = prefix(str, "run"))) {
+ attr |= EFI_VARIABLE_RUNTIME_ACCESS;
+ } else {
+ printf("invalid attribute: %s\n", str);
+ break;
+ }
+
+ str = s;
+ sep = ',';
+ }
+
+ str++;
+
+ *attrp = attr;
+
+ return str;
+}
+
+/**
+ * efi_get_variable() - retrieve value of a UEFI variable
+ *
+ * This function implements the GetVariable runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @variable_name: name of the variable
+ * @vendor: vendor GUID
+ * @attributes: attributes of the variable
+ * @data_size: size of the buffer to which the variable value is copied
+ * @data: buffer to which the variable value is copied
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
+ const efi_guid_t *vendor, u32 *attributes,
+ efi_uintn_t *data_size, void *data)
+{
+ char *native_name;
+ efi_status_t ret;
+ unsigned long in_size;
+ const char *val, *s;
+ u32 attr;
+
+ EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes,
+ data_size, data);
+
+ if (!variable_name || !vendor || !data_size)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ ret = efi_to_native(&native_name, variable_name, vendor);
+ if (ret)
+ return EFI_EXIT(ret);
+
+ EFI_PRINT("get '%s'\n", native_name);
+
+ val = env_get(native_name);
+ free(native_name);
+ if (!val)
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ val = parse_attr(val, &attr);
+
+ in_size = *data_size;
+
+ if ((s = prefix(val, "(blob)"))) {
+ size_t len = strlen(s);
+
+ /* number of hexadecimal digits must be even */
+ if (len & 1)
+ return EFI_EXIT(EFI_DEVICE_ERROR);
+
+ /* two characters per byte: */
+ len /= 2;
+ *data_size = len;
+
+ if (in_size < len) {
+ ret = EFI_BUFFER_TOO_SMALL;
+ goto out;
+ }
+
+ if (!data)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ if (hex2bin(data, s, len))
+ return EFI_EXIT(EFI_DEVICE_ERROR);
+
+ EFI_PRINT("got value: \"%s\"\n", s);
+ } else if ((s = prefix(val, "(utf8)"))) {
+ unsigned len = strlen(s) + 1;
+
+ *data_size = len;
+
+ if (in_size < len) {
+ ret = EFI_BUFFER_TOO_SMALL;
+ goto out;
+ }
+
+ if (!data)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ memcpy(data, s, len);
+ ((char *)data)[len] = '\0';
+
+ EFI_PRINT("got value: \"%s\"\n", (char *)data);
+ } else {
+ EFI_PRINT("invalid value: '%s'\n", val);
+ return EFI_EXIT(EFI_DEVICE_ERROR);
+ }
+
+out:
+ if (attributes)
+ *attributes = attr & EFI_VARIABLE_MASK;
+
+ return EFI_EXIT(ret);
+}
+
+static char *efi_variables_list;
+static char *efi_cur_variable;
+
+/**
+ * parse_uboot_variable() - parse a u-boot variable and get uefi-related
+ * information
+ * @variable: whole data of u-boot variable (ie. name=value)
+ * @variable_name_size: size of variable_name buffer in byte
+ * @variable_name: name of uefi variable in u16, null-terminated
+ * @vendor: vendor's guid
+ * @attributes: attributes
+ *
+ * A uefi variable is encoded into a u-boot variable as described above.
+ * This function parses such a u-boot variable and retrieve uefi-related
+ * information into respective parameters. In return, variable_name_size
+ * is the size of variable name including NULL.
+ *
+ * Return: EFI_SUCCESS if parsing is OK, EFI_NOT_FOUND when
+ * the entire variable list has been returned,
+ * otherwise non-zero status code
+ */
+static efi_status_t parse_uboot_variable(char *variable,
+ efi_uintn_t *variable_name_size,
+ u16 *variable_name,
+ const efi_guid_t *vendor,
+ u32 *attributes)
+{
+ char *guid, *name, *end, c;
+ unsigned long name_len;
+ u16 *p;
+
+ guid = strchr(variable, '_');
+ if (!guid)
+ return EFI_INVALID_PARAMETER;
+ guid++;
+ name = strchr(guid, '_');
+ if (!name)
+ return EFI_INVALID_PARAMETER;
+ name++;
+ end = strchr(name, '=');
+ if (!end)
+ return EFI_INVALID_PARAMETER;
+
+ name_len = end - name;
+ if (*variable_name_size < (name_len + 1)) {
+ *variable_name_size = name_len + 1;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ end++; /* point to value */
+
+ /* variable name */
+ p = variable_name;
+ utf8_utf16_strncpy(&p, name, name_len);
+ variable_name[name_len] = 0;
+ *variable_name_size = name_len + 1;
+
+ /* guid */
+ c = *(name - 1);
+ *(name - 1) = '\0'; /* guid need be null-terminated here */
+ uuid_str_to_bin(guid, (unsigned char *)vendor, UUID_STR_FORMAT_GUID);
+ *(name - 1) = c;
+
+ /* attributes */
+ parse_attr(end, attributes);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_get_next_variable_name() - enumerate the current variable names
+ *
+ * @variable_name_size: size of variable_name buffer in byte
+ * @variable_name: name of uefi variable's name in u16
+ * @vendor: vendor's guid
+ *
+ * This function implements the GetNextVariableName service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size,
+ u16 *variable_name,
+ const efi_guid_t *vendor)
+{
+ char *native_name, *variable;
+ ssize_t name_len, list_len;
+ char regex[256];
+ char * const regexlist[] = {regex};
+ u32 attributes;
+ int i;
+ efi_status_t ret;
+
+ EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, vendor);
+
+ if (!variable_name_size || !variable_name || !vendor)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ if (variable_name[0]) {
+ /* check null-terminated string */
+ for (i = 0; i < *variable_name_size; i++)
+ if (!variable_name[i])
+ break;
+ if (i >= *variable_name_size)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ /* search for the last-returned variable */
+ ret = efi_to_native(&native_name, variable_name, vendor);
+ if (ret)
+ return EFI_EXIT(ret);
+
+ name_len = strlen(native_name);
+ for (variable = efi_variables_list; variable && *variable;) {
+ if (!strncmp(variable, native_name, name_len) &&
+ variable[name_len] == '=')
+ break;
+
+ variable = strchr(variable, '\n');
+ if (variable)
+ variable++;
+ }
+
+ free(native_name);
+ if (!(variable && *variable))
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ /* next variable */
+ variable = strchr(variable, '\n');
+ if (variable)
+ variable++;
+ if (!(variable && *variable))
+ return EFI_EXIT(EFI_NOT_FOUND);
+ } else {
+ /*
+ *new search: free a list used in the previous search
+ */
+ free(efi_variables_list);
+ efi_variables_list = NULL;
+ efi_cur_variable = NULL;
+
+ snprintf(regex, 256, "efi_.*-.*-.*-.*-.*_.*");
+ list_len = hexport_r(&env_htab, '\n',
+ H_MATCH_REGEX | H_MATCH_KEY,
+ &efi_variables_list, 0, 1, regexlist);
+ /* 1 indicates that no match was found */
+ if (list_len <= 1)
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ variable = efi_variables_list;
+ }
+
+ ret = parse_uboot_variable(variable, variable_name_size, variable_name,
+ vendor, &attributes);
+
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_set_variable() - set value of a UEFI variable
+ *
+ * This function implements the SetVariable runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @variable_name: name of the variable
+ * @vendor: vendor GUID
+ * @attributes: attributes of the variable
+ * @data_size: size of the buffer with the variable value
+ * @data: buffer with the variable value
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
+ const efi_guid_t *vendor, u32 attributes,
+ efi_uintn_t data_size, const void *data)
+{
+ char *native_name = NULL, *val = NULL, *s;
+ const char *old_val;
+ size_t old_size;
+ efi_status_t ret = EFI_SUCCESS;
+ u32 attr;
+
+ EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes,
+ data_size, data);
+
+ if (!variable_name || !*variable_name || !vendor ||
+ ((attributes & EFI_VARIABLE_RUNTIME_ACCESS) &&
+ !(attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS))) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ ret = efi_to_native(&native_name, variable_name, vendor);
+ if (ret)
+ goto out;
+
+ old_val = env_get(native_name);
+ if (old_val) {
+ old_val = parse_attr(old_val, &attr);
+
+ /* check read-only first */
+ if (attr & READ_ONLY) {
+ ret = EFI_WRITE_PROTECTED;
+ goto out;
+ }
+
+ if ((data_size == 0 &&
+ !(attributes & EFI_VARIABLE_APPEND_WRITE)) ||
+ !attributes) {
+ /* delete the variable: */
+ env_set(native_name, NULL);
+ ret = EFI_SUCCESS;
+ goto out;
+ }
+
+ /* attributes won't be changed */
+ if (attr != (attributes & ~EFI_VARIABLE_APPEND_WRITE)) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (attributes & EFI_VARIABLE_APPEND_WRITE) {
+ if (!prefix(old_val, "(blob)")) {
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+ old_size = strlen(old_val);
+ } else {
+ old_size = 0;
+ }
+ } else {
+ if (data_size == 0 || !attributes ||
+ (attributes & EFI_VARIABLE_APPEND_WRITE)) {
+ /*
+ * Trying to delete or to update a non-existent
+ * variable.
+ */
+ ret = EFI_NOT_FOUND;
+ goto out;
+ }
+
+ old_size = 0;
+ }
+
+ val = malloc(old_size + 2 * data_size
+ + strlen("{ro,run,boot,nv}(blob)") + 1);
+ if (!val) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ s = val;
+
+ /* store attributes */
+ attributes &= (EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS);
+ s += sprintf(s, "{");
+ while (attributes) {
+ u32 attr = 1 << (ffs(attributes) - 1);
+
+ if (attr == EFI_VARIABLE_NON_VOLATILE)
+ s += sprintf(s, "nv");
+ else if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS)
+ s += sprintf(s, "boot");
+ else if (attr == EFI_VARIABLE_RUNTIME_ACCESS)
+ s += sprintf(s, "run");
+
+ attributes &= ~attr;
+ if (attributes)
+ s += sprintf(s, ",");
+ }
+ s += sprintf(s, "}");
+
+ if (old_size)
+ /* APPEND_WRITE */
+ s += sprintf(s, old_val);
+ else
+ s += sprintf(s, "(blob)");
+
+ /* store payload: */
+ s = bin2hex(s, data, data_size);
+ *s = '\0';
+
+ EFI_PRINT("setting: %s=%s\n", native_name, val);
+
+ if (env_set(native_name, val))
+ ret = EFI_DEVICE_ERROR;
+
+out:
+ free(native_name);
+ free(val);
+
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_query_variable_info() - get information about EFI variables
+ *
+ * This function implements the QueryVariableInfo() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @attributes: bitmask to select variables to be
+ * queried
+ * @maximum_variable_storage_size: maximum size of storage area for the
+ * selected variable types
+ * @remaining_variable_storage_size: remaining size of storage are for the
+ * selected variable types
+ * @maximum_variable_size: maximum size of a variable of the
+ * selected type
+ * Returns: status code
+ */
+efi_status_t __efi_runtime EFIAPI efi_query_variable_info(
+ u32 attributes,
+ u64 *maximum_variable_storage_size,
+ u64 *remaining_variable_storage_size,
+ u64 *maximum_variable_size)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_get_variable_runtime() - runtime implementation of GetVariable()
+ *
+ * @variable_name: name of the variable
+ * @vendor: vendor GUID
+ * @attributes: attributes of the variable
+ * @data_size: size of the buffer to which the variable value is copied
+ * @data: buffer to which the variable value is copied
+ * Return: status code
+ */
+static efi_status_t __efi_runtime EFIAPI
+efi_get_variable_runtime(u16 *variable_name, const efi_guid_t *vendor,
+ u32 *attributes, efi_uintn_t *data_size, void *data)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_get_next_variable_name_runtime() - runtime implementation of
+ * GetNextVariable()
+ *
+ * @variable_name_size: size of variable_name buffer in byte
+ * @variable_name: name of uefi variable's name in u16
+ * @vendor: vendor's guid
+ * Return: status code
+ */
+static efi_status_t __efi_runtime EFIAPI
+efi_get_next_variable_name_runtime(efi_uintn_t *variable_name_size,
+ u16 *variable_name, const efi_guid_t *vendor)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_set_variable_runtime() - runtime implementation of SetVariable()
+ *
+ * @variable_name: name of the variable
+ * @vendor: vendor GUID
+ * @attributes: attributes of the variable
+ * @data_size: size of the buffer with the variable value
+ * @data: buffer with the variable value
+ * Return: status code
+ */
+static efi_status_t __efi_runtime EFIAPI
+efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *vendor,
+ u32 attributes, efi_uintn_t data_size,
+ const void *data)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_variables_boot_exit_notify() - notify ExitBootServices() is called
+ */
+void efi_variables_boot_exit_notify(void)
+{
+ efi_runtime_services.get_variable = efi_get_variable_runtime;
+ efi_runtime_services.get_next_variable_name =
+ efi_get_next_variable_name_runtime;
+ efi_runtime_services.set_variable = efi_set_variable_runtime;
+ efi_update_table_header_crc32(&efi_runtime_services.hdr);
+}
+
+/**
+ * efi_init_variables() - initialize variable services
+ *
+ * Return: status code
+ */
+efi_status_t efi_init_variables(void)
+{
+ return EFI_SUCCESS;
+}