From 627ac54ec0357e7b029230f9c213b92867ebf354 Mon Sep 17 00:00:00 2001 From: Javier Tia Date: Mon, 17 Jun 2024 08:07:45 -0600 Subject: [PATCH] efivar: Copy VarToFile to RTStorageVolatile file at ESP EFI is becoming more common on embedded boards with the embracing of SystemReady-IR. U-Boot which is most commonly used, is usually storing the EFI variables in a file in the ESP. That makes it impossible to support SetVariable at Runtime reliably, since the OS doesn't know how to access, read or write that file. OS'es usually need SetVariable at runtime for three reasons: - Set the BootOrder - Enable UEFI Secure Boot - OSIndication to signal capsule updates on-disk Since the variables are stored in a file U-Boot enables SetVariable at runtime in the EFI config table and stores any updates in RAM. At the same file it creates two volatile variables: - RTStorageVolatile is the location of the file relative to the ESP - VarTofile contains a binary dump of the EFI variables that need to be preserved on the file (BS, RT, NV) U-Boot fills in the VarToFile dynamically on reads and that includes any updates the OS did in the meantime. So, let's update efivar to do the same thing. Once a variable is written to the efivarfs, make sure efivars is mounted as rw and scan for the file RTStorageVolatile. If we find that, copy the VarToFile contents in a file and preserve the variables across reboots Suggested-by: Ilias Apalodimas Signed-off-by: Javier Tia --- src/Makefile | 2 +- src/efivar.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 6fc2a622..16d03b42 100644 --- a/src/Makefile +++ b/src/Makefile @@ -98,7 +98,7 @@ libefivar.so : private LIBS=dl libefivar.so : private MAP=libefivar.map efivar : $(EFIVAR_OBJECTS) | libefivar.so -efivar : private LIBS=efivar dl +efivar : private LIBS=efivar dl blkid efivar-static : $(EFIVAR_OBJECTS) $(patsubst %.o,%.static.o,$(LIBEFIVAR_OBJECTS)) efivar-static : | $(GENERATED_SOURCES) diff --git a/src/efivar.c b/src/efivar.c index 519f7c52..4a8bca8b 100644 --- a/src/efivar.c +++ b/src/efivar.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include extern char *optarg; extern int optind, opterr, optopt; @@ -42,6 +44,11 @@ extern int optind, opterr, optopt; #define SHOW_VERBOSE 0 #define SHOW_DECIMAL 1 +#define EFI_VARTOFILE "b2ac5fc9-92b7-4acd-aeac-11e818c3130c-VarToFile" +#define EFI_RTSTORAGEVOLATILE "b2ac5fc9-92b7-4acd-aeac-11e818c3130c-RTStorageVolatile" + +static void save_esp_filename(); + static const char *attribute_names[] = { "Non-Volatile", "Boot Service Access", @@ -382,6 +389,8 @@ edit_variable(const char *guid_name, void *data, size_t data_size, show_errors(); exit(1); } + + save_esp_filename(); } static void @@ -424,6 +433,172 @@ prepare_data(const char *filename, uint8_t **data, size_t *data_size) exit(1); } +void +find_esp(char **esp_paths, size_t *esp_count) +{ + blkid_cache cache; + blkid_dev_iterate iter; + blkid_dev dev; + const char *devname; + const char *parttype; + const char *esp_parttype = "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"; // ESP UUID + FILE *mounts; + struct mntent *ent; + blkid_probe pr = NULL; + int r, size = 0; + *esp_count = 0; + + // Initialize the blkid cache + if (blkid_get_cache(&cache, NULL) < 0) { + fprintf(stderr, "Failed to get blkid cache\n"); + return; + } + + // Probe all block devices + blkid_probe_all(cache); + + // Create an iterator to go through all block devices + iter = blkid_dev_iterate_begin(cache); + if (!iter) { + fprintf(stderr, "Failed to create blkid iterator\n"); + blkid_put_cache(cache); + return; + } + + // Open /proc/mounts to find mount points + mounts = setmntent("/proc/mounts", "r"); + if (!mounts) { + fprintf(stderr, "Failed to open /proc/mounts\n"); + endmntent(mounts); + return; + } + + while (blkid_dev_next(iter, &dev) == 0) { + dev = blkid_verify(cache, dev); + if (!dev) { + continue; + } + devname = blkid_dev_devname(dev); + + pr = blkid_new_probe_from_filename(devname); + if (!pr) { + fprintf(stderr, "Failed to create blkid probe\n"); + blkid_free_probe(pr); + return; + } + + // Generate partition details + blkid_probe_enable_partitions(pr, 1); + blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS); + + r = blkid_do_safeprobe(pr); + if (r == -2) { + debug("File system %s is ambiguous\n", devname); + continue; + } else if (r != 0) { + debug("Failed to probe file system %s\n", devname); + continue; + } + + // Get the PARTTYPE of the current device + r = blkid_probe_lookup_value(pr, "PART_ENTRY_TYPE", &parttype, NULL); + if (r != 0) { + debug("Failed to probe partition type UUID %s\n", devname); + continue; + } + + if (parttype && strcmp(parttype, esp_parttype) == 0) { + // Iterate over mount points to find ESP matches + while ((ent = getmntent(mounts)) != NULL) { + if (strcmp(ent->mnt_fsname, devname) == 0) { + printf("ESP %s path is mounted at dev %s\n", ent->mnt_dir, devname); + size += strlen(ent->mnt_dir) + 1; // +1 for the null terminator + char *temp = realloc(*esp_paths, size); + if (temp == NULL) { + fprintf(stderr, "Failed to allocate memory for ESP paths\n"); + free(*esp_paths); + *esp_count = 0; + return; + } + *esp_paths = temp; + strcpy(*esp_paths + size - strlen(ent->mnt_dir) - 1, ent->mnt_dir); + (*esp_count)++; + } + } + } + + // Cleanup + blkid_free_probe(pr); + } + + // Cleanup + endmntent(mounts); + blkid_dev_iterate_end(iter); + blkid_put_cache(cache); +} + +static void +get_esp_filename(char **esp_filename) +{ + char *dst_name = NULL; + efi_guid_t dst_guid = efi_guid_empty; + size_t filename_size; + uint32_t filename_attr; + uint8_t *filename = NULL; + int rc; + + parse_name(EFI_RTSTORAGEVOLATILE, &dst_name, &dst_guid); + if (!dst_name || efi_guid_is_empty(&dst_guid)) { + fprintf(stderr, "efivar: could not parse %s\n", EFI_RTSTORAGEVOLATILE); + show_errors(); + exit(1); + } + + rc = efi_get_variable(dst_guid, dst_name, &filename, &filename_size, &filename_attr); + if (rc < 0) { + fprintf(stderr, "efivar: get ESP filename\n"); + show_errors(); + exit(1); + } + + *esp_filename = (char *)filename; +} + +static void +save_esp_filename() +{ + char *esp_paths = NULL; + size_t esp_count = 0; + char *ptr; + char *esp_filename_path = NULL; + char *esp_filename = NULL; + + get_esp_filename(&esp_filename); + if (esp_filename) { + fprintf(stderr, "efivar: No ESP filename found\n"); + show_errors(); + exit(1); + } + + find_esp(&esp_paths, &esp_count); + if (esp_paths) { + ptr = esp_paths; + for (size_t i = 0; i < esp_count; i++) { + esp_filename_path = strcat(ptr, "/"); + esp_filename_path = strcat(esp_filename_path, esp_filename); + save_variable(EFI_VARTOFILE, esp_filename_path, false); + printf("efivar: VarToFile content saved at %s\n", esp_filename_path); + ptr += strlen(ptr) + 1; + } + free(esp_paths); + } + else { + fprintf(stderr, "efivar: no EFI System Partitions (ESP) found\n"); + show_errors(); + exit(1); + } +} + static void __attribute__((__noreturn__)) usage(int ret) {