COMPILE TESTED ONLY.

mount -t fwvarfs opal_secvar /fw

Signed-off-by: Daniel Axtens <d...@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.k...@canonical.com>
+ * and drivers/firmware/efi/vars.c:
+ * Copyright (C) 2001,2003,2004 Dell <matt_dom...@dell.com>
+ * Copyright (C) 2004 Intel Corporation <matthew.e.tolent...@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

Reply via email to