lib/efi: Add new routines to access efi variables
authorGe Song <ge.song@hxt-semitech.com>
Thu, 2 Aug 2018 17:29:36 +0000 (17:29 +0000)
committerSamuel Mendoza-Jonas <sam@mendozajonas.com>
Tue, 7 Aug 2018 01:30:36 +0000 (11:30 +1000)
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@hxt-semitech.com>
[Cleanup file comments, make efivarfs_path static.]
Signed-off-by: Geoff Levand <geoff@infradead.org>
Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com>
lib/Makefile.am
lib/efi/efivar.c [new file with mode: 0644]
lib/efi/efivar.h [new file with mode: 0644]
test/lib/test-efivar.c [new file with mode: 0644]

index 0088e0bf5d14d0a52887fdd2a4aea9a5c2b9981b..59d37ab793dc9efd38760a95bb1b655a4df399d8 100644 (file)
@@ -65,7 +65,9 @@ lib_libpbcore_la_SOURCES = \
        lib/util/util.h \
        lib/flash/config.h \
        lib/flash/flash.h \
-       lib/security/security.h
+       lib/security/security.h \
+       lib/efi/efivar.h \
+       lib/efi/efivar.c
 
 if ENABLE_MTD
 lib_libpbcore_la_SOURCES += \
diff --git a/lib/efi/efivar.c b/lib/efi/efivar.c
new file mode 100644 (file)
index 0000000..1ac6990
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ *  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@hxt-semitech.com>
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+
+#include "efivar.h"
+#include "log/log.h"
+#include "talloc/talloc.h"
+
+static const char *efivarfs_path;
+
+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 -1;
+
+       path = talloc_asprintf(ctx, "%s%s-%s", dir, name, guidstr);
+       if (!path)
+               return -1;
+
+       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/lib/efi/efivar.h b/lib/efi/efivar.h
new file mode 100644 (file)
index 0000000..ebf73fa
--- /dev/null
@@ -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@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
+
+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/test/lib/test-efivar.c b/test/lib/test-efivar.c
new file mode 100644 (file)
index 0000000..8ceb8f5
--- /dev/null
@@ -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@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;
+}