| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| From: Nayna Jain <nayna@linux.ibm.com> |
| Date: Sun, 10 Nov 2019 21:10:33 -0600 |
| Subject: [PATCH 10/19] powerpc/powernv: Add OPAL API interface to access |
| secure variable |
| |
| 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 |
| (cherry picked from commit 9155e2341aa8b5df057dc1c77633b33d1a4f17d2) |
| Signed-off-by: Joel Stanley <joel@jms.id.au> |
| --- |
| arch/powerpc/include/asm/opal-api.h | 5 +- |
| arch/powerpc/include/asm/opal.h | 7 + |
| arch/powerpc/include/asm/secvar.h | 35 +++++ |
| arch/powerpc/kernel/Makefile | 2 +- |
| arch/powerpc/kernel/secvar-ops.c | 17 +++ |
| arch/powerpc/platforms/powernv/Makefile | 1 + |
| arch/powerpc/platforms/powernv/opal-call.c | 3 + |
| arch/powerpc/platforms/powernv/opal-secvar.c | 140 +++++++++++++++++++ |
| arch/powerpc/platforms/powernv/opal.c | 3 + |
| 9 files changed, 211 insertions(+), 2 deletions(-) |
| create mode 100644 arch/powerpc/include/asm/secvar.h |
| create mode 100644 arch/powerpc/kernel/secvar-ops.c |
| create mode 100644 arch/powerpc/platforms/powernv/opal-secvar.c |
| |
| diff --git a/arch/powerpc/include/asm/opal-api.h b/arch/powerpc/include/asm/opal-api.h |
| index 378e3997845a..c1f25a760eb1 100644 |
| --- a/arch/powerpc/include/asm/opal-api.h |
| +++ b/arch/powerpc/include/asm/opal-api.h |
| @@ -211,7 +211,10 @@ |
| #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 */ |
| diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h |
| index a0cf8fba4d12..9986ac34b8e2 100644 |
| --- a/arch/powerpc/include/asm/opal.h |
| +++ b/arch/powerpc/include/asm/opal.h |
| @@ -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 |
| index 000000000000..4cc35b58b986 |
| --- /dev/null |
| +++ b/arch/powerpc/include/asm/secvar.h |
| @@ -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 |
| diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile |
| index b82f7f5e5121..93b0336090f2 100644 |
| --- a/arch/powerpc/kernel/Makefile |
| +++ b/arch/powerpc/kernel/Makefile |
| @@ -158,7 +158,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 |
| index 000000000000..6a29777d6a2d |
| --- /dev/null |
| +++ b/arch/powerpc/kernel/secvar-ops.c |
| @@ -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; |
| +} |
| diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile |
| index a3ac9646119d..c0f8120045c3 100644 |
| --- a/arch/powerpc/platforms/powernv/Makefile |
| +++ b/arch/powerpc/platforms/powernv/Makefile |
| @@ -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 |
| diff --git a/arch/powerpc/platforms/powernv/opal-call.c b/arch/powerpc/platforms/powernv/opal-call.c |
| index a2aa5e433ac8..5cd0f52d258f 100644 |
| --- a/arch/powerpc/platforms/powernv/opal-call.c |
| +++ b/arch/powerpc/platforms/powernv/opal-call.c |
| @@ -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 |
| index 000000000000..14133e120bdd |
| --- /dev/null |
| +++ b/arch/powerpc/platforms/powernv/opal-secvar.c |
| @@ -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); |
| diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c |
| index 38e90270280b..8355bcd00f93 100644 |
| --- a/arch/powerpc/platforms/powernv/opal.c |
| +++ b/arch/powerpc/platforms/powernv/opal.c |
| @@ -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); |