* Copyright (c) 2017 Rob Clark
*/
-#include <malloc.h>
-#include <charset.h>
+#include <common.h>
#include <efi_loader.h>
+#include <env_internal.h>
+#include <hexdump.h>
+#include <malloc.h>
+#include <search.h>
#define READ_ONLY BIT(31)
#define PREFIX_LEN (strlen("efi_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_"))
-static int hex(int 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 int hex2mem(u8 *mem, const char *hexstr, int size)
-{
- int nibble;
- int i;
-
- for (i = 0; i < size; i++) {
- if (*hexstr == '\0')
- break;
-
- nibble = hex(*hexstr);
- if (nibble < 0)
- return -1;
-
- *mem = nibble;
- hexstr++;
-
- nibble = hex(*hexstr);
- if (nibble < 0)
- return -1;
-
- *mem = (*mem << 4) | nibble;
- hexstr++;
- mem++;
- }
-
- return i;
-}
-
-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;
-}
-
+/**
+ * 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)
{
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);
return NULL;
}
-/* parse attributes part of variable value, if present: */
+/**
+ * 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;
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"))) {
return str;
}
-/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetVariable.28.29 */
+/**
+ * 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)
if (ret)
return EFI_EXIT(ret);
- debug("%s: get '%s'\n", __func__, native_name);
+ EFI_PRINT("get '%s'\n", native_name);
val = env_get(native_name);
free(native_name);
in_size = *data_size;
if ((s = prefix(val, "(blob)"))) {
- unsigned len = strlen(s);
+ size_t len = strlen(s);
/* number of hexadecimal digits must be even */
if (len & 1)
len /= 2;
*data_size = len;
- if (in_size < len)
- return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+ if (in_size < len) {
+ ret = EFI_BUFFER_TOO_SMALL;
+ goto out;
+ }
if (!data)
return EFI_EXIT(EFI_INVALID_PARAMETER);
- if (hex2mem(data, s, len) != len)
+ if (hex2bin(data, s, len))
return EFI_EXIT(EFI_DEVICE_ERROR);
- debug("%s: got value: \"%s\"\n", __func__, s);
+ 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)
- return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+ 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';
- debug("%s: got value: \"%s\"\n", __func__, (char *)data);
+ EFI_PRINT("got value: \"%s\"\n", (char *)data);
} else {
- debug("%s: invalid value: '%s'\n", __func__, val);
+ EFI_PRINT("invalid value: '%s'\n", val);
return EFI_EXIT(EFI_DEVICE_ERROR);
}
+out:
if (attributes)
*attributes = attr & EFI_VARIABLE_MASK;
- return EFI_EXIT(EFI_SUCCESS);
+ return EFI_EXIT(ret);
}
-/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetNextVariableName.28.29 */
+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);
- return EFI_EXIT(EFI_DEVICE_ERROR);
+ 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);
}
-/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#SetVariable.28.29 */
+/**
+ * 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 || !vendor) {
+ if (!variable_name || !*variable_name || !vendor ||
+ ((attributes & EFI_VARIABLE_RUNTIME_ACCESS) &&
+ !(attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS))) {
ret = EFI_INVALID_PARAMETER;
goto out;
}
if (ret)
goto out;
-#define ACCESS_ATTR (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)
-
- if ((data_size == 0) || !(attributes & ACCESS_ATTR)) {
- /* delete the variable: */
- env_set(native_name, NULL);
- ret = EFI_SUCCESS;
- goto out;
- }
-
- val = env_get(native_name);
- if (val) {
- parse_attr(val, &attr);
+ old_val = env_get(native_name);
+ if (old_val) {
+ old_val = parse_attr(old_val, &attr);
+ /* check read-only first */
if (attr & READ_ONLY) {
- /* We should not free val */
- val = NULL;
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(2 * data_size + strlen("{ro,run,boot}(blob)") + 1);
+ 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_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS);
+ /* 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_BOOTSERVICE_ACCESS)
+ 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");
}
s += sprintf(s, "}");
+ if (old_size)
+ /* APPEND_WRITE */
+ s += sprintf(s, old_val);
+ else
+ s += sprintf(s, "(blob)");
+
/* store payload: */
- s += sprintf(s, "(blob)");
- s = mem2hex(s, data, data_size);
+ s = bin2hex(s, data, data_size);
*s = '\0';
- debug("%s: setting: %s=%s\n", __func__, native_name, val);
+ EFI_PRINT("setting: %s=%s\n", native_name, val);
if (env_set(native_name, val))
ret = EFI_DEVICE_ERROR;
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;
+}