[U-Boot] [PATCH v3 3/7] efi_loader: variable: split UEFI variables from U-Boot environment
AKASHI Takahiro
takahiro.akashi at linaro.org
Tue Jun 4 06:52:07 UTC 2019
UEFI volatile variables are managed in efi_var_htab while UEFI non-volatile
variables are in efi_nv_var_htab. At every SetVariable API, env_efi_save()
will also be called to save data cache (hash table) to persistent storage.
Signed-off-by: AKASHI Takahiro <takahiro.akashi at linaro.org>
---
lib/efi_loader/Kconfig | 10 +
lib/efi_loader/efi_variable.c | 342 ++++++++++++++++++++++++++--------
2 files changed, 275 insertions(+), 77 deletions(-)
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index cd5436c576b1..8bf4b1754d06 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -18,6 +18,16 @@ config EFI_LOADER
if EFI_LOADER
+choice
+ prompt "Select variables storage"
+ default EFI_VARIABLE_USE_ENV
+
+config EFI_VARIABLE_USE_ENV
+ bool "Same device as U-Boot environment"
+ select ENV_EFI
+
+endchoice
+
config EFI_GET_TIME
bool "GetTime() runtime service"
depends on DM_RTC
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
index e56053194dae..d9887be938c2 100644
--- a/lib/efi_loader/efi_variable.c
+++ b/lib/efi_loader/efi_variable.c
@@ -48,6 +48,66 @@
* converted to utf16?
*/
+/*
+ * We will maintain two variable database: one for volatile variables,
+ * the other for non-volatile variables. The former exists only in memory
+ * and will go away at re-boot. The latter is currently backed up by the same
+ * device as U-Boot environment and also works as variables cache.
+ *
+ * struct hsearch_data efi_var_htab
+ * struct hsearch_data efi_nv_var_htab
+ */
+
+static char *env_efi_get(const char *name, bool is_non_volatile)
+{
+ struct hsearch_data *htab;
+ ENTRY e, *ep;
+
+ /* WATCHDOG_RESET(); */
+
+ if (is_non_volatile)
+ htab = &efi_nv_var_htab;
+ else
+ htab = &efi_var_htab;
+
+ e.key = name;
+ e.data = NULL;
+ hsearch_r(e, FIND, &ep, htab, 0);
+
+ return ep ? ep->data : NULL;
+}
+
+static int env_efi_set(const char *name, const char *value,
+ bool is_non_volatile)
+{
+ struct hsearch_data *htab;
+ ENTRY e, *ep;
+ int ret;
+
+ if (is_non_volatile)
+ htab = &efi_nv_var_htab;
+ else
+ htab = &efi_var_htab;
+
+ /* delete */
+ if (!value || *value == '\0') {
+ ret = hdelete_r(name, htab, H_PROGRAMMATIC);
+ return !ret;
+ }
+
+ /* set */
+ e.key = name;
+ e.data = (char *)value;
+ hsearch_r(e, ENTER, &ep, htab, H_PROGRAMMATIC);
+ if (!ep) {
+ printf("## Error inserting \"%s\" variable, errno=%d\n",
+ name, errno);
+ return 1;
+ }
+
+ return 0;
+}
+
#define PREFIX_LEN (strlen("efi_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_"))
/**
@@ -147,24 +207,12 @@ static const char *parse_attr(const char *str, u32 *attrp)
return str;
}
-/**
- * efi_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)
+static
+efi_status_t EFIAPI efi_get_variable_common(u16 *variable_name,
+ const efi_guid_t *vendor,
+ u32 *attributes,
+ efi_uintn_t *data_size, void *data,
+ bool is_non_volatile)
{
char *native_name;
efi_status_t ret;
@@ -172,22 +220,19 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
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);
+ return ret;
EFI_PRINT("get '%s'\n", native_name);
- val = env_get(native_name);
+ val = env_efi_get(native_name, is_non_volatile);
free(native_name);
if (!val)
- return EFI_EXIT(EFI_NOT_FOUND);
+ return EFI_NOT_FOUND;
val = parse_attr(val, &attr);
@@ -198,7 +243,7 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
/* number of hexadecimal digits must be even */
if (len & 1)
- return EFI_EXIT(EFI_DEVICE_ERROR);
+ return EFI_DEVICE_ERROR;
/* two characters per byte: */
len /= 2;
@@ -210,10 +255,10 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
}
if (!data)
- return EFI_EXIT(EFI_INVALID_PARAMETER);
+ return EFI_INVALID_PARAMETER;
if (hex2bin(data, s, len))
- return EFI_EXIT(EFI_DEVICE_ERROR);
+ return EFI_DEVICE_ERROR;
EFI_PRINT("got value: \"%s\"\n", s);
} else if ((s = prefix(val, "(utf8)"))) {
@@ -227,7 +272,7 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
}
if (!data)
- return EFI_EXIT(EFI_INVALID_PARAMETER);
+ return EFI_INVALID_PARAMETER;
memcpy(data, s, len);
((char *)data)[len] = '\0';
@@ -235,13 +280,67 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
EFI_PRINT("got value: \"%s\"\n", (char *)data);
} else {
EFI_PRINT("invalid value: '%s'\n", val);
- return EFI_EXIT(EFI_DEVICE_ERROR);
+ return EFI_DEVICE_ERROR;
}
out:
if (attributes)
*attributes = attr & EFI_VARIABLE_MASK;
+ return ret;
+}
+
+static
+efi_status_t EFIAPI efi_get_volatile_variable(u16 *variable_name,
+ const efi_guid_t *vendor,
+ u32 *attributes,
+ efi_uintn_t *data_size,
+ void *data)
+{
+ return efi_get_variable_common(variable_name, vendor, attributes,
+ data_size, data, false);
+}
+
+efi_status_t EFIAPI efi_get_nonvolatile_variable(u16 *variable_name,
+ const efi_guid_t *vendor,
+ u32 *attributes,
+ efi_uintn_t *data_size,
+ void *data)
+{
+ return efi_get_variable_common(variable_name, vendor, attributes,
+ data_size, data, true);
+}
+
+/**
+ * efi_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)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes,
+ data_size, data);
+
+ ret = efi_get_volatile_variable(variable_name, vendor, attributes,
+ data_size, data);
+ if (ret == EFI_NOT_FOUND)
+ ret = efi_get_nonvolatile_variable(variable_name, vendor,
+ attributes, data_size, data);
+
return EFI_EXIT(ret);
}
@@ -331,7 +430,7 @@ 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;
+ char *native_name, *variable, *tmp_list, *merged_list;
ssize_t name_len, list_len;
char regex[256];
char * const regexlist[] = {regex};
@@ -387,10 +486,39 @@ efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size,
efi_cur_variable = NULL;
snprintf(regex, 256, "efi_.*-.*-.*-.*-.*_.*");
- list_len = hexport_r(&env_htab, '\n',
+ list_len = hexport_r(&efi_var_htab, '\n',
H_MATCH_REGEX | H_MATCH_KEY,
&efi_variables_list, 0, 1, regexlist);
- /* 1 indicates that no match was found */
+ /*
+ * Note: '1' indicates that nothing is matched
+ */
+ if (list_len <= 1) {
+ free(efi_variables_list);
+ efi_variables_list = NULL;
+ list_len = hexport_r(&efi_nv_var_htab, '\n',
+ H_MATCH_REGEX | H_MATCH_KEY,
+ &efi_variables_list, 0, 1,
+ regexlist);
+ } else {
+ tmp_list = NULL;
+ list_len = hexport_r(&efi_nv_var_htab, '\n',
+ H_MATCH_REGEX | H_MATCH_KEY,
+ &tmp_list, 0, 1,
+ regexlist);
+ if (list_len <= 1) {
+ list_len = 2; /* don't care actual number */
+ } else {
+ /* merge two variables lists */
+ merged_list = malloc(strlen(efi_variables_list)
+ + strlen(tmp_list) + 1);
+ strcpy(merged_list, efi_variables_list);
+ strcat(merged_list, tmp_list);
+ free(efi_variables_list);
+ free(tmp_list);
+ efi_variables_list = merged_list;
+ }
+ }
+
if (list_len <= 1)
return EFI_EXIT(EFI_NOT_FOUND);
@@ -403,77 +531,71 @@ efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size,
return EFI_EXIT(ret);
}
-/**
- * efi_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)
+static
+efi_status_t EFIAPI efi_set_variable_common(u16 *variable_name,
+ const efi_guid_t *vendor,
+ u32 attributes,
+ efi_uintn_t data_size,
+ const void *data,
+ bool is_non_volatile)
{
char *native_name = NULL, *val = NULL, *s;
- efi_status_t ret = EFI_SUCCESS;
+ efi_uintn_t size;
u32 attr;
-
- EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes,
- data_size, data);
+ efi_status_t ret = EFI_SUCCESS;
/* TODO: implement APPEND_WRITE */
if (!variable_name || !vendor ||
(attributes & EFI_VARIABLE_APPEND_WRITE)) {
ret = EFI_INVALID_PARAMETER;
- goto out;
+ goto err;
}
ret = efi_to_native(&native_name, variable_name, vendor);
if (ret)
- goto out;
+ goto err;
#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;
+ /* check if a variable exists */
+ size = 0;
+ ret = EFI_CALL(efi_get_variable(variable_name, vendor, &attr,
+ &size, NULL));
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ if ((is_non_volatile && !(attr & EFI_VARIABLE_NON_VOLATILE)) ||
+ (!is_non_volatile && (attr & EFI_VARIABLE_NON_VOLATILE))) {
+ ret = EFI_INVALID_PARAMETER;
+ goto err;
+ }
}
- val = env_get(native_name);
- if (val) {
- parse_attr(val, &attr);
-
- /* We should not free val */
- val = NULL;
- if (attr & READ_ONLY) {
- ret = EFI_WRITE_PROTECTED;
+ /* delete a variable */
+ if (data_size == 0 || !(attributes & ACCESS_ATTR)) {
+ if (size) {
+ if (attr & READ_ONLY) {
+ ret = EFI_WRITE_PROTECTED;
+ goto err;
+ }
goto out;
}
+ ret = EFI_SUCCESS;
+ goto err; /* not error, but nothing to do */
+ }
+ /* create/modify a variable */
+ if (size && attr != attributes) {
/*
* attributes won't be changed
* TODO: take care of APPEND_WRITE once supported
*/
- if (attr != attributes) {
- ret = EFI_INVALID_PARAMETER;
- goto out;
- }
+ ret = EFI_INVALID_PARAMETER;
+ goto err;
}
val = malloc(2 * data_size + strlen("{ro,run,boot,nv}(blob)") + 1);
if (!val) {
ret = EFI_OUT_OF_RESOURCES;
- goto out;
+ goto err;
}
s = val;
@@ -487,7 +609,7 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
EFI_VARIABLE_RUNTIME_ACCESS);
s += sprintf(s, "{");
while (attributes) {
- u32 attr = 1 << (ffs(attributes) - 1);
+ attr = 1 << (ffs(attributes) - 1);
if (attr == EFI_VARIABLE_NON_VOLATILE)
s += sprintf(s, "nv");
@@ -509,12 +631,78 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
EFI_PRINT("setting: %s=%s\n", native_name, val);
- if (env_set(native_name, val))
+out:
+ ret = EFI_SUCCESS;
+ if (env_efi_set(native_name, val, is_non_volatile))
ret = EFI_DEVICE_ERROR;
-out:
+err:
free(native_name);
free(val);
+ return ret;
+}
+
+static
+efi_status_t EFIAPI efi_set_volatile_variable(u16 *variable_name,
+ const efi_guid_t *vendor,
+ u32 attributes,
+ efi_uintn_t data_size,
+ const void *data)
+{
+ return efi_set_variable_common(variable_name, vendor, attributes,
+ data_size, data, false);
+}
+
+efi_status_t EFIAPI efi_set_nonvolatile_variable(u16 *variable_name,
+ const efi_guid_t *vendor,
+ u32 attributes,
+ efi_uintn_t data_size,
+ const void *data)
+{
+ efi_status_t ret;
+
+ ret = efi_set_variable_common(variable_name, vendor, attributes,
+ data_size, data, true);
+ if (ret == EFI_SUCCESS)
+ /* FIXME: what if save failed? */
+ if (env_efi_save())
+ ret = EFI_DEVICE_ERROR;
+
+ return ret;
+}
+
+/**
+ * efi_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)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes,
+ data_size, data);
+
+ if (attributes & EFI_VARIABLE_NON_VOLATILE)
+ ret = efi_set_nonvolatile_variable(variable_name, vendor,
+ attributes,
+ data_size, data);
+ else
+ ret = efi_set_volatile_variable(variable_name, vendor,
+ attributes, data_size, data);
+
return EFI_EXIT(ret);
}
--
2.21.0
More information about the U-Boot
mailing list