[PATCH v4 2/2] lib: Support to deal with petitboot's configuration on efi-based platforms
Samuel Mendoza-Jonas
sam at mendozajonas.com
Wed May 30 10:35:42 AEST 2018
On Sat, 2018-05-26 at 11:53 +0800, Ge Song wrote:
> Provide methods to load/store petitboot's configuration on efi-based
> platforms. A test case is also provided.
>
> Signed-off-by: Ge Song <ge.song at hxt-semitech.com>
> ---
> Since V4:
> * test-efivar.c: Use a test directory other than the system's efivarfs to
> complete the test for efivar lib.
> * lib/efi: Add API to support caller to pass a custom efivarfs path.
Looks good!
>
>
> lib/Makefile.am | 2 +
> test/lib/Makefile.am | 3 +-
> lib/efi/efivar.h | 46 +++++
> lib/efi/efivar.c | 204 ++++++++++++++++++++
> test/lib/test-efivar.c | 127 ++++++++++++
> 5 files changed, 381 insertions(+), 1 deletion(-)
>
> diff --git a/lib/Makefile.am b/lib/Makefile.am
> index 8f682021d45a..cfaa0d2cad47 100644
> --- a/lib/Makefile.am
> +++ b/lib/Makefile.am
> @@ -59,6 +59,8 @@ lib_libpbcore_la_SOURCES = \
> lib/util/util.h \
> lib/flash/config.h \
> lib/flash/flash.h \
> + lib/efi/efivar.h \
> + lib/efi/efivar.c \
> $(gpg_int_SOURCES)
>
> if ENABLE_MTD
> diff --git a/test/lib/Makefile.am b/test/lib/Makefile.am
> index 9636b08d6a6b..22e3ac91499d 100644
> --- a/test/lib/Makefile.am
> +++ b/test/lib/Makefile.am
> @@ -23,7 +23,8 @@ lib_TESTS = \
> test/lib/test-process-parent-stdout \
> test/lib/test-process-both \
> test/lib/test-process-stdout-eintr \
> - test/lib/test-fold
> + test/lib/test-fold \
> + test/lib/test-efivar
>
> $(lib_TESTS): LIBS += $(core_lib)
>
> diff --git a/lib/efi/efivar.h b/lib/efi/efivar.h
> new file mode 100644
> index 000000000000..058b196e5050
> --- /dev/null
> +++ b/lib/efi/efivar.h
> @@ -0,0 +1,46 @@
> +/*
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + *
> + * Copyright (C) 2018 Huaxintong Semiconductor Technology Co.,Ltd. All rights
> + * reserved.
> + * Author: Ge Song <ge.song at hxt-semitech.com>
> + */
> +#ifndef EFIVAR_H
> +#define EFIVAR_H
> +
> +#include <linux/magic.h>
> +#include <stdint.h>
> +
> +#define EFI_VARIABLE_NON_VOLATILE 0x00000001
> +#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002
> +#define EFI_VARIABLE_RUNTIME_ACCESS 0x00000004
> +#define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x00000008
> +#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x00000010
> +#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 0x00000020
> +#define EFI_VARIABLE_APPEND_WRITE 0x00000040
> +
> +#ifndef EFIVARFS_MAGIC
> +#define EFIVARFS_MAGIC 0xde5e81e4
> +#endif
> +
> +extern const char *efivarfs_path;
> +
> +void set_efivarfs_path(const char *path);
> +const char *get_efivarfs_path(void);
> +int efi_get_variable(void *ctx, const char *guidstr, const char *name,
> + uint8_t **data, size_t *data_size, uint32_t *attributes);
> +int efi_set_variable(void *ctx, const char *guidstr, const char *name,
> + uint8_t *data, size_t data_size, uint32_t attributes);
> +int efi_del_variable(void *ctx, const char *guidstr, const char *name);
> +#endif /* EFIVAR_H */
> diff --git a/lib/efi/efivar.c b/lib/efi/efivar.c
> new file mode 100644
> index 000000000000..8e6fce820bfb
> --- /dev/null
> +++ b/lib/efi/efivar.c
> @@ -0,0 +1,204 @@
> +/*
> + * Methods to manipulation of EFI variables on UEFI-based platforms
> + *
> + * The library relies on pesudo file system named "efivarfs". This is the
> + * new interface to manipulate EFI varibles and was part of the linux
> + * kernel v3.10 release.
> + *
> + * There is also a legacy efivars interface exported via the path
> + * "firmware/efi/vars/" relative to the mount point of sysfs. Since it has
> + * some drawbacks and is deprecated, we don't support this interface.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + *
> + * Copyright (C) 2018 Huaxintong Semiconductor Technology Co.,Ltd. All rights
> + * reserved.
> + * Author: Ge Song <ge.song at hxt-semitech.com>
> + */
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <linux/fs.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/ioctl.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +
> +#include "efivar.h"
> +#include "talloc/talloc.h"
> +
> +const char *efivarfs_path = NULL;
> +
> +inline void set_efivarfs_path(const char *path)
> +{
> + efivarfs_path = path;
> +}
> +
> +inline const char *get_efivarfs_path(void)
> +{
> +
> + return efivarfs_path;
> +}
> +
> +int efi_del_variable(void *ctx, const char *guidstr,
> + const char *name)
> +{
> + int fd, flag, errno_value;
> + int rc = -1;
> + const char *dir;
> + char *path;
> +
> + dir = get_efivarfs_path();
> + if (!dir)
> + return EFAULT;
> +
> + path = talloc_asprintf(ctx, "%s%s-%s", dir, name, guidstr);
> + if (!path)
> + return ENOMEM;
> +
> + fd = open(path, O_RDONLY|O_NONBLOCK);
> + if (fd == -1)
> + goto err;
> +
> + rc = ioctl(fd, FS_IOC_GETFLAGS, &flag);
> + if (rc == -1)
> + goto err;
> +
> + flag &= ~FS_IMMUTABLE_FL;
> + rc = ioctl(fd, FS_IOC_SETFLAGS, &flag);
> + if (rc == -1)
> + goto err;
> +
> + close(fd);
> + rc = unlink(path);
> +
> +err:
> + errno_value = errno;
> + if (fd > 0)
> + close(fd);
> +
> + errno = errno_value;
> + return rc;
> +}
> +
> +int efi_get_variable(void *ctx, const char *guidstr, const char *name,
> + uint8_t **data, size_t *data_size, uint32_t *attributes)
> +{
> + int fd, errno_value;
> + int rc = -1;
> + void *p, *buf;
> + size_t bufsize = 4096;
> + size_t filesize = 0;
> + ssize_t sz;
> + const char *dir;
> + char *path;
> +
> + dir = get_efivarfs_path();
> + if (!dir)
> + return EFAULT;
> +
> + path = talloc_asprintf(ctx, "%s%s-%s", dir, name, guidstr);
> + if (!path)
> + return ENOMEM;
> +
> + fd = open(path, O_RDONLY|O_NONBLOCK);
> + if (fd < 0)
> + goto err;
> +
> + buf = talloc_size(ctx, bufsize);
> + if (!buf)
> + goto err;
> +
> + do {
> + p = buf + filesize;
> + sz = read(fd, p, bufsize);
> + if (sz < 0 && errno == EAGAIN) {
> + continue;
> + } else if (sz == 0) {
> + break;
> + }
> + filesize += sz;
> + } while (1);
> +
> + *attributes = *(uint32_t *)buf;
> + *data = (uint8_t *)(buf + sizeof(uint32_t));
> + *data_size = strlen(buf + sizeof(uint32_t));
> + rc = 0;
> +
> +err:
> + errno_value = errno;
> + if (fd > 0)
> + close(fd);
> +
> + errno = errno_value;
> + return rc;
> +}
> +
> +int efi_set_variable(void *ctx, const char *guidstr, const char *name,
> + uint8_t *data, size_t data_size, uint32_t attributes)
> +{
> + int rc = -1, errno_value;
> + int fd = -1;
> + ssize_t len;
> + const char *dir;
> + char *path;
> + void *buf;
> + size_t bufsize;
> + mode_t mask = 0644;
> +
> + dir = get_efivarfs_path();
> + if (!dir)
> + return EFAULT;
> +
> + path = talloc_asprintf(ctx, "%s%s-%s", dir, name, guidstr);
> + if (!path)
> + return ENOMEM;
> +
> + if (!access(path, F_OK)) {
> + rc = efi_del_variable(ctx, guidstr, name);
> + if (rc < 0) {
> + goto err;
> + }
> + }
> +
> + fd = open(path, O_CREAT|O_WRONLY, mask);
> + if (fd < 0)
> + goto err;
> +
> + bufsize = sizeof(uint32_t) + data_size;
> + buf = talloc_size(ctx, bufsize);
> + if (!buf)
> + goto err;
> +
> + *(uint32_t *)buf = attributes;
> + memcpy(buf + sizeof(uint32_t), data, data_size);
> +
> + len = write(fd, buf, bufsize);
> + if ((size_t)len != bufsize)
> + goto err;
> + else
> + rc = 0;
> +
> +err:
> + errno_value = errno;
> + if (fd > 0)
> + close(fd);
> +
> + errno = errno_value;
> + return rc;
> +}
> diff --git a/test/lib/test-efivar.c b/test/lib/test-efivar.c
> new file mode 100644
> index 000000000000..8ceb8f50cae9
> --- /dev/null
> +++ b/test/lib/test-efivar.c
> @@ -0,0 +1,127 @@
> +/*
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + *
> + * Copyright (C) 2018 Huaxintong Semiconductor Technology Co.,Ltd. All rights
> + * reserved.
> + * Author: Ge Song <ge.song at hxt-semitech.com>
> + */
> +#include <assert.h>
> +#include <dirent.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <linux/limits.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/stat.h>
> +#include <sys/statfs.h>
> +#include <unistd.h>
> +
> +#include "efi/efivar.h"
> +#include "talloc/talloc.h"
> +
> +#define DEF_ATTR (EFI_VARIABLE_NON_VOLATILE | \
> + EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)
> +
> +static const char *test_efivar_guid = "c9c07add-256e-4452-b911-f8d0d35a1ac7";
> +static const char *test_varname = "efivartest";
> +static const char *test_data = "petitboot";
> +
> +static char* find_efitest_path(void)
> +{
> + static char dir[PATH_MAX] = {0};
> + static bool run = false;
> + char *rest_path = "/efivarfs_data/";
> + char *pos = NULL;
> +
> + if (run)
> + return dir;
> +
> + readlink("/proc/self/exe", dir, PATH_MAX);
> +
> + pos = strrchr(dir, '/');
> + *pos = '\0';
> +
> + strcat(dir, rest_path);
> + run = true;
> +
> + return dir;
> +}
> +
> +static bool probe(void)
> +{
> + char *path;
> + int rc;
> +
> + path = find_efitest_path();
> +
> + rc = access(path, F_OK);
> + if (rc) {
> + if (errno == ENOENT) {
> + rc = mkdir(path, 0755);
> + if(rc)
> + return false;
> + } else {
> + return false;
> + }
> + }
> +
> + set_efivarfs_path(path);
> +
> + return true;
> +}
> +
> +int main(void)
> +{
> + void *ctx = NULL;
> + int rc, errno_value;
> + size_t size;
> + uint8_t *data = NULL;
> + uint32_t attr = DEF_ATTR;
> + char *path = NULL;
> +
> + if(!probe())
> + return ENOENT;
> +
> + talloc_new(ctx);
> + size = strlen(test_data) + 1;
> + rc = efi_set_variable(ctx, test_efivar_guid, test_varname,
> + (uint8_t *)test_data, size, attr);
> +
> + rc = efi_get_variable(ctx, test_efivar_guid, test_varname,
> + &data, &size, &attr);
> +
> + assert(data != NULL);
> + rc = strcmp((char *)data, test_data);
> + if (rc) {
> + talloc_free(ctx);
> + assert(0);
> + }
> +
> + rc = efi_del_variable(ctx, test_efivar_guid, test_varname);
> +
> + rc = efi_get_variable(ctx, test_efivar_guid, test_varname,
> + &data, &size, &attr);
> +
> + errno_value = errno;
> + talloc_free(ctx);
> +
> + assert(errno_value == ENOENT);
> +
> + path = find_efitest_path();
> + rmdir(path);
> +
> + return EXIT_SUCCESS;
> +}
More information about the Petitboot
mailing list