powerpc/powernv: Add OPAL API interface to access secure variable
authorNayna Jain <nayna@linux.ibm.com>
Mon, 11 Nov 2019 03:10:33 +0000 (21:10 -0600)
committerMichael Ellerman <mpe@ellerman.id.au>
Tue, 12 Nov 2019 13:33:22 +0000 (00:33 +1100)
The X.509 certificates trusted by the platform and required to secure
boot the OS kernel are wrapped in secure variables, which are
controlled by OPAL.

This patch adds firmware/kernel interface to read and write OPAL
secure variables based on the unique key.

This support can be enabled using CONFIG_OPAL_SECVAR.

Signed-off-by: Claudio Carvalho <cclaudio@linux.ibm.com>
Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
Signed-off-by: Eric Richter <erichte@linux.ibm.com>
[mpe: Make secvar_ops __ro_after_init, only build opal-secvar.c if PPC_SECURE_BOOT=y]
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/1573441836-3632-2-git-send-email-nayna@linux.ibm.com
arch/powerpc/include/asm/opal-api.h
arch/powerpc/include/asm/opal.h
arch/powerpc/include/asm/secvar.h [new file with mode: 0644]
arch/powerpc/kernel/Makefile
arch/powerpc/kernel/secvar-ops.c [new file with mode: 0644]
arch/powerpc/platforms/powernv/Makefile
arch/powerpc/platforms/powernv/opal-call.c
arch/powerpc/platforms/powernv/opal-secvar.c [new file with mode: 0644]
arch/powerpc/platforms/powernv/opal.c

index 378e399..c1f25a7 100644 (file)
 #define OPAL_MPIPL_UPDATE                      173
 #define OPAL_MPIPL_REGISTER_TAG                        174
 #define OPAL_MPIPL_QUERY_TAG                   175
-#define OPAL_LAST                              175
+#define OPAL_SECVAR_GET                                176
+#define OPAL_SECVAR_GET_NEXT                   177
+#define OPAL_SECVAR_ENQUEUE_UPDATE             178
+#define OPAL_LAST                              178
 
 #define QUIESCE_HOLD                   1 /* Spin all calls at entry */
 #define QUIESCE_REJECT                 2 /* Fail all calls with OPAL_BUSY */
index a0cf8fb..9986ac3 100644 (file)
@@ -298,6 +298,13 @@ int opal_sensor_group_clear(u32 group_hndl, int token);
 int opal_sensor_group_enable(u32 group_hndl, int token, bool enable);
 int opal_nx_coproc_init(uint32_t chip_id, uint32_t ct);
 
+int opal_secvar_get(const char *key, uint64_t key_len, u8 *data,
+                   uint64_t *data_size);
+int opal_secvar_get_next(const char *key, uint64_t *key_len,
+                        uint64_t key_buf_size);
+int opal_secvar_enqueue_update(const char *key, uint64_t key_len, u8 *data,
+                              uint64_t data_size);
+
 s64 opal_mpipl_update(enum opal_mpipl_ops op, u64 src, u64 dest, u64 size);
 s64 opal_mpipl_register_tag(enum opal_mpipl_tags tag, u64 addr);
 s64 opal_mpipl_query_tag(enum opal_mpipl_tags tag, u64 *addr);
diff --git a/arch/powerpc/include/asm/secvar.h b/arch/powerpc/include/asm/secvar.h
new file mode 100644 (file)
index 0000000..4cc35b5
--- /dev/null
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 IBM Corporation
+ * Author: Nayna Jain
+ *
+ * PowerPC secure variable operations.
+ */
+#ifndef SECVAR_OPS_H
+#define SECVAR_OPS_H
+
+#include <linux/types.h>
+#include <linux/errno.h>
+
+extern const struct secvar_operations *secvar_ops;
+
+struct secvar_operations {
+       int (*get)(const char *key, uint64_t key_len, u8 *data,
+                  uint64_t *data_size);
+       int (*get_next)(const char *key, uint64_t *key_len,
+                       uint64_t keybufsize);
+       int (*set)(const char *key, uint64_t key_len, u8 *data,
+                  uint64_t data_size);
+};
+
+#ifdef CONFIG_PPC_SECURE_BOOT
+
+extern void set_secvar_ops(const struct secvar_operations *ops);
+
+#else
+
+static inline void set_secvar_ops(const struct secvar_operations *ops) { }
+
+#endif
+
+#endif
index e8eb295..3cf2642 100644 (file)
@@ -161,7 +161,7 @@ ifneq ($(CONFIG_PPC_POWERNV)$(CONFIG_PPC_SVM),)
 obj-y                          += ucall.o
 endif
 
-obj-$(CONFIG_PPC_SECURE_BOOT)  += secure_boot.o ima_arch.o
+obj-$(CONFIG_PPC_SECURE_BOOT)  += secure_boot.o ima_arch.o secvar-ops.o
 
 # Disable GCOV, KCOV & sanitizers in odd or sensitive code
 GCOV_PROFILE_prom_init.o := n
diff --git a/arch/powerpc/kernel/secvar-ops.c b/arch/powerpc/kernel/secvar-ops.c
new file mode 100644 (file)
index 0000000..6a29777
--- /dev/null
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 IBM Corporation
+ * Author: Nayna Jain
+ *
+ * This file initializes secvar operations for PowerPC Secureboot
+ */
+
+#include <linux/cache.h>
+#include <asm/secvar.h>
+
+const struct secvar_operations *secvar_ops __ro_after_init;
+
+void set_secvar_ops(const struct secvar_operations *ops)
+{
+       secvar_ops = ops;
+}
index a3ac964..c0f8120 100644 (file)
@@ -20,3 +20,4 @@ obj-$(CONFIG_PPC_MEMTRACE)    += memtrace.o
 obj-$(CONFIG_PPC_VAS)  += vas.o vas-window.o vas-debug.o
 obj-$(CONFIG_OCXL_BASE)        += ocxl.o
 obj-$(CONFIG_SCOM_DEBUGFS) += opal-xscom.o
+obj-$(CONFIG_PPC_SECURE_BOOT) += opal-secvar.o
index a2aa5e4..5cd0f52 100644 (file)
@@ -290,3 +290,6 @@ OPAL_CALL(opal_nx_coproc_init,                      OPAL_NX_COPROC_INIT);
 OPAL_CALL(opal_mpipl_update,                   OPAL_MPIPL_UPDATE);
 OPAL_CALL(opal_mpipl_register_tag,             OPAL_MPIPL_REGISTER_TAG);
 OPAL_CALL(opal_mpipl_query_tag,                        OPAL_MPIPL_QUERY_TAG);
+OPAL_CALL(opal_secvar_get,                     OPAL_SECVAR_GET);
+OPAL_CALL(opal_secvar_get_next,                        OPAL_SECVAR_GET_NEXT);
+OPAL_CALL(opal_secvar_enqueue_update,          OPAL_SECVAR_ENQUEUE_UPDATE);
diff --git a/arch/powerpc/platforms/powernv/opal-secvar.c b/arch/powerpc/platforms/powernv/opal-secvar.c
new file mode 100644 (file)
index 0000000..14133e1
--- /dev/null
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PowerNV code for secure variables
+ *
+ * Copyright (C) 2019 IBM Corporation
+ * Author: Claudio Carvalho
+ *         Nayna Jain
+ *
+ * APIs to access secure variables managed by OPAL.
+ */
+
+#define pr_fmt(fmt) "secvar: "fmt
+
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <asm/opal.h>
+#include <asm/secvar.h>
+#include <asm/secure_boot.h>
+
+static int opal_status_to_err(int rc)
+{
+       int err;
+
+       switch (rc) {
+       case OPAL_SUCCESS:
+               err = 0;
+               break;
+       case OPAL_UNSUPPORTED:
+               err = -ENXIO;
+               break;
+       case OPAL_PARAMETER:
+               err = -EINVAL;
+               break;
+       case OPAL_RESOURCE:
+               err = -ENOSPC;
+               break;
+       case OPAL_HARDWARE:
+               err = -EIO;
+               break;
+       case OPAL_NO_MEM:
+               err = -ENOMEM;
+               break;
+       case OPAL_EMPTY:
+               err = -ENOENT;
+               break;
+       case OPAL_PARTIAL:
+               err = -EFBIG;
+               break;
+       default:
+               err = -EINVAL;
+       }
+
+       return err;
+}
+
+static int opal_get_variable(const char *key, uint64_t ksize,
+                            u8 *data, uint64_t *dsize)
+{
+       int rc;
+
+       if (!key || !dsize)
+               return -EINVAL;
+
+       *dsize = cpu_to_be64(*dsize);
+
+       rc = opal_secvar_get(key, ksize, data, dsize);
+
+       *dsize = be64_to_cpu(*dsize);
+
+       return opal_status_to_err(rc);
+}
+
+static int opal_get_next_variable(const char *key, uint64_t *keylen,
+                                 uint64_t keybufsize)
+{
+       int rc;
+
+       if (!key || !keylen)
+               return -EINVAL;
+
+       *keylen = cpu_to_be64(*keylen);
+
+       rc = opal_secvar_get_next(key, keylen, keybufsize);
+
+       *keylen = be64_to_cpu(*keylen);
+
+       return opal_status_to_err(rc);
+}
+
+static int opal_set_variable(const char *key, uint64_t ksize, u8 *data,
+                            uint64_t dsize)
+{
+       int rc;
+
+       if (!key || !data)
+               return -EINVAL;
+
+       rc = opal_secvar_enqueue_update(key, ksize, data, dsize);
+
+       return opal_status_to_err(rc);
+}
+
+static const struct secvar_operations opal_secvar_ops = {
+       .get = opal_get_variable,
+       .get_next = opal_get_next_variable,
+       .set = opal_set_variable,
+};
+
+static int opal_secvar_probe(struct platform_device *pdev)
+{
+       if (!opal_check_token(OPAL_SECVAR_GET)
+                       || !opal_check_token(OPAL_SECVAR_GET_NEXT)
+                       || !opal_check_token(OPAL_SECVAR_ENQUEUE_UPDATE)) {
+               pr_err("OPAL doesn't support secure variables\n");
+               return -ENODEV;
+       }
+
+       set_secvar_ops(&opal_secvar_ops);
+
+       return 0;
+}
+
+static const struct of_device_id opal_secvar_match[] = {
+       { .compatible = "ibm,secvar-backend",},
+       {},
+};
+
+static struct platform_driver opal_secvar_driver = {
+       .driver = {
+               .name = "secvar",
+               .of_match_table = opal_secvar_match,
+       },
+};
+
+static int __init opal_secvar_init(void)
+{
+       return platform_driver_probe(&opal_secvar_driver, opal_secvar_probe);
+}
+device_initcall(opal_secvar_init);
index 38e9027..8355bcd 100644 (file)
@@ -1002,6 +1002,9 @@ static int __init opal_init(void)
        /* Initialise OPAL Power control interface */
        opal_power_control_init();
 
+       /* Initialize OPAL secure variables */
+       opal_pdev_init("ibm,secvar-backend");
+
        return 0;
 }
 machine_subsys_initcall(powernv, opal_init);