[PATCH v5 1/2] lib: Support to deal with petitboot's configuration on efi-based platforms
Ge Song
songgebird at gmail.com
Tue Jun 5 12:14:47 AEST 2018
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 V5:
* Has no modification on V4.
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.
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;
+}
--
2.11.0
More information about the Petitboot
mailing list