All of lore.kernel.org
 help / color / mirror / Atom feed
From: Heinrich Schuchardt <xypron.glpk@gmx.de>
To: u-boot@lists.denx.de
Subject: [PATCH 10/16] efi_loader: UEFI variable persistence
Date: Fri, 27 Mar 2020 06:27:54 +0100	[thread overview]
Message-ID: <20200327052800.11022-11-xypron.glpk@gmx.de> (raw)
In-Reply-To: <20200327052800.11022-1-xypron.glpk@gmx.de>

Persist non-volatile UEFI variables in a file on the EFI system partition.

The file is written:

* whenever a non-volatile UEFI variable is changed after initialization
  of the UEFI sub-system.
* upon ExitBootServices()

The file is read during the UEFI sub-system initialization to restore
non-volatile UEFI variables.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
---
 include/efi_variable.h              |  36 +++++
 lib/efi_loader/Kconfig              |   8 +
 lib/efi_loader/Makefile             |   1 +
 lib/efi_loader/efi_variable.c       |  12 +-
 lib/efi_loader/efi_variables_file.c | 235 ++++++++++++++++++++++++++++
 5 files changed, 291 insertions(+), 1 deletion(-)
 create mode 100644 include/efi_variable.h
 create mode 100644 lib/efi_loader/efi_variables_file.c

diff --git a/include/efi_variable.h b/include/efi_variable.h
new file mode 100644
index 0000000000..fb8294fc2e
--- /dev/null
+++ b/include/efi_variable.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * File interface for UEFI variables
+ *
+ * Copyright (c) 2020, Heinrich Schuchardt
+ */
+
+#ifndef _EFI_VARIABLE_H
+#define _EFI_VARIABLE_H
+
+#define EFI_VAR_FILE_NAME "ubootefi.var"
+
+#define EFI_VAR_BUF_SIZE 0x4000
+
+#define EFI_VAR_FILE_MAGIC 0x7261566966456255 /* UbEfiVar */
+
+struct efi_var_entry {
+	u32 length;
+	u32 attr;
+	efi_guid_t guid;
+	u16 name[0];
+};
+
+struct efi_var_file {
+	u64 reserved; /* May be overwritten by memory probing */
+	u64 magic;
+	u32 length;
+	u32 crc32;
+	struct efi_var_entry var[0];
+};
+
+efi_status_t efi_var_to_file(void);
+
+efi_status_t efi_var_from_file(void);
+
+#endif
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index e10ca05549..41705fc252 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -27,6 +27,14 @@ config EFI_LOADER

 if EFI_LOADER

+config EFI_VARIABLE_FILE_STORE
+	bool "Store non-volatile UEFI variables as file"
+	depends on FAT_WRITE
+	default y
+	help
+	  Select tis option if you want non-volatile UEFI variables to be stored
+	  as file /ubootefi.var on the EFI system partition.
+
 config EFI_GET_TIME
 	bool "GetTime() runtime service"
 	depends on DM_RTC
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index 9b3b704473..621a767ab3 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -35,6 +35,7 @@ obj-y += efi_runtime.o
 obj-y += efi_setup.o
 obj-$(CONFIG_EFI_UNICODE_COLLATION_PROTOCOL2) += efi_unicode_collation.o
 obj-y += efi_variable.o
+obj-y += efi_variables_file.o
 obj-y += efi_watchdog.o
 obj-$(CONFIG_LCD) += efi_gop.o
 obj-$(CONFIG_DM_VIDEO) += efi_gop.o
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
index d99ad6ddae..952a0a0db7 100644
--- a/lib/efi_loader/efi_variable.c
+++ b/lib/efi_loader/efi_variable.c
@@ -7,6 +7,7 @@

 #include <common.h>
 #include <efi_loader.h>
+#include <efi_variable.h>
 #include <env_internal.h>
 #include <hexdump.h>
 #include <malloc.h>
@@ -604,6 +605,11 @@ efi_status_t efi_set_variable_int(u16 *variable_name,
 	if (env_set(native_name, val))
 		ret = EFI_DEVICE_ERROR;

+	/* Write non-volatile EFI variables to file */
+	if (attributes && EFI_VARIABLE_NON_VOLATILE &&
+	    ret == EFI_SUCCESS && efi_obj_list_initialized == EFI_SUCCESS)
+		efi_var_to_file();
+
 out:
 	free(native_name);
 	free(val);
@@ -694,6 +700,10 @@ efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *vendor,
  */
 void efi_variables_boot_exit_notify(void)
 {
+	/* Write non-volatile EFI variables to file */
+	efi_var_to_file();
+
+	/* Switch variable services functions to runtime version */
 	efi_runtime_services.get_variable = efi_get_variable_runtime;
 	efi_runtime_services.get_next_variable_name =
 				efi_get_next_variable_name_runtime;
@@ -708,5 +718,5 @@ void efi_variables_boot_exit_notify(void)
  */
 efi_status_t efi_init_variables(void)
 {
-	return EFI_SUCCESS;
+	return efi_var_from_file();
 }
diff --git a/lib/efi_loader/efi_variables_file.c b/lib/efi_loader/efi_variables_file.c
new file mode 100644
index 0000000000..4a918d3fde
--- /dev/null
+++ b/lib/efi_loader/efi_variables_file.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * File interface for UEFI variables
+ *
+ * Copyright (c) 2020, Heinrich Schuchardt
+ */
+
+#include <common.h>
+#include <charset.h>
+#include <fs.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+#include <u-boot/crc.h>
+
+#define PART_STR_LEN 10
+
+/**
+ * efi_set_blk_dev_to_system_partition() - select EFI system partition
+ *
+ * Set the EFI system partition as current block device.
+ *
+ * Return:	status code
+ */
+static efi_status_t __maybe_unused efi_set_blk_dev_to_system_partition(void)
+{
+	char part_str[PART_STR_LEN];
+	int r;
+
+	if (!efi_system_partition.if_type)
+		return EFI_NOT_FOUND;
+	snprintf(part_str, PART_STR_LEN, "%u:%u",
+		 efi_system_partition.devnum, efi_system_partition.part);
+	r = fs_set_blk_dev(blk_get_if_type_name(efi_system_partition.if_type),
+			   part_str, FS_TYPE_ANY);
+	if (r) {
+		printf("Cannot read EFI system partition\n");
+		return EFI_DEVICE_ERROR;
+	}
+	return EFI_SUCCESS;
+}
+
+/**
+ * efi_var_collect() - collect non-volatile variables in buffer
+ *
+ * A buffer is allocated and filled with all non-volatile variables in a
+ * format ready to be written to disk.
+ *
+ * @bufp:	pointer to pointer of buffer with collected variables
+ * @lenp:	pointer to length of buffer
+ * Return:	status code
+ */
+static efi_status_t __maybe_unused efi_var_collect(struct efi_var_file **bufp,
+						   loff_t *lenp)
+{
+	size_t len = EFI_VAR_BUF_SIZE;
+	struct efi_var_file *buf;
+	struct efi_var_entry *var, *old_var;
+	size_t old_var_name_length = 2;
+
+	*bufp = NULL; /* Avoid double free() */
+	buf = calloc(1, len);
+	if (!buf)
+		return EFI_OUT_OF_RESOURCES;
+	var = buf->var;
+	old_var = var;
+	for (;;) {
+		efi_uintn_t data_length, var_name_length;
+		u8 *data;
+		efi_status_t ret;
+
+		if ((uintptr_t)buf + len <=
+		    (uintptr_t)var->name + old_var_name_length)
+			return EFI_BUFFER_TOO_SMALL;
+
+		var_name_length = (uintptr_t)buf + len - (uintptr_t)var->name;
+		memcpy(var->name, old_var->name, old_var_name_length);
+		guidcpy(&var->guid, &old_var->guid);
+		ret = efi_get_next_variable_name_int(
+				&var_name_length, var->name, &var->guid);
+		if (ret == EFI_NOT_FOUND)
+			break;
+		if (ret != EFI_SUCCESS) {
+			free(buf);
+			return ret;
+		}
+		old_var_name_length = var_name_length;
+		old_var = var;
+
+		data = (u8 *)var->name + old_var_name_length;
+		data_length = (uintptr_t)buf + len - (uintptr_t)data;
+		ret = efi_get_variable_int(var->name, &var->guid,
+					   &var->attr, &data_length, data);
+		if (ret != EFI_SUCCESS) {
+			free(buf);
+			return ret;
+		}
+		if (!(var->attr & EFI_VARIABLE_NON_VOLATILE))
+			continue;
+		var->length = data_length;
+		var = (struct efi_var_entry *)
+		      ALIGN((uintptr_t)data + data_length, 8);
+	}
+
+	buf->reserved = 0;
+	buf->magic = EFI_VAR_FILE_MAGIC;
+	len = (uintptr_t)var - (uintptr_t)buf;
+	buf->crc32 = crc32(0, (u8 *)buf->var,
+			   len - sizeof(struct efi_var_file));
+	buf->length = len;
+	*bufp = buf;
+	*lenp = len;
+
+	return EFI_SUCCESS;
+}
+
+/**
+ * efi_var_to_file() - save non-volatile variables as file
+ *
+ * File ubootefi.var is created on the EFI system partion.
+ *
+ * Return:	status code
+ */
+efi_status_t efi_var_to_file(void)
+{
+#ifdef CONFIG_EFI_VARIABLE_FILE_STORE
+	efi_status_t ret;
+	struct efi_var_file *buf;
+	loff_t len;
+	loff_t actlen;
+	int r;
+
+	ret = efi_var_collect(&buf, &len);
+	if (ret != EFI_SUCCESS)
+		goto error;
+
+	ret = efi_set_blk_dev_to_system_partition();
+	if (ret != EFI_SUCCESS)
+		goto error;
+
+	r = fs_write(EFI_VAR_FILE_NAME, map_to_sysmem(buf), 0, len, &actlen);
+	if (r || len != actlen)
+		ret =  EFI_DEVICE_ERROR;
+
+error:
+	if (ret != EFI_SUCCESS)
+		printf("Failed to persist EFI variables\n");
+	free(buf);
+	return ret;
+#else
+	return EFI_SUCCESS;
+#endif
+}
+
+/**
+ * efi_var_restore() - restore EFI variables from buffer
+ *
+ * @buf:	buffer
+ * Return:	status code
+ */
+static efi_status_t __maybe_unused efi_var_restore(struct efi_var_file *buf)
+{
+	struct efi_var_entry *var, *last_var;
+	efi_status_t ret;
+
+	if (buf->reserved || buf->magic != EFI_VAR_FILE_MAGIC ||
+	    buf->crc32 != crc32(0, (u8 *)buf->var,
+				buf->length - sizeof(struct efi_var_file))) {
+		printf("Invalid EFI variables file\n");
+		return EFI_INVALID_PARAMETER;
+	}
+
+	var = buf->var;
+	last_var = (struct efi_var_entry *)((u8 *)buf + buf->length);
+	while (var < last_var) {
+		u16 *data = var->name + u16_strlen(var->name) + 1;
+
+		if (var->attr & EFI_VARIABLE_NON_VOLATILE && var->length) {
+			ret = efi_set_variable_int(var->name, &var->guid,
+						   var->attr, var->length,
+						   data);
+			if (ret != EFI_SUCCESS)
+				printf("Failed to set EFI variable %ls\n",
+				       var->name);
+			}
+		var = (struct efi_var_entry *)
+		      ALIGN((uintptr_t)data + var->length, 8);
+	}
+	return EFI_SUCCESS;
+}
+
+/**
+ * efi_var_from_file() - read variables from file
+ *
+ * File ubootefi.var is read from the EFI system partitions and the variables
+ * stored in the file are created.
+ *
+ * In case the file does not exist yet or a variable cannot be set EFI_SUCCESS
+ * is returned
+ *
+ * Return:	status code
+ */
+efi_status_t efi_var_from_file(void)
+{
+#ifdef CONFIG_EFI_VARIABLE_FILE_STORE
+	struct efi_var_file *buf;
+	loff_t len;
+	efi_status_t ret;
+	int r;
+
+	buf = calloc(1, EFI_VAR_BUF_SIZE);
+	if (!buf) {
+		printf("Out of memory\n");
+		return EFI_OUT_OF_RESOURCES;
+	}
+
+	ret = efi_set_blk_dev_to_system_partition();
+	if (ret != EFI_SUCCESS) {
+		printf("No EFI system partition\n");
+		goto error;
+	}
+	r = fs_read(EFI_VAR_FILE_NAME, map_to_sysmem(buf), 0, EFI_VAR_BUF_SIZE,
+		    &len);
+	if (r || len < sizeof(struct efi_var_file)) {
+		printf("Failed to load EFI variables\n");
+		goto error;
+	}
+	if (buf->length != len || efi_var_restore(buf) != EFI_SUCCESS)
+		printf("Invalid EFI variables file\n");
+error:
+	free(buf);
+#endif
+	return EFI_SUCCESS;
+}
--
2.25.1

  parent reply	other threads:[~2020-03-27  5:27 UTC|newest]

Thread overview: 53+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-03-27  5:27 [PATCH 00/16] efi_loader: non-volatile and runtime variables Heinrich Schuchardt
2020-03-27  5:27 ` [PATCH 01/16] cmd: efidebug: fix int to pointer cast Heinrich Schuchardt
2020-03-27  5:27 ` [PATCH 02/16] efi_loader: only reserve memory if fdt node enabled Heinrich Schuchardt
2020-03-27  5:27 ` [PATCH 03/16] efi_loader: eliminate EFI_CALL() for variable access Heinrich Schuchardt
2020-03-27  5:27 ` [PATCH 04/16] part: detect EFI system partition Heinrich Schuchardt
2020-03-27  6:35   ` Punit Agrawal
2020-03-27  7:21     ` Heinrich Schuchardt
2020-03-27  5:27 ` [PATCH 05/16] efi_loader: identify " Heinrich Schuchardt
2020-03-27  5:27 ` [PATCH 06/16] efi_loader: keep attributes in efi_set_variable_int() Heinrich Schuchardt
2020-03-27  5:27 ` [PATCH 07/16] efi_loader: export initialization state Heinrich Schuchardt
2020-03-27  5:27 ` [PATCH 08/16] efi_loader: change setup sequence Heinrich Schuchardt
2020-03-27  5:27 ` [PATCH 09/16] efi_loader: imply FAT, FAT_WRITE Heinrich Schuchardt
2020-03-31  5:28   ` AKASHI Takahiro
2020-03-31  6:44   ` Heinrich Schuchardt
2020-03-31  7:44     ` AKASHI Takahiro
2020-03-31  8:20       ` Mark Kettenis
2020-04-01  0:31         ` AKASHI Takahiro
2020-04-01  6:43           ` Heinrich Schuchardt
2020-04-01 17:56           ` Mark Kettenis
2020-04-02  1:34             ` AKASHI Takahiro
2020-03-31 13:08       ` Heinrich Schuchardt
2020-03-31 23:57         ` AKASHI Takahiro
2020-04-01  1:14           ` AKASHI Takahiro
2020-04-01  6:31             ` Heinrich Schuchardt
2020-04-01  7:04               ` AKASHI Takahiro
2020-04-02 12:33                 ` Ilias Apalodimas
2020-03-27  5:27 ` Heinrich Schuchardt [this message]
2020-03-27  8:07   ` [PATCH 10/16] efi_loader: UEFI variable persistence Punit Agrawal
2020-03-27 10:30     ` Heinrich Schuchardt
2020-03-30 10:03       ` Punit Agrawal
2020-04-06 16:06       ` Ilias Apalodimas
2021-01-02 22:15   ` Peter Robinson
2020-03-27  5:27 ` [PATCH 11/16] efi_loader: export efi_convert_pointer() Heinrich Schuchardt
2020-03-27  5:27 ` [PATCH 12/16] efi_loader: optional pointer for ConvertPointer Heinrich Schuchardt
2020-03-31  5:23   ` AKASHI Takahiro
2020-03-31  6:52   ` Heinrich Schuchardt
2020-03-31  7:51     ` AKASHI Takahiro
2020-03-27  5:27 ` [PATCH 13/16] efi_loader: memory buffer for variables Heinrich Schuchardt
2020-03-27  8:09   ` Punit Agrawal
2020-03-27 10:45     ` Heinrich Schuchardt
2020-03-30 10:50       ` Punit Agrawal
2020-03-27  5:27 ` [PATCH 14/16] efi_loader: use memory based variable storage Heinrich Schuchardt
2020-03-27 19:44 ` [PATCH 00/16] efi_loader: non-volatile and runtime variables Simon Glass
2020-03-28  6:42   ` Heinrich Schuchardt
2020-03-31  6:05 ` [PATCH 15/16] efi_loader: enable UEFI variables at runtime Heinrich Schuchardt
2020-03-31  6:05   ` [PATCH 15/16] efi_selftest: adjust runtime test for variables Heinrich Schuchardt
2020-04-01  1:41   ` [PATCH 15/16] efi_loader: enable UEFI variables at runtime AKASHI Takahiro
2020-04-01  6:26     ` Heinrich Schuchardt
2020-04-01  6:51       ` AKASHI Takahiro
2020-03-31  6:07 ` [PATCH 16/16] efi_selftest: adjust runtime test for variables Heinrich Schuchardt
2020-04-01  1:05   ` AKASHI Takahiro
2020-04-01  6:37     ` Heinrich Schuchardt
2020-04-01  7:27       ` AKASHI Takahiro

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200327052800.11022-11-xypron.glpk@gmx.de \
    --to=xypron.glpk@gmx.de \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.