[WIP RFC PATCH 6/6] fwvarfs: Add opal_secvar backend
Daniel Axtens
dja at axtens.net
Mon May 20 16:25:53 AEST 2019
COMPILE TESTED ONLY.
mount -t fwvarfs opal_secvar /fw
Signed-off-by: Daniel Axtens <dja at axtens.net>
---
Documentation/filesystems/fwvarfs.txt | 3 +
fs/fwvarfs/Kconfig | 11 ++
fs/fwvarfs/Makefile | 1 +
fs/fwvarfs/fwvarfs.c | 3 +
fs/fwvarfs/fwvarfs.h | 4 +
fs/fwvarfs/opal_secvar.c | 218 ++++++++++++++++++++++++++
6 files changed, 240 insertions(+)
create mode 100644 fs/fwvarfs/opal_secvar.c
diff --git a/Documentation/filesystems/fwvarfs.txt b/Documentation/filesystems/fwvarfs.txt
index 7c1e921e5c50..3ecc4b4428a5 100644
--- a/Documentation/filesystems/fwvarfs.txt
+++ b/Documentation/filesystems/fwvarfs.txt
@@ -36,6 +36,9 @@ Supported backends
backend. Read-only with no creation or deletion, but sufficient that
efivar --print --name <whatever> works the same as efivarfs.
+ * opal_secvar - COMPILE-TESTED ONLY implementation against PowerNV
+ OPAL Secure Variable storage.
+
Usage
-----
diff --git a/fs/fwvarfs/Kconfig b/fs/fwvarfs/Kconfig
index e4474da11dbc..cb9cbc6f8fb3 100644
--- a/fs/fwvarfs/Kconfig
+++ b/fs/fwvarfs/Kconfig
@@ -34,3 +34,14 @@ config FWVAR_FS_EFI_BACKEND
in the same way the do with efivarfs.
Say N here unless you're exploring fwvarfs.
+
+config FWVAR_FS_OPAL_SECVAR_BACKEND
+ bool "OPAL Secure Variable backend"
+ depends on FWVAR_FS
+ depends on OPAL_SECVAR
+ help
+ Include a read-only, compile-tested-only, not up-to-date
+ backend for OPAL Secure Variables. This is really just
+ designed to show how the code would work, and you should
+ only select Y here if you are developing OPAL secure
+ variables.
diff --git a/fs/fwvarfs/Makefile b/fs/fwvarfs/Makefile
index 2ab9dfd650ca..8d258acdfef7 100644
--- a/fs/fwvarfs/Makefile
+++ b/fs/fwvarfs/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_FWVAR_FS) += fwvarfs.o
obj-$(CONFIG_FWVAR_FS_MEM_BACKEND) += mem.o
obj-$(CONFIG_FWVAR_FS_EFI_BACKEND) += efi.o
+obj-$(CONFIG_FWVAR_FS_OPAL_SECVAR_BACKEND) += opal_secvar.o
diff --git a/fs/fwvarfs/fwvarfs.c b/fs/fwvarfs/fwvarfs.c
index 643ec6585b4d..3c93b6442e95 100644
--- a/fs/fwvarfs/fwvarfs.c
+++ b/fs/fwvarfs/fwvarfs.c
@@ -24,6 +24,9 @@ static struct fwvarfs_backend *fwvarfs_backends[] = {
#endif
#ifdef CONFIG_FWVAR_FS_EFI_BACKEND
&fwvarfs_efi_backend,
+#endif
+#ifdef CONFIG_FWVAR_FS_OPAL_SECVAR_BACKEND
+ &fwvarfs_opal_secvar_backend,
#endif
NULL,
};
diff --git a/fs/fwvarfs/fwvarfs.h b/fs/fwvarfs/fwvarfs.h
index 49bde268401f..5780046dafae 100644
--- a/fs/fwvarfs/fwvarfs.h
+++ b/fs/fwvarfs/fwvarfs.h
@@ -117,4 +117,8 @@ extern struct fwvarfs_backend fwvarfs_mem_backend;
extern struct fwvarfs_backend fwvarfs_efi_backend;
#endif
+#if defined(CONFIG_FWVAR_FS_OPAL_SECVAR_BACKEND)
+extern struct fwvarfs_backend fwvarfs_opal_secvar_backend;
+#endif
+
#endif /* FWVARFS_H */
diff --git a/fs/fwvarfs/opal_secvar.c b/fs/fwvarfs/opal_secvar.c
new file mode 100644
index 000000000000..4a1749317ed9
--- /dev/null
+++ b/fs/fwvarfs/opal_secvar.c
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 IBM Corporation
+ * Author: Daniel Axtens
+ *
+ * Loosely based on efivarfs:
+ * Copyright (C) 2012 Red Hat, Inc.
+ * Copyright (C) 2012 Jeremy Kerr <jeremy.kerr at canonical.com>
+ * and drivers/firmware/efi/vars.c:
+ * Copyright (C) 2001,2003,2004 Dell <Matt_Domsch at dell.com>
+ * Copyright (C) 2004 Intel Corporation <matthew.e.tolentino at intel.com>
+ *
+ * We cheat by not allowing for case-insensitivity.
+ */
+
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include "fwvarfs.h"
+
+#include <linux/uuid.h>
+#include <linux/ucs2_string.h>
+#include <asm/opal-api.h>
+#include <asm/opal-secvar.h>
+
+#define pr_fmt(fmt) "fwvarfs: opal_secvar: " fmt
+
+static LIST_HEAD(opal_secvar_file_list);
+
+struct fwvarfs_opal_secvar_file {
+ struct list_head list;
+ u16 *name;
+ guid_t vendor;
+};
+
+// stolen from efi.h
+static inline char *
+guid_to_str(guid_t *guid, char *out)
+{
+ sprintf(out, "%pUl", guid->b);
+ return out;
+}
+
+// need a forward decl to pass down to register
+struct fwvarfs_backend fwvarfs_opal_secvar_backend;
+
+
+static ssize_t fwvarfs_opal_secvar_read(void *variable, char *buf,
+ size_t count, loff_t off)
+{
+ struct fwvarfs_opal_secvar_file *file_data = variable;
+ unsigned long datasize = 0;
+ u32 attributes;
+ void *data;
+ ssize_t size = 0;
+ loff_t ppos = off;
+ int rc;
+
+ // get size
+ rc = opal_get_secure_variable(file_data->name, &file_data->vendor,
+ NULL, &datasize, NULL);
+ if (rc != OPAL_SUCCESS && rc != OPAL_PARTIAL)
+ return -EIO;
+
+ data = kmalloc(datasize + sizeof(attributes), GFP_KERNEL);
+
+ if (!data)
+ return -ENOMEM;
+
+ rc = opal_get_secure_variable(file_data->name, &file_data->vendor,
+ &attributes, &datasize, data + sizeof(attributes));
+ if (rc != OPAL_SUCCESS) {
+ size = -EIO;
+ goto out_free;
+ }
+
+ memcpy(data, &attributes, sizeof(attributes));
+ size = memory_read_from_buffer(buf, count, &ppos, data,
+ datasize + sizeof(attributes));
+out_free:
+ kfree(data);
+
+ return size;
+}
+
+static int fwvarfs_opal_secvar_callback(u16 *name16, guid_t vendor,
+ unsigned long name_size)
+{
+ struct fwvarfs_opal_secvar_file *file_data;
+ char *name;
+ int len;
+ int err = -ENOMEM;
+
+ file_data = kzalloc(sizeof(*file_data), GFP_KERNEL);
+ if (!file_data)
+ return err;
+
+ file_data->name = kmemdup(name16, name_size, GFP_KERNEL);
+ if (!file_data->name)
+ goto fail;
+
+ file_data->vendor = vendor;
+
+ len = ucs2_utf8size(name16);
+
+ /* name, plus '-', plus GUID, plus NUL */
+ name = kmalloc(len + 1 + UUID_STRING_LEN + 1, GFP_KERNEL);
+ if (!name)
+ goto fail_embedded_name;
+
+ ucs2_as_utf8(name, name16, len);
+
+ name[len] = '-';
+
+ guid_to_str(&vendor, name + len + 1);
+
+ name[len + UUID_STRING_LEN + 1] = '\0';
+
+ // no convenient way to get size without reading the whole thing,
+ // present size as 0 for now.
+
+ err = fwvarfs_register_var(&fwvarfs_opal_secvar_backend, name,
+ file_data, 0);
+ if (err)
+ goto fail_name;
+
+ INIT_LIST_HEAD(&file_data->list);
+ list_add(&opal_secvar_file_list, &file_data->list);
+
+ /* copied by the above, I think */
+ kfree(name);
+
+ return 0;
+fail_name:
+ kfree(name);
+fail_embedded_name:
+ kfree(file_data->name);
+fail:
+ kfree(file_data);
+ return err;
+}
+
+
+static void fwvarfs_opal_secvar_destroy(void *var)
+{
+ struct fwvarfs_opal_secvar_file *file_data = var;
+
+ kfree(file_data->name);
+ list_del(&file_data->list);
+ kfree(file_data);
+}
+
+
+static int fwvarfs_opal_secvar_enumerate(void)
+{
+ int err;
+ struct fwvarfs_opal_secvar_file *pos, *tmp;
+ unsigned long variable_name_size = 1024;
+ u16 *variable_name;
+ unsigned long status;
+ guid_t vendor_guid;
+
+ variable_name = kzalloc(variable_name_size, GFP_KERNEL);
+ if (!variable_name)
+ return -ENOMEM;
+
+ /*
+ * Assume that as per EFI spec, the maximum storage allocated for both
+ * the variable name and variable data is 1024 bytes.
+ */
+
+ do {
+ variable_name_size = 1024;
+
+ status = opal_get_next_secure_variable(&variable_name_size,
+ variable_name,
+ &vendor_guid);
+ switch (status) {
+ case OPAL_SUCCESS:
+ err = fwvarfs_opal_secvar_callback(variable_name,
+ vendor_guid, variable_name_size);
+ if (err)
+ status = OPAL_EMPTY;
+
+ break;
+ case OPAL_EMPTY:
+ break;
+ case OPAL_UNSUPPORTED:
+ status = OPAL_EMPTY;
+ err = -ENODEV;
+ default:
+ pr_warn("opal_get_next_secure_variable: status=%lx\n",
+ status);
+ status = OPAL_EMPTY;
+ err = -EIO;
+ break;
+ }
+
+ } while (status != OPAL_EMPTY);
+
+ kfree(variable_name);
+
+ if (err) {
+ list_for_each_entry_safe(pos, tmp, &opal_secvar_file_list,
+ list) {
+ fwvarfs_opal_secvar_destroy(pos);
+ }
+ }
+
+ return err;
+}
+
+struct fwvarfs_backend fwvarfs_opal_secvar_backend = {
+ .name = "opal_secvar",
+ .destroy = fwvarfs_opal_secvar_destroy,
+ .enumerate = fwvarfs_opal_secvar_enumerate,
+ .read = fwvarfs_opal_secvar_read,
+};
--
2.19.1
More information about the Linuxppc-dev
mailing list