Skip to content

Commit

Permalink
efivar: Copy VarToFile to RTStorageVolatile file at ESP
Browse files Browse the repository at this point in the history
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 <ilias.apalodimas@linaro.org>
Signed-off-by: Javier Tia <javier.tia@linaro.org>
  • Loading branch information
jetm committed Jun 17, 2024
1 parent 2491825 commit 627ac54
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
175 changes: 175 additions & 0 deletions src/efivar.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <blkid/blkid.h>
#include <mntent.h>

extern char *optarg;
extern int optind, opterr, optopt;
Expand All @@ -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",
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand Down

0 comments on commit 627ac54

Please sign in to comment.