Translate

Archives

Efivars and Efivarfs

Recently a new filesystem was added to the 3.8 Linux kernel and backported to the Fedora kernel-3.6.10-4.fc18 and others. Here is the scant documentation for it:

efivarfs - a (U)EFI variable filesystem

The efivarfs filesystem was created to address the shortcomings of
using entries in sysfs to maintain EFI variables. The old sysfs EFI
variables code only supported variables of up to 1024 bytes. This
limitation existed in version 0.99 of the EFI specification, but was
removed before any full releases. Since variables can now be larger
than a single page, sysfs isn't the best interface for this.

Variables can be created, deleted and modified with the efivarfs
filesystem.

efivarfs is typically mounted like this,

	mount -t efivarfs none /sys/firmware/efi/efivars


Currently this filesystem is not automatically mounted on my version of Fedora 18 Beta but probably should be as the appropriate entry should have been added to systemd’s mount-setup.c by now. For the moment, just manually mount it if you wish to use this filesystem.

Here is a simple C program which demonstrates how to use efivarfs to write out an EFI variable to NVRAM, or read or delete an EFI variable from NVRAM.

//
//   Copyright (c) 2012  Finnbarr P. Murphy.   All rights reserved.
//
//   Demo the use of efivarfs to read, write and delete EFI NVRAM variables.
//
//   Parts of the code taken from mokutil and EDK1.
//
//   Writes 12345 to an EFI variable called FPMURPHY with a GUID of EFI_FPMURPHY_GUID
//   Alternatively read or delete the variable.
//  


#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>

#define BITS_PER_LONG (sizeof(unsigned long) * 8)
#define EFI_ERROR(x) ((x) | (1L << (BITS_PER_LONG - 1)))

#define EFI_SUCCESS		0
#define EFI_INVALID_PARAMETER   EFI_ERROR(2)
#define EFI_UNSUPPORTED		EFI_ERROR(3)
#define EFI_BAD_BUFFER_SIZE     EFI_ERROR(4)
#define EFI_BUFFER_TOO_SMALL	EFI_ERROR(5)
#define EFI_NOT_FOUND           EFI_ERROR(14)
#define EFI_OUT_OF_RESOURCES    EFI_ERROR(15)

#define EFI_VARIABLE_NON_VOLATILE       0x0000000000000001
#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002
#define EFI_VARIABLE_RUNTIME_ACCESS     0x0000000000000004

typedef struct {
    uint32_t  data1;
    uint16_t  data2;
    uint16_t  data3;
    uint8_t   data4[8];
} efi_guid_t;

typedef unsigned long efi_status_t;

typedef struct _efi_variable_t {
    const char    *VariableName;
    efi_guid_t    VendorGuid;
    uint32_t      DataSize;
    uint8_t       *Data;
    uint32_t      Attributes;
} __attribute__((packed)) efi_variable_t;


#define SYSFS_EFI_VARS "/sys/firmware/efi/efivars"

#define EFI_FPMURPHY_GUID \
    (efi_guid_t) {0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xe8, 0x15, 0xcd, 0x8e, 0x26}}


int
variable_to_name(efi_variable_t *var, char *name)
{
    char *p = name;
    efi_guid_t *guid = &(var->VendorGuid);

    if (!var->VariableName)
        return -1;

    strcpy (p, var->VariableName);
    p += strlen (p);
    p += sprintf (p, "-");

    sprintf(p, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
        guid->data1, guid->data2, guid->data3,
        guid->data4[0], guid->data4[1], guid->data4[2], guid->data4[3],
        guid->data4[4], guid->data4[5], guid->data4[6], guid->data4[7]
    );

    return strlen (name);
}


efi_status_t
read_variable (efi_variable_t *var)
{
    char name[PATH_MAX];
    char filename[PATH_MAX];
    int fd;
    struct stat buf;
    size_t readsize, datasize;
    void *buffer;

    if (!var)
        return EFI_INVALID_PARAMETER;

    variable_to_name (var, name);
    snprintf (filename, PATH_MAX-1, "%s/%s", SYSFS_EFI_VARS, name);
    fd = open (filename, O_RDONLY);
    if (fd == -1) {
        return EFI_NOT_FOUND;
    }

    if (fstat (fd, &buf) != 0) {
        close (fd);
        return EFI_INVALID_PARAMETER;
    }

    readsize = read (fd, &var->Attributes, sizeof(uint32_t));
    if (readsize != sizeof(uint32_t)) {
        close (fd);
        return EFI_INVALID_PARAMETER;
    }

    datasize = buf.st_size - sizeof(uint32_t);

    buffer = malloc (datasize);
    if (buffer == NULL) {
        close (fd);
        return EFI_OUT_OF_RESOURCES;
    }

    readsize = read (fd, buffer, datasize);
    if (readsize != datasize) {
        close (fd);
        free (buffer);
        return EFI_INVALID_PARAMETER;
    }
    var->Data = buffer;
    var->DataSize = datasize;

    close (fd);
    return EFI_SUCCESS;
}


efi_status_t
write_variable (efi_variable_t *var)
{
    int fd;
    size_t writesize;
    void *buffer;
    unsigned long total;
    char name[PATH_MAX];
    char filename[PATH_MAX];

    if (!var)
        return EFI_INVALID_PARAMETER;

    variable_to_name(var, name);
    snprintf(filename, PATH_MAX-1, "%s/%s", SYSFS_EFI_VARS, name);
    if (!filename )
        return EFI_INVALID_PARAMETER;

    buffer = malloc(var->DataSize + sizeof(uint32_t));
    if (buffer == NULL) {
        return EFI_OUT_OF_RESOURCES;
    }

    memcpy (buffer, &var->Attributes, sizeof(uint32_t));
    memcpy (buffer + sizeof(uint32_t), var->Data, var->DataSize);
    total = var->DataSize + sizeof(uint32_t);

    fd = open (filename, O_WRONLY | O_CREAT, 
                         S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    if (fd == -1) {
        free (buffer);
        return EFI_INVALID_PARAMETER;
    }

    writesize = write (fd, buffer, total);
    if (writesize != total) {
        free (buffer);
        close (fd);
        return EFI_INVALID_PARAMETER;
    }

    close (fd);
    free (buffer);
    return EFI_SUCCESS;
}


efi_status_t
delete_variable(efi_variable_t *var)
{
    char name[PATH_MAX];
    char filename[PATH_MAX];

    if (!var)
        return EFI_INVALID_PARAMETER;

    variable_to_name(var, name);
    snprintf(filename, PATH_MAX-1, "%s/%s", SYSFS_EFI_VARS, name);

    if (unlink (filename) == 0)
        return EFI_SUCCESS;

    return EFI_OUT_OF_RESOURCES;
}


int
usage(char *name)
{
    fprintf(stderr, "usage: %s [ -w | --write | -r | --read | -d | --delete]\n", name);
    exit(1);
}


int
main(int argc, char *argv[])
{
    efi_variable_t var;
    uint8_t auth[5];
    int status = EFI_SUCCESS;

    auth[0] = 0;
    auth[1] = 1;
    auth[2] = 2;
    auth[3] = 3;
    auth[4] = 4;

    if (argc == 2) { 
        memset (&var, 0, sizeof(var));
        var.VariableName = "FPMURPHY";
        var.VendorGuid = EFI_FPMURPHY_GUID;
        if (strcmp (argv[1], "-h") == 0 ||
            strcmp (argv[1], "--help") == 0) {
            usage(argv[0]);
        } else if (strcmp (argv[1], "-w") == 0 ||
                   strcmp (argv[1], "--write") == 0) {
            var.Data = auth;
            var.DataSize = 5;
            var.Attributes = EFI_VARIABLE_NON_VOLATILE | 
                             EFI_VARIABLE_BOOTSERVICE_ACCESS | 
                             EFI_VARIABLE_RUNTIME_ACCESS;
            if (write_variable (&var) != EFI_SUCCESS) {
                fprintf (stderr, "Failed to write variable: %s\n", var.VariableName);
                status = 1;
            }
        } else if (strcmp (argv[1], "-r") == 0 ||
                   strcmp (argv[1], "--read") == 0) {
            if (read_variable (&var) != EFI_SUCCESS) {
                fprintf (stderr, "Failed to read variable: %s\n", var.VariableName);
                status = 2;
            }
        } else if (strcmp (argv[1], "-d") == 0 ||
                   strcmp (argv[1], "--delete") == 0) {
            if (delete_variable (&var) != EFI_SUCCESS) {
                fprintf (stderr, "Failed to delete variable: %s\n", var.VariableName);
                status = 3;
            }
        } else
           usage(argv[0]);
    } else
       usage(argv[0]);

    exit(status);
}


You need to be root to run this program.

Here is what the EFI dmpstore utility displays for the FPMURPHY variable:

Variable NV+RT+BS '605DAB50-E046-4300-ABB6-3DE815CD8E26:FPMURPHY' DataSize = 5
  00000000: 00 01 02 03 04                                   *.....*


Prior to efivarfs, the “approved” way to access EFI boot variables was by means of the older efivars mechanism created by Matt Domsch starting in 2001. See …/drivers/firmware/efivars.c. This kernel module displays EFI variables in separate directories under /sys/firmware/efi/vars.

Here is the documentation on that module:

		This directory exposes interfaces for interactive with
		EFI variables.  For more information on EFI variables,
		see 'Variable Services' in the UEFI specification
		(section 7.2 in specification version 2.3 Errata D).

		In summary, EFI variables are named, and are classified
		into separate namespaces through the use of a vendor
		GUID.  They also have an arbitrary binary value
		associated with them.

		The efivars module enumerates these variables and
		creates a separate directory for each one found.  Each
		directory has a name of the form "-"
		and contains the following files:

		attributes:	A read-only text file enumerating the
				EFI variable flags.  Potential values
				include:

				EFI_VARIABLE_NON_VOLATILE
				EFI_VARIABLE_BOOTSERVICE_ACCESS
				EFI_VARIABLE_RUNTIME_ACCESS
				EFI_VARIABLE_HARDWARE_ERROR_RECORD
				EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS

				See the EFI documentation for an
				explanation of each of these variables.

		data:		A read-only binary file that can be read
				to attain the value of the EFI variable

		guid:		The vendor GUID of the variable.  This
				should always match the GUID in the
				variable's name.

		raw_var:	A binary file that can be read to obtain
				a structure that contains everything
				there is to know about the variable.
				For structure definition see "struct
				efi_variable" in the kernel sources.

				This file can also be written to in
				order to update the value of a variable.
				For this to work however, all fields of
				the "struct efi_variable" passed must
				match byte for byte with the structure
				read out of the file, save for the value
				portion.

				**Note** the efi_variable structure
				read/written with this file contains a
				'long' type that may change widths
				depending on your underlying
				architecture.

		size:		As ASCII representation of the size of
				the variable's value.


		In addition, two other magic binary files are provided
		in the top-level directory and are used for adding and
		removing variables:

		new_var:	Takes a "struct efi_variable" and
				instructs the EFI firmware to create a
				new variable.

		del_var:	Takes a "struct efi_variable" and
				instructs the EFI firmware to remove any
				variable that has a matching vendor GUID
				and variable key name.


Here is sample output from /sys/firmware/efi/vars/:

$ ls -F
Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8c/
BootCurrent-8be4df61-93ca-11d2-aa0d-00e098032b8c/
BootOptionSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c/
BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c/
ConIn-8be4df61-93ca-11d2-aa0d-00e098032b8c/
ConInDev-8be4df61-93ca-11d2-aa0d-00e098032b8c/
ConOut-8be4df61-93ca-11d2-aa0d-00e098032b8c/
ConOutDev-8be4df61-93ca-11d2-aa0d-00e098032b8c/
ErrOutDev-8be4df61-93ca-11d2-aa0d-00e098032b8c/
Lang-8be4df61-93ca-11d2-aa0d-00e098032b8c/
LangCodes-8be4df61-93ca-11d2-aa0d-00e098032b8c/
MTC-eb704011-1402-11d3-8e77-00a0c969723b/
MemoryTypeInformation-4c19049f-4137-4dd3-9c10-8b97a83ffdfa/
PlatformLang-8be4df61-93ca-11d2-aa0d-00e098032b8c/
PlatformLangCodes-8be4df61-93ca-11d2-aa0d-00e098032b8c/
RTC-378d7b65-8da9-4773-b6e4-a47826a833e1/
del_var
new_var


Note the del_var, new_var and raw_var files! These are special and are binary files.

$ ls -l *_var
--w------- 1 root root 0 Dec 25 18:39 del_var
--w------- 1 root root 0 Dec 25 18:16 new_var
$ cd ConIn-8be4df61-93ca-11d2-aa0d-00e098032b8c/
$ ls -l
-r--------  1 root root 4096 Dec 26 15:00 attributes
-r--------  1 root root 4096 Dec 26 15:00 data
-r--------  1 root root 4096 Dec 26 15:00 guid
-rw-------  1 root root 4096 Dec 26 15:00 raw_var
-r--------  1 root root 4096 Dec 26 15:00 size


Here is a simple C program which sets the globally defined UEFI boot manager Timeout variable, or reads or deletes the variable, using the efivars mechanism.

//
//   Copyright (c) 2012  Finnbarr P. Murphy.   All rights reserved.
//
//   Demo the use of sysfs to read, write and delete UEFI Timeout variable.
//
//   Parts of the code taken from efibootmgr and EDK1.
//


#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>

#define BITS_PER_LONG (sizeof(unsigned long) * 8)
#define EFI_ERROR(x) ((x) | (1L << (BITS_PER_LONG - 1)))

#define EFI_SUCCESS        0
#define EFI_INVALID_PARAMETER   EFI_ERROR(2)
#define EFI_UNSUPPORTED        EFI_ERROR(3)
#define EFI_BAD_BUFFER_SIZE     EFI_ERROR(4)
#define EFI_BUFFER_TOO_SMALL    EFI_ERROR(5)
#define EFI_NOT_FOUND           EFI_ERROR(14)
#define EFI_OUT_OF_RESOURCES    EFI_ERROR(15)

#define EFI_VARIABLE_NON_VOLATILE       0x0000000000000001
#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002
#define EFI_VARIABLE_RUNTIME_ACCESS     0x0000000000000004

typedef struct {
    uint32_t  data1;
    uint16_t  data2;
    uint16_t  data3;
    uint8_t   data4[8];
} efi_guid_t;

typedef unsigned long efi_status_t;
typedef uint16_t efi_char16_t;        /* UCS-2 */

typedef struct _efi_variable_t {
    efi_char16_t  VariableName[1024/sizeof(efi_char16_t)];
    efi_guid_t    VendorGuid;
    unsigned long DataSize;
    uint8_t       Data[1024];
    efi_status_t  Status;
    uint32_t      Attributes;
} __attribute__((packed)) efi_variable_t;

#define EFI_GLOBAL_VARIABLE \
    (efi_guid_t) {0x8BE4DF61, 0x93CA, 0x11d2, {0xAA, 0x0D, 0x00, 0xE0, 0x98, 0x03, 0x2B, 0x8C}}

#define SYSFS_EFI_VARS "/sys/firmware/efi/vars"


unsigned long
efichar_from_char(efi_char16_t *dest, const char *src, size_t dest_len)
{
    int i, src_len = strlen(src);

    for (i = 0; i < src_len && i < (dest_len/sizeof(*dest)) - 1; i++)
        dest[i] = src[i];
      
    dest[i] = 0;
    return (i * sizeof(*dest));
}


unsigned long
efichar_to_char(char *dest, const efi_char16_t *src, size_t dest_len)
{
    int i, src_len = efichar_strlen(src, -1);

    for (i=0; i < src_len && i < (dest_len/sizeof(*dest)) - 1; i++)
        dest[i] = src[i];
    
    dest[i] = 0;
    return i;
}


int
efichar_strlen(const efi_char16_t *p, int max)
{
    int len = 0;
    const efi_char16_t *start = p;

    if (!p || !*p) 
       return 0;

    while ((max < 0 || p - start < max) && *(p+len))
        ++len;

    return len;
}


void
guid_to_string(efi_guid_t *guid, char *out)
{
    sprintf(out, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
        guid->data1, guid->data2, guid->data3,
        guid->data4[0], guid->data4[1], guid->data4[2], guid->data4[3],
        guid->data4[4], guid->data4[5], guid->data4[6], guid->data4[7]
    );
}


int
variable_to_name(efi_variable_t *var, char *name)
{
    char *p = name;
    efi_guid_t *guid = &(var->VendorGuid);

    if (!var->VariableName)
        return -1;

    efichar_to_char(p, var->VariableName, PATH_MAX);
    p += strlen (p);
    p += sprintf (p, "-");

    guid_to_string(guid, p);

    return strlen (name);
}


void
fill_var(efi_variable_t *var, const char *name)
{
    efi_guid_t guid = EFI_GLOBAL_VARIABLE;

    efichar_from_char(var->VariableName, name, 1024);
    memcpy(&var->VendorGuid, &guid, sizeof(guid));
    var->Attributes = EFI_VARIABLE_NON_VOLATILE
                    | EFI_VARIABLE_BOOTSERVICE_ACCESS
                    | EFI_VARIABLE_RUNTIME_ACCESS;
}


efi_status_t
read_variable(const char *name, efi_variable_t *var)
{
    char filename[PATH_MAX];
    int fd;
    size_t readsize;

    if (!name || !var) 
        return EFI_INVALID_PARAMETER;

    snprintf(filename, PATH_MAX-1, "%s/%s/raw_var", SYSFS_EFI_VARS, name);
    fd = open(filename, O_RDONLY);
    if (fd == -1) {
        return EFI_NOT_FOUND;
    }
    readsize = read(fd, var, sizeof(*var));
    if (readsize != sizeof(*var)) {
        close(fd);
        return EFI_INVALID_PARAMETER;
    }
    close(fd);
    return var->Status;
}


efi_status_t
write_variable(const char *filename, efi_variable_t *var)
{
    int fd;
    size_t writesize;
    char buffer[PATH_MAX+40];

    if (!filename || !var) return EFI_INVALID_PARAMETER;
    memset(buffer, 0, sizeof(buffer));

    fd = open(filename, O_WRONLY);
    if (fd == -1) {
        return EFI_INVALID_PARAMETER;
    }

    writesize = write(fd, var, sizeof(*var));
    if (writesize != sizeof(*var)) {
        close(fd);
        return EFI_INVALID_PARAMETER;
    }

    close(fd);
    return EFI_SUCCESS;
}


efi_status_t
edit_variable(efi_variable_t *var)
{
    char name[PATH_MAX];
    char filename[PATH_MAX];

    if (!var) 
       return EFI_INVALID_PARAMETER;

    variable_to_name(var, name);
    snprintf(filename, PATH_MAX-1, "%s/%s/raw_var", SYSFS_EFI_VARS,name);

    return write_variable(filename, var);
}


efi_status_t
create_variable(efi_variable_t *var)
{
    char filename[PATH_MAX];

    if (!var) 
        return EFI_INVALID_PARAMETER;

    snprintf(filename, PATH_MAX-1, "%s/%s", SYSFS_EFI_VARS,"new_var");

    return write_variable(filename, var);
}


efi_status_t
delete_variable(const char *name)
{
    efi_variable_t var;
    char filename[PATH_MAX];

    memset(&var, 0, sizeof(var));
    fill_var(&var, name);

    if (!&var) 
        return EFI_INVALID_PARAMETER;

    snprintf(filename, PATH_MAX-1, "%s/%s", SYSFS_EFI_VARS,"del_var");

    return write_variable(filename, &var);
}


efi_status_t
create_or_edit_variable(efi_variable_t *var)
{
    efi_variable_t testvar;
    char name[PATH_MAX];

    memcpy(&testvar, var, sizeof(*var));
    variable_to_name(var, name);

    if (read_variable(name, &testvar) == EFI_SUCCESS)
        return edit_variable(var);
    else 
        return create_variable(var);
}


efi_status_t
set_timeout(uint16_t num)
{
    efi_variable_t var;
    uint16_t *n = (uint16_t *)var.Data;

    memset(&var, 0, sizeof(var));
    fill_var(&var, "Timeout");
    *n = num;
    var.DataSize = sizeof(uint16_t);

    return create_or_edit_variable(&var);
}


int
get_timeout()
{
    efi_guid_t guid = EFI_GLOBAL_VARIABLE;
    efi_status_t status;
    efi_variable_t var;
    char name_guid[PATH_MAX];
    uint16_t *n = (uint16_t *)(var.Data);
    char timeout_uuid[40];

    memset(&var, 0, sizeof(var));

    guid_to_string(&guid, timeout_uuid);
    snprintf(name_guid, sizeof(name_guid), "%s-%s", "Timeout", timeout_uuid);

    status = read_variable(name_guid, &var);
    if (status)
        return -1;
    return *n;
}


int
usage(char *name)
{
    fprintf(stderr, "usage: %s [ -w | --write | -r | --read | -d | --delete]\n", name);
    exit(1);
}


int
main(int argc, char *argv[])
{
    efi_variable_t var;
    efi_guid_t guid = EFI_GLOBAL_VARIABLE;
    int status = EFI_SUCCESS;
    int num;

    if (argc == 2) { 
        if (strcmp (argv[1], "-h") == 0 ||
            strcmp (argv[1], "--help") == 0) {
            usage(argv[0]);
        } else if (strcmp (argv[1], "-w") == 0 ||
                   strcmp (argv[1], "--write") == 0) {
            set_timeout(5);
        } else if (strcmp (argv[1], "-r") == 0 ||
                   strcmp (argv[1], "--read") == 0) {
              num = get_timeout();
              if (num != -1) {
                  printf("Timeout: %u seconds\n", num);
              }
        } else if (strcmp (argv[1], "-d") == 0 ||
                   strcmp (argv[1], "--delete") == 0) {
            delete_variable("Timeout");
        } else
           usage(argv[0]);
    } else
       usage(argv[0]);

    exit(status);
}


Note the maximum size of VariableName + Data = 1024 in efivars. This comes from an older EFI specification (0.99).

In my opinion, the older efivars mechanism should be retired and utilities which make use of this mechanism should be re-written to use efivarfs. A number of lines of code could then be removed from the kernel source! As far as I can ascertain, only the following utilities and tools use the efivars mechanism:

  • efibootmgr – Written by Matt XXXXXX. Used to create, modify or delete UEFI boot variables.
  • uefivars – Written by me. ~imply dumps the UEFI Boot variables.
  • Ubuntu Firmware Test Suite – fwts, uefidump.

Note that you cannot use efivars for writing your own EFI variables to NVRAM. This is because the kernel module does extensive checking to ensure only a specific set of variables (EFI boot-related variables) can be accessed.

static const struct variable_validate variable_validate[] = {
      { "BootNext", validate_uint16 },
      { "BootOrder", validate_boot_order },
      { "DriverOrder", validate_boot_order },
      { "Boot*", validate_load_option },
      { "Driver*", validate_load_option },
      { "ConIn", validate_device_path },
      { "ConInDev", validate_device_path },
      { "ConOut", validate_device_path },
      { "ConOutDev", validate_device_path },
      { "ErrOut", validate_device_path },
      { "ErrOutDev", validate_device_path },
      { "Timeout", validate_uint16 },
      { "Lang", validate_ascii_string },
      { "PlatformLang", validate_ascii_string },
      { "", NULL },
};


And for each of these “permitted” variables, extensive validation is performed in the kernel module on the value that you wish to set the variable to. For example, here is the validate_load_option validation routine for BootXXXX and DriverXXXX variables:

static bool
validate_load_option(struct efi_variable *var, int match, u8 *buffer,
		     unsigned long len)
{
	u16 filepathlength;
	int i, desclength = 0, namelen;

	namelen = utf16_strnlen(var->VariableName, sizeof(var->VariableName));

	/* Either "Boot" or "Driver" followed by four digits of hex */
	for (i = match; i < match+4; i++) {
		if (var->VariableName[i] > 127 ||
		    hex_to_bin(var->VariableName[i] & 0xff) < 0)
			return true;
	}

	/* Reject it if there's 4 digits of hex and then further content */
	if (namelen > match + 4)
		return false;

	/* A valid entry must be at least 8 bytes */
	if (len < 8)
		return false;

	filepathlength = buffer[4] | buffer[5] << 8;

	/*
	 * There's no stored length for the description, so it has to be
	 * found by hand
	 */
	desclength = utf16_strsize((efi_char16_t *)(buffer + 6), len - 6) + 2;

	/* Each boot entry must have a descriptor */
	if (!desclength)
		return false;

	/*
	 * If the sum of the length of the description, the claimed filepath
	 * length and the original header are greater than the length of the
	 * variable, it's malformed
	 */
	if ((desclength + filepathlength + 6) > len)
		return false;

	/*
	 * And, finally, check the filepath
	 */
	return validate_device_path(var, match, buffer + desclength + 6,
				    filepathlength);
}


Why these checks and validation were not performed in the efibootmgr utility rather than in the kernel module is a mystery since that would have been the logical place to do this type of operation rather than in the efivars kernel module. By the way efivars is nowadays (since 2009?) built into the kernel and is no longer a standalone kernel module.

If the efivars mechanism is retired, kernel developers would also have to modify the efi_pstore_* functions to use efivarfs. These routines, which I have written about in a previous post, enable generic access to platform level persistent storage, i.e. UEFI NVRAM. The routines are used to create EFI variables of the form “dump-type

Another item made available through efivars is /sys/firmware/efi/systab

# cat systab
MPS=0xfc8d0
ACPI20=0xda7b1000
ACPI=0xda7b1000
SMBIOS=0xf0480


This comes from the following function in efivars.c:

static ssize_t systab_show(struct kobject *kobj,
			   struct kobj_attribute *attr, char *buf)
{
	char *str = buf;

	if (!kobj || !buf)
		return -EINVAL;

	if (efi.mps != EFI_INVALID_TABLE_ADDR)
		str += sprintf(str, "MPS=0x%lx\n", efi.mps);
	if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
		str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20);
	if (efi.acpi != EFI_INVALID_TABLE_ADDR)
		str += sprintf(str, "ACPI=0x%lx\n", efi.acpi);
	if (efi.smbios != EFI_INVALID_TABLE_ADDR)
		str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios);
	if (efi.hcdp != EFI_INVALID_TABLE_ADDR)
		str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp);
	if (efi.boot_info != EFI_INVALID_TABLE_ADDR)
		str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info);
	if (efi.uga != EFI_INVALID_TABLE_ADDR)
		str += sprintf(str, "UGA=0x%lx\n", efi.uga);

	return str - buf;
}


I am unaware of any utility or application which is using this file to access such information.

Well, that is all for this post. I think I have provided you with sufficient information to understand how to use both the new efivarfs filesystem and older efivars mechanism to create, read, write and delete (U)EFI variables.

Merry Christmas and a Happy New Year to all my readers!

5 comments to Efivars and Efivarfs

  • Keshav P R

    Nice post. Do you have any patches for efibootmgr that makes it work with efivarfs (and prefer /sys/firmware/efi/efivars over /sys/firmware/efi/vars if possible)?

    • Thank you. Yes, I have a working prototype. However, I want to run it past Matt Domsch before I release it as I want to also revamp all the command line options to make the utility more intuitive to use.

      • Keshav P R

        I would love to test it in my Thinkpad Edge E430. Can you upload your modified efibootmgr source to some git site (maybe your github page)? BTW the current upstream maintainer of efibootmgr is Jordan Hargrave, not Matt Domsch.

  • Keshav P R

    What is the status of your efibootmgr and uefivars support for efivarfs?

    Can you also make efibootmgr automatically detect the file path of the loader file in efisys like the way gummiboot’s setup utility does, like (with unix-style path separator so that it is easy to use in bash scripts)

    efibootmgr -c -l /boot/efi/EFI/tools/shellx64.efi -L Shell

    instead of

    efibootmgr -c -d /dev/sda -p 1 -l \EFI\tools\shellx64.efi -L Shell

  • Keshav Padram

    Any update on your efibootmgr fork? Please see https://github.com/fpmurphy/Various/issues/1 for my comments.