[PATCH 2/2] uefi: add in support for new uefivar file system interface
Keng-Yu Lin
kengyu at canonical.com
Tue Sep 11 06:14:02 UTC 2012
On Thu, Sep 6, 2012 at 2:28 AM, Colin King <colin.king at canonical.com> wrote:
> From: Colin Ian King <colin.king at canonical.com>
>
> Currently we read uefi variables via a /sys interface, however a
> new kernel feature will export these variables via a psuedo file
> system, so add support for this.
>
> This is rather a large change. We abstract out the reading of
> uefi variables using two new helper functions:
>
> fwts_uefi_get_variable_names() and
> fwts_uefi_free_variable_names().
>
> We modify the abstraction of the UEFI variables, before we used
> to have a 1-to-1 mapping onto sysfs, now we have to cater for large
> sized variable contents, so we now make the data size and variable
> name size allocated on the heap. We also add in support for the
> new efifs and abstract this and the /sys variable reading into
> two helper functions.
>
> Finally, this patch modifies the uefidump test to use these
> changes in the uefi API.
> ---
> src/lib/include/fwts_uefi.h | 12 +-
> src/lib/src/fwts_uefi.c | 339 ++++++++++++++++++++++++++++++++++++++----
> src/uefi/uefidump/uefidump.c | 31 ++--
> 3 files changed, 333 insertions(+), 49 deletions(-)
>
> diff --git a/src/lib/include/fwts_uefi.h b/src/lib/include/fwts_uefi.h
> index 763702e..f45027d 100644
> --- a/src/lib/include/fwts_uefi.h
> +++ b/src/lib/include/fwts_uefi.h
> @@ -23,13 +23,13 @@
> #define FWTS_UEFI_LOAD_ACTIVE 0x00000001
>
> typedef struct {
> - uint16_t varname[512];
> + uint16_t *varname;
> uint8_t guid[16];
> - uint64_t datalen;
> - uint8_t data[1024];
> + size_t datalen;
> + uint8_t *data;
> uint64_t status;
> uint32_t attributes;
> -} __attribute__((packed)) fwts_uefi_var;
> +} fwts_uefi_var;
>
> typedef uint8_t fwts_uefi_mac_addr[32];
> typedef uint8_t fwts_uefi_ipv4_addr[4];
> @@ -302,6 +302,8 @@ void fwts_uefi_str16_to_str(char *dst, const size_t len, const uint16_t *src);
> size_t fwts_uefi_str16len(const uint16_t *str);
> void fwts_uefi_get_varname(char *varname, const size_t len, const fwts_uefi_var *var);
> int fwts_uefi_get_variable(const char *varname, fwts_uefi_var *var);
> -int fwts_uefi_set_variable(const char *varname, fwts_uefi_var *var);
> +void fwts_uefi_free_variable(fwts_uefi_var *var);
> +void fwts_uefi_free_variable_names(fwts_list *list);
> +int fwts_uefi_get_variable_names(fwts_list *list);
>
> #endif
> diff --git a/src/lib/src/fwts_uefi.c b/src/lib/src/fwts_uefi.c
> index d4f68f4..c4b2019 100644
> --- a/src/lib/src/fwts_uefi.c
> +++ b/src/lib/src/fwts_uefi.c
> @@ -21,15 +21,129 @@
> #include <stdio.h>
> #include <sys/types.h>
> #include <sys/stat.h>
> +#include <sys/vfs.h>
> #include <fcntl.h>
> #include <unistd.h>
> +#include <dirent.h>
>
> #include "fwts.h"
> #include "fwts_uefi.h"
>
> -static inline void fwts_uefi_set_filename(char *filename, const int len, const char *varname)
> +/* Old sysfs uefi packed binary blob variables */
> +typedef struct {
> + uint16_t varname[512];
> + uint8_t guid[16];
> + uint64_t datalen;
> + uint8_t data[1024];
> + uint64_t status;
> + uint32_t attributes;
> +} __attribute__((packed)) fwts_uefi_sys_fs_var;
> +
> +/* New efifs variable */
> +typedef struct {
> + uint32_t attributes;
> + uint8_t data[0]; /* variable length, depends on file size */
> +} __attribute__((packed)) fwts_uefi_efivars_fs_var;
> +
> +/* Which interface are we using? */
> +#define UEFI_IFACE_UNKNOWN (0) /* Not yet known */
> +#define UEFI_IFACE_NONE (1) /* None found */
> +#define UEFI_IFACE_SYSFS (2) /* sysfs */
> +#define UEFI_IFACE_EFIVARS (3) /* efivar fs */
> +
> +/* File system magic numbers */
> +#define EFIVARS_FS_MAGIC 0x6165676C
> +#define SYS_FS_MAGIC 0x62656572
> +
> +/*
> + * fwts_uefi_get_interface()
> + * find which type of EFI variable file system we are using,
> + * sets path to the name of the file system path, returns
> + * the file system interface (if found).
> + */
> +static int fwts_uefi_get_interface(char **path)
> +{
> + static int efivars_interface = UEFI_IFACE_UNKNOWN;
> + static char efivar_path[4096];
> +
> + FILE *fp;
> + char fstype[1024];
> + struct statfs statbuf;
> +
> + if (path == NULL) /* Sanity check */
> + return FWTS_ERROR;
> +
> + /* Already discovered, return the cached values */
> + if (efivars_interface != UEFI_IFACE_UNKNOWN) {
> + *path = efivar_path;
> + return efivars_interface;
> + }
> +
> + *efivar_path = '\0'; /* Assume none */
> +
> + /* Scan mounts to see if sysfs or efivar fs is somewhere else */
> + if ((fp = fopen("/proc/mounts", "r")) != NULL) {
> + while (!feof(fp)) {
> + char mount[4096];
> +
> + if (fscanf(fp, "%*s %4095s %1023s", mount, fstype) == 2) {
> + /* Always try to find the newer interface first */
> + if (!strcmp(fstype, "efivars")) {
> + strcpy(efivar_path, mount);
> + break;
> + }
> + /* Failing that, look for sysfs, but don't break out
> + the loop as we need to keep on searching for efivar fs */
> + if (!strcmp(fstype, "sysfs"))
> + strcpy(efivar_path, "/sys/firmware/efi/vars");
> + }
> + }
> + }
> + fclose(fp);
> +
> + *path = NULL;
> +
> + /* No mounted path found, bail out */
> + if (*efivar_path == '\0') {
> + efivars_interface = UEFI_IFACE_NONE;
> + return UEFI_IFACE_NONE;
> + }
> +
> + /* Can't stat it, bail out */
> + if (statfs(efivar_path, &statbuf) < 0) {
> + efivars_interface = UEFI_IFACE_NONE;
> + return UEFI_IFACE_NONE;
> + };
> +
> + /* We've now found a valid file system we can use */
> + *path = efivar_path;
> +
> + if (statbuf.f_type == EFIVARS_FS_MAGIC) {
> + efivars_interface = UEFI_IFACE_EFIVARS;
> + return UEFI_IFACE_EFIVARS;
> + }
> +
> + if (statbuf.f_type == SYS_FS_MAGIC) {
> + efivars_interface = UEFI_IFACE_SYSFS;
> + return UEFI_IFACE_EFIVARS;
> + }
> +
> + return UEFI_IFACE_UNKNOWN;
> +}
> +
> +/*
> + * fwts_uefi_str_to_str16()
> + * convert 8 bit C string to 16 bit sring.
> + */
> +void fwts_uefi_str_to_str16(uint16_t *dst, const size_t len, const char *src)
> {
> - snprintf(filename, len, "/sys/firmware/efi/vars/%s/raw_var", varname);
> + size_t i = len;
> +
> + while ((*src) && (i > 1)) {
> + *dst++ = *src++;
> + i--;
> + }
> + *dst = '\0';
> }
>
> /*
> @@ -70,57 +184,230 @@ void fwts_uefi_get_varname(char *varname, const size_t len, const fwts_uefi_var
> }
>
> /*
> - * fwts_uefi_get_variable()
> - * fetch a UEFI variable given its name.
> + * fwts_uefi_get_variable_sys_fs()
> + * fetch a UEFI variable given its name, via sysfs
> */
> -int fwts_uefi_get_variable(const char *varname, fwts_uefi_var *var)
> +static int fwts_uefi_get_variable_sys_fs(const char *varname, fwts_uefi_var *var, char *path)
> {
> int fd;
> - int n;
> - int ret = FWTS_OK;
> char filename[PATH_MAX];
> + fwts_uefi_sys_fs_var uefi_sys_fs_var;
>
> - if ((!varname) || (!var))
> - return FWTS_ERROR;
> + memset(var, 0, sizeof(fwts_uefi_var));
>
> - fwts_uefi_set_filename(filename, sizeof(filename), varname);
> + snprintf(filename, sizeof(filename), "%s/%s/raw_var", path, varname);
>
> if ((fd = open(filename, O_RDONLY)) < 0)
> return FWTS_ERROR;
>
> - memset(var, 0, sizeof(fwts_uefi_var));
> -
> - if ((n = read(fd, var, sizeof(fwts_uefi_var))) != sizeof(fwts_uefi_var))
> - ret = FWTS_ERROR;
> + memset(&uefi_sys_fs_var, 0, sizeof(uefi_sys_fs_var));
>
> + /* Read the raw fixed sized data */
> + if (read(fd, &uefi_sys_fs_var, sizeof(uefi_sys_fs_var)) != sizeof(uefi_sys_fs_var)) {
> + close(fd);
> + return FWTS_ERROR;
> + }
> close(fd);
>
> - return ret;
> + /* Sanity check datalen is OK */
> + if (uefi_sys_fs_var.datalen > sizeof(uefi_sys_fs_var.data))
> + return FWTS_ERROR;
> +
> + /* Allocate space for the variable name */
> + var->varname = calloc(1, sizeof(uefi_sys_fs_var.varname));
> + if (var->varname == NULL)
> + return FWTS_ERROR;
> +
> + /* Allocate space for the data */
> + var->data = calloc(1, (size_t)uefi_sys_fs_var.datalen);
> + if (var->data == NULL) {
> + free(var->varname);
> + return FWTS_ERROR;
> + }
> +
> + /* And copy the data */
> + memcpy(var->varname, uefi_sys_fs_var.varname, sizeof(uefi_sys_fs_var.varname));
> + memcpy(var->data, uefi_sys_fs_var.data, uefi_sys_fs_var.datalen);
> + memcpy(var->guid, uefi_sys_fs_var.guid, sizeof(var->guid));
> + var->datalen = (size_t)uefi_sys_fs_var.datalen;
> + var->attributes = uefi_sys_fs_var.attributes;
> + var->status = uefi_sys_fs_var.status;
> +
> + return FWTS_OK;
> }
>
> /*
> - * fwts_uefi_set_variable()
> - * write back a UEFI variable given its name and contents in var.
> + * fwts_uefi_get_variable_efivars_fs()
> + * fetch a UEFI variable given its name, via efivars fs
> */
> -int fwts_uefi_set_variable(const char *varname, fwts_uefi_var *var)
> +static int fwts_uefi_get_variable_efivars_fs(const char *varname, fwts_uefi_var *var, char *path)
> {
> int fd;
> - ssize_t n;
> - int ret = FWTS_OK;
> char filename[PATH_MAX];
> + struct stat statbuf;
> + fwts_uefi_efivars_fs_var *efivars_fs_var;
> + size_t varname_len = strlen(varname);
> +
> + memset(var, 0, sizeof(fwts_uefi_var));
>
> - if ((!varname) || (!var))
> + /* Variable names include the GUID, so must be at least 36 chars long */
> +
> + if (varname_len < 36)
> return FWTS_ERROR;
>
> - fwts_uefi_set_filename(filename, sizeof(filename), varname);
> + /* Get the GUID */
> + fwts_guid_str_to_buf(varname + varname_len - 36, var->guid, sizeof(var->guid));
> +
> + snprintf(filename, sizeof(filename), "%s/%s", path, varname);
>
> - if ((fd = open(filename, O_WRONLY)) < 0)
> + if (stat(filename, &statbuf) < 0)
> return FWTS_ERROR;
>
> - if ((n = write(fd, var, sizeof(fwts_uefi_var))) != sizeof(fwts_uefi_var))
> - ret = FWTS_ERROR;
> + /* Variable name, less the GUID, in 16 bit ints */
> + var->varname = calloc(1, (varname_len + 1 - 36) * sizeof(uint16_t));
> + if (var->varname == NULL)
> + return FWTS_ERROR;
>
> + /* Convert name to internal 16 bit version */
> + fwts_uefi_str_to_str16(var->varname, varname_len - 36, varname);
> +
> + /* Need to read the data in one read, so allocate a buffer big enough */
> + if ((efivars_fs_var = calloc(1, statbuf.st_size)) == NULL) {
> + free(var->varname);
> + return FWTS_ERROR;
> + }
> +
> + if ((fd = open(filename, O_RDONLY)) < 0) {
> + free(var->varname);
> + free(efivars_fs_var);
> + return FWTS_ERROR;
> + }
> +
> + if (read(fd, efivars_fs_var, statbuf.st_size) != statbuf.st_size) {
> + free(var->varname);
> + free(efivars_fs_var);
> + close(fd);
> + return FWTS_ERROR;
> + }
> close(fd);
>
> - return ret;
> + var->status = 0;
> +
> + /*
> + * UEFI variable data in efifs is:
> + * 4 bytes : attributes
> + * N bytes : uefi variable contents
> + */
> + var->attributes = efivars_fs_var->attributes;
> +
> + var->datalen = statbuf.st_size - 4;
> + if ((var->data = calloc(1, var->datalen)) == NULL) {
> + free(var->varname);
> + free(efivars_fs_var);
> + return FWTS_ERROR;
> + }
> + memcpy(var->data, efivars_fs_var->data, var->datalen);
> +
> + free(efivars_fs_var);
> +
> + return FWTS_OK;
> }
> +
> +/*
> + * fwts_uefi_get_variable()
> + * fetch a UEFI variable given its name.
> + */
> +int fwts_uefi_get_variable(const char *varname, fwts_uefi_var *var)
> +{
> + char *path;
> +
> + if ((!varname) || (!var)) /* Sanity checks */
> + return FWTS_ERROR;
> +
> + switch (fwts_uefi_get_interface(&path)) {
> + case UEFI_IFACE_SYSFS:
> + return fwts_uefi_get_variable_sys_fs(varname, var, path);
> + case UEFI_IFACE_EFIVARS:
> + return fwts_uefi_get_variable_efivars_fs(varname, var, path);
> + default:
> + return FWTS_ERROR;
> + }
> +}
> +
> +/*
> + * fwts_uefi_free_variable()
> + * free data and varname associated with a UEFI variable as
> + * fetched by fwts_uefi_get_variable.
> + */
> +void fwts_uefi_free_variable(fwts_uefi_var *var)
> +{
> + free(var->data);
> + free(var->varname);
> +}
> +
> +static int fwts_uefi_true_filter(const struct dirent *d)
> +{
> + return 1;
> +}
> +
> +/*
> + * fwts_uefi_free_variable_names
> + * free the list of uefi variable names
> + */
> +void fwts_uefi_free_variable_names(fwts_list *list)
> +{
> + fwts_list_free_items(list, free);
> +}
> +
> +/*
> + * fwts_uefi_get_variable_names
> + * gather a list of all the uefi variable names
> + */
> +int fwts_uefi_get_variable_names(fwts_list *list)
> +{
> + int i, n;
> + struct dirent **names = NULL;
> + char *path;
> + char *name;
> + int ret = FWTS_OK;
> +
> + fwts_list_init(list);
> +
> + switch (fwts_uefi_get_interface(&path)) {
> + case UEFI_IFACE_SYSFS:
> + case UEFI_IFACE_EFIVARS:
> + break;
> + default:
> + return FWTS_ERROR;
> + }
> +
> + /* Gather variable names in alphabetical order */
> + n = scandir(path, &names, fwts_uefi_true_filter, alphasort);
> + if (n < 0)
> + return FWTS_ERROR;
> +
> + for (i = 0; i < n; i++) {
> + if (names[i]->d_name == NULL)
> + continue;
> + if (!strcmp(names[i]->d_name, "."))
> + continue;
> + if (!strcmp(names[i]->d_name, ".."))
> + continue;
> +
> + name = strdup(names[i]->d_name);
> + if (name == NULL) {
> + ret = FWTS_ERROR;
> + fwts_uefi_free_variable_names(list);
> + break;
> + } else
> + fwts_list_append(list, name);
> + }
> +
> + /* Free dirent names */
> + for (i = 0; i < n; i++)
> + free(names[i]);
> + free(names);
> +
> + return ret;
> +}
> +
> diff --git a/src/uefi/uefidump/uefidump.c b/src/uefi/uefidump/uefidump.c
> index 1f71107..4b0842f 100644
> --- a/src/uefi/uefidump/uefidump.c
> +++ b/src/uefi/uefidump/uefidump.c
> @@ -17,8 +17,6 @@
> *
> */
>
> -#include <dirent.h>
> -
> #include "fwts.h"
> #include "fwts_uefi.h"
>
> @@ -466,6 +464,7 @@ static void uefidump_info_bootorder(fwts_framework *fw, fwts_uefi_var *var)
> *data++, i < (n - 1) ? "," : "");
> }
> fwts_log_info_verbatum(fw, " Boot Order: %s.", str);
> + free(str);
> }
>
> static void uefidump_info_bootdev(fwts_framework *fw, fwts_uefi_var *var)
> @@ -510,11 +509,6 @@ static uefidump_info uefidump_info_table[] = {
> { NULL, NULL }
> };
>
> -static int uefidump_true_filter(const struct dirent *d)
> -{
> - return 1;
> -}
> -
> /*
> * uefidump_attribute()
> * convert attribute into a human readable form
> @@ -568,7 +562,7 @@ static void uefidump_var(fwts_framework *fw, fwts_uefi_var *var)
>
> /* otherwise just do a plain old hex dump */
> fwts_log_info_verbatum(fw, " Size: %d bytes of data.", (int)var->datalen);
> - data = (uint8_t*)&var->data;
> + data = (uint8_t*)var->data;
>
> for (i=0; i<(int)var->datalen; i+= 16) {
> char buffer[128];
> @@ -591,25 +585,26 @@ static int uefidump_init(fwts_framework *fw)
>
> static int uefidump_test1(fwts_framework *fw)
> {
> - int n;
> - int i;
> - struct dirent **names = NULL;
> + fwts_list name_list;
>
> - n = scandir("/sys/firmware/efi/vars", &names, uefidump_true_filter, alphasort);
> - if (n <= 0) {
> + if (fwts_uefi_get_variable_names(&name_list) == FWTS_ERROR) {
> fwts_log_info(fw, "Cannot find any UEFI variables.");
> } else {
> - for (i=0; i<n; i++) {
> + fwts_list_link *item;
> +
> + fwts_list_foreach(item, &name_list) {
> fwts_uefi_var var;
> - if ((names[i] != NULL) &&
> - (fwts_uefi_get_variable(names[i]->d_name, &var) == FWTS_OK)) {
> + char *name = fwts_list_data(char *, item);
> +
> + if (fwts_uefi_get_variable(name, &var) == FWTS_OK) {
> uefidump_var(fw, &var);
> + fwts_uefi_free_variable(&var);
> fwts_log_nl(fw);
> }
> - free(names[i]);
> }
> }
> - free(names);
> +
> + fwts_uefi_free_variable_names(&name_list);
>
> return FWTS_OK;
> }
> --
> 1.7.10.4
>
Acked-by: Keng-Yu Lin <kengyu at canonical.com>
More information about the fwts-devel
mailing list