Bug#1143444: trixie-pu: package efivar/38-3.1+deb13u1
Christopher Obbard <[email protected]> Sun, 02 Aug 2026 00:02:25 +0100
| Newsgroups | gmane.linux.debian.devel.release |
|---|---|
| Message-ID | <178562534517.1879187.12296125853693658882.reportbug__11895.8122025774$1785625542$gmane$org@nox> |
This is a multi-part MIME message sent by reportbug. --===============1356331198776715103== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline Package: release.debian.org Severity: normal Tags: trixie X-Debbugs-Cc: [email protected], [email protected], [email protected], [email protected] Control: affects -1 + src:efivar User: [email protected] Usertags: pu Dear Stable Release Managers, I would like to update efivar in trixie to backport a single upstream patch which enables persistent EFI variable storage on platforms that lack dedicated firmware-managed variable storage (e.g. ARM64 devices such as the Qualcomm RB3 Gen 2 Development Kit). This patch was originally proposed to trixie-backports upload (see Bug#1142580) which was accepted. In that bug, both Chris Hofstaedtler and Steve McIntyre suggested this is something which should be fixed in trixie proper rather than only in backports, so I am following up with their request. The backport (39-2~bpo13+1) has been in trixie-backports since 2026-07-22. [ Reason ] Embedded and non-x86 platforms often have no firmware-managed store for EFI variables. The vast majority of these devices (anything booting via U-Boot) instead keep their EFI variables in a file on the ESP. To make SetVariable-at-runtime work, U-Boot advertises SetVariableRT in the RTPROP table and backs it with a memory store so the kernel can read and write variables through efivarfs as normal. Those writes only land in RAM and are silently lost on the next reboot. To let the OS persist them, U-Boot also exposes two read-only variables in the b2ac5fc9-92b7-4acd-aeac-11e818c3130c namespace: RTStorageVolatile - the name of the variable store file, relative to the ESP. VarToFile - a binary dump of the variables that should be preserved. Upstream commit 68daa04654ac ("efivarfs: Update a file variable store On SetVariable RT", rhboot/efivar#267) teaches the efivarfs backend to look for those two variables and, when present, writes the variable store back out to the file on the ESP after a variable is set or deleted. This is not a regression on bookworm as the functionality has never worked there. It is missing functionality that became much more visible in trixie, which is the first Debian release to support this class of hardware in any real way. The equivalent change is already in unstable and testing via efivar 39-2; see Bug#1102494. [ Impact ] Without this update, on affected hardware every EFI variable write from userspace is silently discarded after a reboot. In practice that means: - efibootmgr changes (e.g. boot order or new boot entries) do not survive a reboot, so the system cannot be reconfigured to boot anything other than what the firmware/U-Boot environment already points at. - bootctl, grub-install, shim-signed and the d-i/installer bootloader steps all appear to succeed and then has no effect. - UEFI capsule updates (therefore fwupd firmware updates) cannot work, since they depend on SetVariable at runtime persisting. - the failures are silent; no errors are reported;, the variable reads back correctly for the rest of the boot but resets to default after a reboot. The only workaround available to users today is to manually copy the variable store to the ESP after every change, e.g. dd if=/sys/firmware/efi/efivars/VarToFile-b2ac5fc9-92b7-4acd-aeac-11e818c3130c \ of=/boot/efi/ubootefi.var skip=4 bs=1 which is not something we can expect users to so; and nothing in Debian does this automatically. If the update is not approved, trixie remains effectively unusable for boot management and firmware updates on this hardware for the lifetime of the release and the only supported answer is "install the package from trixie-backports". [ Tests ] efivar has no upstream test suite covering the efivarfs backend so testing has been manual. - The identical patch has been in unstable and testing since efivar 39-2 (uploaded 2025-08-19) and in trixie-backports since 39-2~bpo13+1 (2026-07-22). No regressions have been reported against it in that time. - Manually tested on various Qualcomm ARM64 devices running trixie: creating and reordering boot entries with efibootmgr now persists across a reboot where previously the changes were lost. - Manually tested on amd64 with normal firmware-managed variable storage to confirm there is no behaviour change: RTStorageVolatile does not exist there so the new code returns immediately and efibootmgr/fwupd behave exactly as before. - The package builds on all release architectures and the salsa-ci pipeline (including lintian, build and reproducibility jobs) passes. [ Risks ] The change is small and self-contained: it touches only src/efivarfs.c, adding 153 lines and removing 4 (the removal is purely moving an existing macro definition earlier in the file so the new code can use it). No public API, ABI or packaging changes are involved. The main mitigating factor is that all of the new code is gated on reading the RTStorageVolatile variable from the b2ac5fc9-92b7-4acd-aeac-11e818c3130c namespace. On any system that does not use a file-backed variable store that variable does not exist, get_esp_filename() returns an error and efivarfs_set_variable() and efivarfs_del_variable() behave exactly as they do today. The blast radius is therefore limited to the platforms the change is intended to fix. For completeness, the risks I am aware of: - On affected platforms, every variable set/delete now additionally rewrites the whole variable store file on the ESP. This is more I/O than before and the write is not atomic so an badly-timed power loss during a variable write could leave a truncated store file. This is also part of the upstream design and is the same behaviour already shipping in testing/unstable and trixie-backports. - The new write_file() helper calls exit(1) on some error paths rather than returning an error to the caller, which is not ideal for a library. Again, this is only reachable on platforms that have the U-Boot variables and is unchanged from the version in testing/unstable. The alternative to taking this upsate is leaving the fix in trixie-backports only; which is what prompted the discussion in #1142580. [ Checklist ] [x] *all* changes are documented in the d/changelog [x] I reviewed all changes and I approve them [x] attach debdiff against the package in (old)stable [x] the issue is verified as fixed in unstable [ Changes ] The proposed 38-3.1+deb13u1 contains exactly three things: 1. debian/patches/0003-efivarfs-Update-a-file-variable-store-On-SetVariable.patch New patch. A byte-for-byte copy of the patch shipped in unstable and testing as debian/patches/0002-efivarfs-Update-a-file-variable-store-On-SetVariable.patch in efivar 39-2, which is upstream commit 68daa04654ac. It applies to 38 unchanged. It: - defines the RTStorageVolatile name and the b2ac5fc9-92b7-4acd-aeac-11e818c3130c GUID, and a list of the usual ESP mount points (/boot/efi/, /boot/, /efi/); - adds get_esp_filename() to read the store filename out of RTStorageVolatile, and get_esp_filepath() to locate that file under one of the ESP mount points; - adds write_file(), which copies the contents of the VarToFile variable (skipping its 4-byte size prefix) over that file; - moves the existing make_efivarfs_path() macro earlier in the file so the above can use it; - calls the new efi_update_var_file() at the end of efivarfs_set_variable() and efivarfs_del_variable(). All of this is a no-op when RTStorageVolatile is absent. 2. debian/patches/series Adds the one line for the new patch. 3. debian/changelog The new 38-3.1+deb13u1 entry. There are no other changes: no build system, packaging, symbols or maintainer script changes. The full debdiff against 38-3.1 in trixie is attached. [ Other info ] The packaging change is prepared on the `trixie` branch of the efivar Salsa repository at https://salsa.debian.org/efi-team/efivar and I have discussed the intent with the maintainers in #1142580. I am happy to make any changes you would like before uploading and to follow this up for the lifetime of trixie. Some background on why I care about this: I work at Qualcomm and part of my job is making Debian better on Qualcomm hardware. We maintain downstream customised Debian image builds for Qualcomm hardware (https://github.com/qualcomm-linux/qcom-deb-images) and are working to reduce the delta against Debian and other upstream projects over the long term. This change is one of those deltas: with it, stock trixie can manage its own boot configuration on these machines. Related bugs and links: #1142580 - efivar: intent to maintain a backport of efivar for trixie-backports (where the point release was suggested) #1102494 - the underlying issue, fixed in unstable by efivar 39-2 https://github.com/rhboot/efivar/pull/267 - the upstream PR Cheers! Christopher Obbard --===============1356331198776715103== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="efivar_38-3.1+deb13u1.debdiff" diff -Nru efivar-38/debian/changelog efivar-38/debian/changelog --- efivar-38/debian/changelog 2024-02-28 02:57:40.000000000 +0000 +++ efivar-38/debian/changelog 2026-08-01 23:20:32.000000000 +0100 @@ -1,3 +1,9 @@ +efivar (38-3.1+deb13u1) trixie; urgency=medium + + * d/patches: backport efivarfs SetVariable at runtime support + + -- Christopher Obbard <[email protected]> Sat, 01 Aug 2026 23:20:32 +0100 + efivar (38-3.1) unstable; urgency=medium * Non-maintainer upload. diff -Nru efivar-38/debian/patches/0003-efivarfs-Update-a-file-variable-store-On-SetVariable.patch efivar-38/debian/patches/0003-efivarfs-Update-a-file-variable-store-On-SetVariable.patch --- efivar-38/debian/patches/0003-efivarfs-Update-a-file-variable-store-On-SetVariable.patch 1970-01-01 01:00:00.000000000 +0100 +++ efivar-38/debian/patches/0003-efivarfs-Update-a-file-variable-store-On-SetVariable.patch 2026-08-01 23:11:02.000000000 +0100 @@ -0,0 +1,240 @@ +From 68daa04654acbe1bbaa17ebfc23c371b39e69c6b Mon Sep 17 00:00:00 2001 +From: Ilias Apalodimas <[email protected]> +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. + +Signed-off-by: Ilias Apalodimas <[email protected]> +--- + src/efivarfs.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 153 insertions(+), 4 deletions(-) + +diff --git a/src/efivarfs.c b/src/efivarfs.c +index 034d6c1..2dea252 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; + +-- +2.43.0 + diff -Nru efivar-38/debian/patches/series efivar-38/debian/patches/series --- efivar-38/debian/patches/series 2023-11-29 14:23:32.000000000 +0000 +++ efivar-38/debian/patches/series 2026-08-01 23:11:02.000000000 +0100 @@ -1,2 +1,3 @@ 0001-linux-handle-non-ACPI-systems-in-device_get.patch 0002_no_host_march.patch +0003-efivarfs-Update-a-file-variable-store-On-SetVariable.patch --===============1356331198776715103==--