diff mbox series

efivarfs: Backport patch to update file variable store on SetVariableRT

Message ID 20251223062001.1889274-1-shrkum@qti.qualcomm.com
State New
Headers show
Series efivarfs: Backport patch to update file variable store on SetVariableRT | expand

Commit Message

shrkum@qti.qualcomm.com Dec. 23, 2025, 6:20 a.m. UTC
Backport upstream commit 68daa04654ac to enable persisting EFI variable
updates when U-Boot provides SetVariableRT support via efivarfs. This
addresses limitations on embedded boards that store EFI variables in a
file on the ESP instead of NVRAM.

Upstream commit:
https://github.com/rhboot/efivar/commit/68daa04654acbe1bbaa17ebfc23c371b39e69c6b

Signed-off-by: Shravan Kumar <shrkum@qti.qualcomm.com>
---
 ...file-variable-store-on-SetVariableRT.patch | 239 ++++++++++++++++++
 meta/recipes-bsp/efivar/efivar_39.bb          |   1 +
 2 files changed, 240 insertions(+)
 create mode 100644 meta/recipes-bsp/efivar/efivar/0002-efivarfs-backport-patch-to-update-file-variable-store-on-SetVariableRT.patch
diff mbox series

Patch

diff --git a/meta/recipes-bsp/efivar/efivar/0002-efivarfs-backport-patch-to-update-file-variable-store-on-SetVariableRT.patch b/meta/recipes-bsp/efivar/efivar/0002-efivarfs-backport-patch-to-update-file-variable-store-on-SetVariableRT.patch
new file mode 100644
index 0000000000..d5ec3d5419
--- /dev/null
+++ b/meta/recipes-bsp/efivar/efivar/0002-efivarfs-backport-patch-to-update-file-variable-store-on-SetVariableRT.patch
@@ -0,0 +1,239 @@ 
+From fb90073d51dd8a9f726fbbfcf4fab4aa7781eea3 Mon Sep 17 00:00:00 2001
+From: Ilias Apalodimas <ilias.apalodimas@linaro.org>
+Date: Wed, 18 Jun 2025 22:37:04 +0300
+Subject: [PATCH] efivarfs: Update a file variable store On SetVariable RT
+
+Embedded boards have hardware limitations when storing and managing EFI
+variables. Some hardware comes with an eMMC & an RPMB partition which they
+use to store the EFI variables securely. However, the vast majority of
+boards (using U-Boot), stores the EFI variables in a file in the ESP.
+
+This has a few limitations
+- UEFI secure boot cannot be enabled as it can be very easily
+  overridden
+- SetVariable at runtime is impossible to support
+
+Distros and capsule updates on-disk do rely on the that service though
+and U-Boot does implement a workaround.
+
+U-Boot enables SetVariableRT in the RTPROP table and creates a memory backend,
+so the linux kernel can naturally read and write variables via the efivarfs
+filesystem. Those reads and writes end up in memory though. So they are visible
+while the OS is live and are lost in the event of a reboot.
+
+At the same time it also creates two EFI RO variables.
+RTStorageVolatile -- Holds the filename  the variables are stored relative to
+                     the ESP
+VarToFile -- Holds a binary dump of all the EFI variables that should be
+             preserved (BS, NV, RT).
+
+By using these two variables we can persist the changes after reboots by
+doing
+dd if=/sys/firmware/efi/efivars/VarToFile-b2ac5fc9-92b7-4acd-aeac-11e818c3130c of=/boot/efi/ubootefi.var skip=4 bs=1
+
+So let's plug this functionality into the efivafs backend and enable it
+automatically if those variables are detected.
+
+Upstream-Status: Backport [https://github.com/rhboot/efivar/commit/68daa04654acbe1bbaa17ebfc23c371b39e69c6b]
+
+Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
+---
+ src/efivarfs.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 153 insertions(+), 4 deletions(-)
+
+diff --git a/src/efivarfs.c b/src/efivarfs.c
+index 034d6c19..2dea2525 100644
+--- a/src/efivarfs.c
++++ b/src/efivarfs.c
+@@ -28,6 +28,24 @@
+ #  define EFIVARFS_MAGIC 0xde5e81e4
+ #endif
+ 
++/*
++ * RTStorageVolatile-b2ac5fc9-92b7-4acd-aeac-11e818c3130c holds the name of
++ * the file we need to update relative to the ESP
++ */
++#define NAME_RTSV	"RTStorageVolatile"
++/*
++ * Namespace of the special EFI variables pointing to the file and data we
++ * need to update
++ */
++#define GUID_FILE_STORE_VARS \
++	EFI_GUID(0xB2AC5FC9,0x92B7,0x4ACD,0xAEAC,0x11,0xE8,0x18,0xC3,0x13,0x0C)
++
++static const char *esp_paths[] = {
++	"/boot/efi/",
++	"/boot/",
++	"/efi/"
++};
++
+ static char const default_efivarfs_path[] = "/sys/firmware/efi/efivars/";
+ static char *efivarfs_path;
+ 
+@@ -64,6 +82,137 @@ fini_efivarfs_path(void)
+ 	}
+ }
+ 
++static int
++get_esp_filepath(const char *filename, char *filepath, size_t sz)
++{
++	size_t num_paths = sizeof(esp_paths) / sizeof(esp_paths[0]);
++	size_t rc;
++
++	for (size_t i = 0; i < num_paths; ++i) {
++		struct stat buffer;
++
++		rc = snprintf(filepath, sz, "%s%s", esp_paths[i], filename);
++		if (rc >= sz) {
++			fprintf(stderr, "Error: Filepath too big. Max allowed %ld\n", sz);
++			return -1;
++		}
++		if (!stat(filepath, &buffer))
++			return 0;
++	}
++
++	return -1;
++}
++
++static int
++get_esp_filename(char *filename, size_t sz)
++{
++	size_t size;
++	uint32_t attr;
++	uint8_t *data = NULL;
++	int rc = 0;
++
++	rc = efi_get_variable(GUID_FILE_STORE_VARS, NAME_RTSV, &data, &size, &attr);
++	if (rc < 0)
++		/*
++		 * Return an error here so we can bail out and not try to
++		 * write the file
++		 */
++		return rc;
++
++	if (size > sz) {
++		fprintf(stderr, "Error: Filename too big. Max allowed %ld\n", sz);
++		free(data);
++		return -1;
++	}
++
++	memcpy(filename, data, sz);
++	free(data);
++
++	return 0;
++}
++
++#define make_efivarfs_path(str, guid, name) ({				\
++		asprintf(str, "%s%s-" GUID_FORMAT, get_efivarfs_path(),	\
++			name, GUID_FORMAT_ARGS(&(guid)));		\
++	})
++
++static void
++write_file(const char *filepath) {
++	size_t bytes_read;
++	unsigned char buffer[1024];
++	FILE *output_file = NULL;
++	FILE *var2file = NULL;
++	bool fail = false;
++	char *path;
++	int rc;
++
++	rc = make_efivarfs_path(&path, GUID_FILE_STORE_VARS, "VarToFile");
++	if (rc < 0) {
++		efi_error("make_efivarfs_path failed");
++		exit(1);
++	}
++
++	var2file = fopen(path, "rb");
++	if (!var2file) {
++		fprintf(stderr, "Error: Could not open file '%s'\n", path);
++		goto err;
++	}
++
++	output_file = fopen(filepath, "wb");
++	if (!output_file) {
++		fprintf(stderr, "Error: Could not open file '%s'\n", filepath);
++		goto err;
++	}
++
++	if (fread(buffer, 1, 4, var2file) < 4) {
++		fprintf(stderr, "Error: Could not skip first 4 bytes or '%s' file is too small\n", filepath);
++		fail = true;
++		goto err;
++	}
++
++	while ((bytes_read = fread(buffer, 1, sizeof(buffer), var2file)) > 0) {
++		size_t total_written = 0;
++		while (total_written < bytes_read) {
++			size_t written = fwrite(buffer + total_written, 1, bytes_read - total_written, output_file);
++			if (!written) {
++				fprintf(stderr, "Error: Could not write data to ESP '%s' file\n", filepath);
++				fail = true;
++				goto err;
++			}
++			total_written += written;
++		}
++	}
++
++err:
++	if (path)
++		free(path);
++	if (var2file)
++		fclose(var2file);
++	if (output_file)
++		fclose(output_file);
++
++	if (fail)
++		exit(1);
++}
++
++static void
++efi_update_var_file(void)
++{
++	int rc = 0;
++	char filename[PATH_MAX / 4] = { 0 };
++	char filepath[PATH_MAX] = { 0 };
++
++	rc = get_esp_filename(filename, sizeof(filename));
++	if (rc < 0)
++		return;
++
++	rc = get_esp_filepath(filename, filepath, sizeof(filepath));
++	if (!rc)
++		write_file(filepath);
++	else
++		fprintf(stderr, "Error: '%s' file not found in ESP partition. EFI variable changes won't persist reboots\n", filename);
++}
++
+ static int
+ efivarfs_probe(void)
+ {
+@@ -94,10 +243,6 @@ efivarfs_probe(void)
+ 	return 0;
+ }
+ 
+-#define make_efivarfs_path(str, guid, name) ({				\
+-		asprintf(str, "%s%s-" GUID_FORMAT, get_efivarfs_path(),	\
+-			name, GUID_FORMAT_ARGS(&(guid)));		\
+-	})
+ 
+ static int
+ efivarfs_set_fd_immutable(int fd, int immutable)
+@@ -312,6 +457,8 @@ efivarfs_del_variable(efi_guid_t guid, const char *name)
+ 	if (rc < 0)
+ 		efi_error("unlink failed");
+ 
++	efi_update_var_file();
++
+ 	__typeof__(errno) errno_value = errno;
+ 	free(path);
+ 	errno = errno_value;
+@@ -442,6 +589,8 @@ efivarfs_set_variable(efi_guid_t guid, const char *name, const uint8_t *data,
+ 		goto err;
+ 	}
+ 
++	efi_update_var_file();
++
+ 	/* we're done */
+ 	ret = 0;
+ 
diff --git a/meta/recipes-bsp/efivar/efivar_39.bb b/meta/recipes-bsp/efivar/efivar_39.bb
index fb6b6b3821..c0e8d52181 100644
--- a/meta/recipes-bsp/efivar/efivar_39.bb
+++ b/meta/recipes-bsp/efivar/efivar_39.bb
@@ -9,6 +9,7 @@  COMPATIBLE_HOST = "(i.86|x86_64|arm|aarch64|riscv64).*-linux"
 
 SRC_URI = "git://github.com/rhinstaller/efivar.git;branch=main;protocol=https \
            file://0001-docs-do-not-build-efisecdb-manpage.patch \
+           file://0002-efivarfs-backport-patch-to-update-file-variable-store-on-SetVariableRT.patch \
            "
 SRCREV = "c47820c37ac26286559ec004de07d48d05f3308c"