[U-Boot] [PATCH v2 07/11] efi_loader: variable: split UEFI variables from U-Boot environment
AKASHI Takahiro
takahiro.akashi at linaro.org
Wed Apr 24 06:30:41 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/efi_variable.c | 162 ++++++++++++++++++++++++++++++++--
1 file changed, 155 insertions(+), 7 deletions(-)
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
index 2f489ab9db97..f7b1ce2f3350 100644
--- a/lib/efi_loader/efi_variable.c
+++ b/lib/efi_loader/efi_variable.c
@@ -48,6 +48,115 @@
* 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.
+ */
+
+enum efi_var_type {
+ EFI_VAR_TYPE_VOLATILE,
+ EFI_VAR_TYPE_NON_VOLATILE,
+};
+
+struct hsearch_data efi_var_htab;
+struct hsearch_data efi_nv_var_htab;
+
+static char *env_efi_get(const char *name, int type)
+{
+ struct hsearch_data *htab;
+ ENTRY e, *ep;
+
+ /* WATCHDOG_RESET(); */
+
+ if (type == EFI_VAR_TYPE_VOLATILE)
+ htab = &efi_var_htab;
+ else
+ htab = &efi_nv_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, int type)
+{
+ struct hsearch_data *htab;
+ ENTRY e, *ep;
+ int ret;
+
+ if (type == EFI_VAR_TYPE_VOLATILE)
+ htab = &efi_var_htab;
+ else
+ htab = &efi_nv_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;
+}
+
+int efi_variable_import(const char *buf, int check)
+{
+ env_t *ep = (env_t *)buf;
+
+ if (check) {
+ u32 crc;
+
+ memcpy(&crc, &ep->crc, sizeof(crc));
+
+ if (crc32(0, ep->data, CONFIG_ENV_EFI_SIZE) != crc) {
+ pr_err("bad CRC of UEFI variables\n");
+ return -ENOMSG; /* needed for env_load() */
+ }
+ }
+
+ if (himport_r(&efi_nv_var_htab, (char *)ep->data, CONFIG_ENV_EFI_SIZE,
+ '\0', 0, 0, 0, NULL))
+ return 0;
+
+ pr_err("Cannot import environment: errno = %d\n", errno);
+
+ /* set_default_env("import failed", 0); */
+
+ return -EIO;
+}
+
+/* Export the environment and generate CRC for it. */
+int efi_variable_export(env_t *env_out)
+{
+ char *res;
+ ssize_t len;
+
+ res = (char *)env_out->data;
+ len = hexport_r(&efi_nv_var_htab, '\0', 0, &res, CONFIG_ENV_EFI_SIZE,
+ 0, NULL);
+ if (len < 0) {
+ pr_err("Cannot export environment: errno = %d\n", errno);
+ return 1;
+ }
+
+ env_out->crc = crc32(0, env_out->data, CONFIG_ENV_EFI_SIZE);
+
+ return 0;
+}
+
#define PREFIX_LEN (strlen("efi_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_"))
/**
@@ -184,7 +293,9 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
EFI_PRINT("get '%s'\n", native_name);
- val = env_get(native_name);
+ val = env_efi_get(native_name, EFI_VAR_TYPE_VOLATILE);
+ if (!val)
+ val = env_efi_get(native_name, EFI_VAR_TYPE_NON_VOLATILE);
free(native_name);
if (!val)
return EFI_EXIT(EFI_NOT_FOUND);
@@ -326,7 +437,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};
@@ -382,10 +493,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);
@@ -420,6 +560,7 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
char *native_name = NULL, *val = NULL, *s;
efi_status_t ret = EFI_SUCCESS;
u32 attr;
+ int type;
EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes,
data_size, data);
@@ -435,14 +576,18 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
#define ACCESS_ATTR (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)
+ type = (attributes & EFI_VARIABLE_NON_VOLATILE) ?
+ EFI_VAR_TYPE_NON_VOLATILE : EFI_VAR_TYPE_VOLATILE;
if ((data_size == 0) || !(attributes & ACCESS_ATTR)) {
/* delete the variable: */
- env_set(native_name, NULL);
+ env_efi_set(native_name, NULL, type);
ret = EFI_SUCCESS;
goto out;
}
- val = env_get(native_name);
+ val = env_efi_get(native_name, EFI_VAR_TYPE_VOLATILE);
+ if (!val)
+ val = env_efi_get(native_name, EFI_VAR_TYPE_NON_VOLATILE);
if (val) {
parse_attr(val, &attr);
@@ -493,9 +638,12 @@ 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))
+ if (env_efi_set(native_name, val, type))
ret = EFI_DEVICE_ERROR;
+ /* FIXME: what if save failed? */
+ env_efi_save();
+
out:
free(native_name);
free(val);
--
2.20.1
More information about the U-Boot
mailing list