Joel Stanley | a1fccbf | 2020-06-23 17:25:56 +0930 | [diff] [blame] | 1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| 2 | From: Nayna Jain <nayna@linux.ibm.com> |
| 3 | Date: Sun, 10 Nov 2019 21:10:33 -0600 |
Joel Stanley | cb9bf57 | 2020-09-29 16:18:12 +0930 | [diff] [blame] | 4 | Subject: [PATCH 10/19] powerpc/powernv: Add OPAL API interface to access |
Joel Stanley | a1fccbf | 2020-06-23 17:25:56 +0930 | [diff] [blame] | 5 | secure variable |
| 6 | |
| 7 | The X.509 certificates trusted by the platform and required to secure |
| 8 | boot the OS kernel are wrapped in secure variables, which are |
| 9 | controlled by OPAL. |
| 10 | |
| 11 | This patch adds firmware/kernel interface to read and write OPAL |
| 12 | secure variables based on the unique key. |
| 13 | |
| 14 | This support can be enabled using CONFIG_OPAL_SECVAR. |
| 15 | |
| 16 | Signed-off-by: Claudio Carvalho <cclaudio@linux.ibm.com> |
| 17 | Signed-off-by: Nayna Jain <nayna@linux.ibm.com> |
| 18 | Signed-off-by: Eric Richter <erichte@linux.ibm.com> |
| 19 | [mpe: Make secvar_ops __ro_after_init, only build opal-secvar.c if PPC_SECURE_BOOT=y] |
| 20 | Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> |
| 21 | Link: https://lore.kernel.org/r/1573441836-3632-2-git-send-email-nayna@linux.ibm.com |
| 22 | (cherry picked from commit 9155e2341aa8b5df057dc1c77633b33d1a4f17d2) |
| 23 | Signed-off-by: Joel Stanley <joel@jms.id.au> |
| 24 | --- |
| 25 | arch/powerpc/include/asm/opal-api.h | 5 +- |
| 26 | arch/powerpc/include/asm/opal.h | 7 + |
| 27 | arch/powerpc/include/asm/secvar.h | 35 +++++ |
| 28 | arch/powerpc/kernel/Makefile | 2 +- |
| 29 | arch/powerpc/kernel/secvar-ops.c | 17 +++ |
| 30 | arch/powerpc/platforms/powernv/Makefile | 1 + |
| 31 | arch/powerpc/platforms/powernv/opal-call.c | 3 + |
| 32 | arch/powerpc/platforms/powernv/opal-secvar.c | 140 +++++++++++++++++++ |
| 33 | arch/powerpc/platforms/powernv/opal.c | 3 + |
| 34 | 9 files changed, 211 insertions(+), 2 deletions(-) |
| 35 | create mode 100644 arch/powerpc/include/asm/secvar.h |
| 36 | create mode 100644 arch/powerpc/kernel/secvar-ops.c |
| 37 | create mode 100644 arch/powerpc/platforms/powernv/opal-secvar.c |
| 38 | |
| 39 | diff --git a/arch/powerpc/include/asm/opal-api.h b/arch/powerpc/include/asm/opal-api.h |
| 40 | index 378e3997845a..c1f25a760eb1 100644 |
| 41 | --- a/arch/powerpc/include/asm/opal-api.h |
| 42 | +++ b/arch/powerpc/include/asm/opal-api.h |
| 43 | @@ -211,7 +211,10 @@ |
| 44 | #define OPAL_MPIPL_UPDATE 173 |
| 45 | #define OPAL_MPIPL_REGISTER_TAG 174 |
| 46 | #define OPAL_MPIPL_QUERY_TAG 175 |
| 47 | -#define OPAL_LAST 175 |
| 48 | +#define OPAL_SECVAR_GET 176 |
| 49 | +#define OPAL_SECVAR_GET_NEXT 177 |
| 50 | +#define OPAL_SECVAR_ENQUEUE_UPDATE 178 |
| 51 | +#define OPAL_LAST 178 |
| 52 | |
| 53 | #define QUIESCE_HOLD 1 /* Spin all calls at entry */ |
| 54 | #define QUIESCE_REJECT 2 /* Fail all calls with OPAL_BUSY */ |
| 55 | diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h |
| 56 | index a0cf8fba4d12..9986ac34b8e2 100644 |
| 57 | --- a/arch/powerpc/include/asm/opal.h |
| 58 | +++ b/arch/powerpc/include/asm/opal.h |
| 59 | @@ -298,6 +298,13 @@ int opal_sensor_group_clear(u32 group_hndl, int token); |
| 60 | int opal_sensor_group_enable(u32 group_hndl, int token, bool enable); |
| 61 | int opal_nx_coproc_init(uint32_t chip_id, uint32_t ct); |
| 62 | |
| 63 | +int opal_secvar_get(const char *key, uint64_t key_len, u8 *data, |
| 64 | + uint64_t *data_size); |
| 65 | +int opal_secvar_get_next(const char *key, uint64_t *key_len, |
| 66 | + uint64_t key_buf_size); |
| 67 | +int opal_secvar_enqueue_update(const char *key, uint64_t key_len, u8 *data, |
| 68 | + uint64_t data_size); |
| 69 | + |
| 70 | s64 opal_mpipl_update(enum opal_mpipl_ops op, u64 src, u64 dest, u64 size); |
| 71 | s64 opal_mpipl_register_tag(enum opal_mpipl_tags tag, u64 addr); |
| 72 | s64 opal_mpipl_query_tag(enum opal_mpipl_tags tag, u64 *addr); |
| 73 | diff --git a/arch/powerpc/include/asm/secvar.h b/arch/powerpc/include/asm/secvar.h |
| 74 | new file mode 100644 |
| 75 | index 000000000000..4cc35b58b986 |
| 76 | --- /dev/null |
| 77 | +++ b/arch/powerpc/include/asm/secvar.h |
| 78 | @@ -0,0 +1,35 @@ |
| 79 | +/* SPDX-License-Identifier: GPL-2.0 */ |
| 80 | +/* |
| 81 | + * Copyright (C) 2019 IBM Corporation |
| 82 | + * Author: Nayna Jain |
| 83 | + * |
| 84 | + * PowerPC secure variable operations. |
| 85 | + */ |
| 86 | +#ifndef SECVAR_OPS_H |
| 87 | +#define SECVAR_OPS_H |
| 88 | + |
| 89 | +#include <linux/types.h> |
| 90 | +#include <linux/errno.h> |
| 91 | + |
| 92 | +extern const struct secvar_operations *secvar_ops; |
| 93 | + |
| 94 | +struct secvar_operations { |
| 95 | + int (*get)(const char *key, uint64_t key_len, u8 *data, |
| 96 | + uint64_t *data_size); |
| 97 | + int (*get_next)(const char *key, uint64_t *key_len, |
| 98 | + uint64_t keybufsize); |
| 99 | + int (*set)(const char *key, uint64_t key_len, u8 *data, |
| 100 | + uint64_t data_size); |
| 101 | +}; |
| 102 | + |
| 103 | +#ifdef CONFIG_PPC_SECURE_BOOT |
| 104 | + |
| 105 | +extern void set_secvar_ops(const struct secvar_operations *ops); |
| 106 | + |
| 107 | +#else |
| 108 | + |
| 109 | +static inline void set_secvar_ops(const struct secvar_operations *ops) { } |
| 110 | + |
| 111 | +#endif |
| 112 | + |
| 113 | +#endif |
| 114 | diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile |
| 115 | index b82f7f5e5121..93b0336090f2 100644 |
| 116 | --- a/arch/powerpc/kernel/Makefile |
| 117 | +++ b/arch/powerpc/kernel/Makefile |
| 118 | @@ -158,7 +158,7 @@ ifneq ($(CONFIG_PPC_POWERNV)$(CONFIG_PPC_SVM),) |
| 119 | obj-y += ucall.o |
| 120 | endif |
| 121 | |
| 122 | -obj-$(CONFIG_PPC_SECURE_BOOT) += secure_boot.o ima_arch.o |
| 123 | +obj-$(CONFIG_PPC_SECURE_BOOT) += secure_boot.o ima_arch.o secvar-ops.o |
| 124 | |
| 125 | # Disable GCOV, KCOV & sanitizers in odd or sensitive code |
| 126 | GCOV_PROFILE_prom_init.o := n |
| 127 | diff --git a/arch/powerpc/kernel/secvar-ops.c b/arch/powerpc/kernel/secvar-ops.c |
| 128 | new file mode 100644 |
| 129 | index 000000000000..6a29777d6a2d |
| 130 | --- /dev/null |
| 131 | +++ b/arch/powerpc/kernel/secvar-ops.c |
| 132 | @@ -0,0 +1,17 @@ |
| 133 | +// SPDX-License-Identifier: GPL-2.0 |
| 134 | +/* |
| 135 | + * Copyright (C) 2019 IBM Corporation |
| 136 | + * Author: Nayna Jain |
| 137 | + * |
| 138 | + * This file initializes secvar operations for PowerPC Secureboot |
| 139 | + */ |
| 140 | + |
| 141 | +#include <linux/cache.h> |
| 142 | +#include <asm/secvar.h> |
| 143 | + |
| 144 | +const struct secvar_operations *secvar_ops __ro_after_init; |
| 145 | + |
| 146 | +void set_secvar_ops(const struct secvar_operations *ops) |
| 147 | +{ |
| 148 | + secvar_ops = ops; |
| 149 | +} |
| 150 | diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile |
| 151 | index a3ac9646119d..c0f8120045c3 100644 |
| 152 | --- a/arch/powerpc/platforms/powernv/Makefile |
| 153 | +++ b/arch/powerpc/platforms/powernv/Makefile |
| 154 | @@ -20,3 +20,4 @@ obj-$(CONFIG_PPC_MEMTRACE) += memtrace.o |
| 155 | obj-$(CONFIG_PPC_VAS) += vas.o vas-window.o vas-debug.o |
| 156 | obj-$(CONFIG_OCXL_BASE) += ocxl.o |
| 157 | obj-$(CONFIG_SCOM_DEBUGFS) += opal-xscom.o |
| 158 | +obj-$(CONFIG_PPC_SECURE_BOOT) += opal-secvar.o |
| 159 | diff --git a/arch/powerpc/platforms/powernv/opal-call.c b/arch/powerpc/platforms/powernv/opal-call.c |
| 160 | index a2aa5e433ac8..5cd0f52d258f 100644 |
| 161 | --- a/arch/powerpc/platforms/powernv/opal-call.c |
| 162 | +++ b/arch/powerpc/platforms/powernv/opal-call.c |
| 163 | @@ -290,3 +290,6 @@ OPAL_CALL(opal_nx_coproc_init, OPAL_NX_COPROC_INIT); |
| 164 | OPAL_CALL(opal_mpipl_update, OPAL_MPIPL_UPDATE); |
| 165 | OPAL_CALL(opal_mpipl_register_tag, OPAL_MPIPL_REGISTER_TAG); |
| 166 | OPAL_CALL(opal_mpipl_query_tag, OPAL_MPIPL_QUERY_TAG); |
| 167 | +OPAL_CALL(opal_secvar_get, OPAL_SECVAR_GET); |
| 168 | +OPAL_CALL(opal_secvar_get_next, OPAL_SECVAR_GET_NEXT); |
| 169 | +OPAL_CALL(opal_secvar_enqueue_update, OPAL_SECVAR_ENQUEUE_UPDATE); |
| 170 | diff --git a/arch/powerpc/platforms/powernv/opal-secvar.c b/arch/powerpc/platforms/powernv/opal-secvar.c |
| 171 | new file mode 100644 |
| 172 | index 000000000000..14133e120bdd |
| 173 | --- /dev/null |
| 174 | +++ b/arch/powerpc/platforms/powernv/opal-secvar.c |
| 175 | @@ -0,0 +1,140 @@ |
| 176 | +// SPDX-License-Identifier: GPL-2.0 |
| 177 | +/* |
| 178 | + * PowerNV code for secure variables |
| 179 | + * |
| 180 | + * Copyright (C) 2019 IBM Corporation |
| 181 | + * Author: Claudio Carvalho |
| 182 | + * Nayna Jain |
| 183 | + * |
| 184 | + * APIs to access secure variables managed by OPAL. |
| 185 | + */ |
| 186 | + |
| 187 | +#define pr_fmt(fmt) "secvar: "fmt |
| 188 | + |
| 189 | +#include <linux/types.h> |
| 190 | +#include <linux/platform_device.h> |
| 191 | +#include <linux/of_platform.h> |
| 192 | +#include <asm/opal.h> |
| 193 | +#include <asm/secvar.h> |
| 194 | +#include <asm/secure_boot.h> |
| 195 | + |
| 196 | +static int opal_status_to_err(int rc) |
| 197 | +{ |
| 198 | + int err; |
| 199 | + |
| 200 | + switch (rc) { |
| 201 | + case OPAL_SUCCESS: |
| 202 | + err = 0; |
| 203 | + break; |
| 204 | + case OPAL_UNSUPPORTED: |
| 205 | + err = -ENXIO; |
| 206 | + break; |
| 207 | + case OPAL_PARAMETER: |
| 208 | + err = -EINVAL; |
| 209 | + break; |
| 210 | + case OPAL_RESOURCE: |
| 211 | + err = -ENOSPC; |
| 212 | + break; |
| 213 | + case OPAL_HARDWARE: |
| 214 | + err = -EIO; |
| 215 | + break; |
| 216 | + case OPAL_NO_MEM: |
| 217 | + err = -ENOMEM; |
| 218 | + break; |
| 219 | + case OPAL_EMPTY: |
| 220 | + err = -ENOENT; |
| 221 | + break; |
| 222 | + case OPAL_PARTIAL: |
| 223 | + err = -EFBIG; |
| 224 | + break; |
| 225 | + default: |
| 226 | + err = -EINVAL; |
| 227 | + } |
| 228 | + |
| 229 | + return err; |
| 230 | +} |
| 231 | + |
| 232 | +static int opal_get_variable(const char *key, uint64_t ksize, |
| 233 | + u8 *data, uint64_t *dsize) |
| 234 | +{ |
| 235 | + int rc; |
| 236 | + |
| 237 | + if (!key || !dsize) |
| 238 | + return -EINVAL; |
| 239 | + |
| 240 | + *dsize = cpu_to_be64(*dsize); |
| 241 | + |
| 242 | + rc = opal_secvar_get(key, ksize, data, dsize); |
| 243 | + |
| 244 | + *dsize = be64_to_cpu(*dsize); |
| 245 | + |
| 246 | + return opal_status_to_err(rc); |
| 247 | +} |
| 248 | + |
| 249 | +static int opal_get_next_variable(const char *key, uint64_t *keylen, |
| 250 | + uint64_t keybufsize) |
| 251 | +{ |
| 252 | + int rc; |
| 253 | + |
| 254 | + if (!key || !keylen) |
| 255 | + return -EINVAL; |
| 256 | + |
| 257 | + *keylen = cpu_to_be64(*keylen); |
| 258 | + |
| 259 | + rc = opal_secvar_get_next(key, keylen, keybufsize); |
| 260 | + |
| 261 | + *keylen = be64_to_cpu(*keylen); |
| 262 | + |
| 263 | + return opal_status_to_err(rc); |
| 264 | +} |
| 265 | + |
| 266 | +static int opal_set_variable(const char *key, uint64_t ksize, u8 *data, |
| 267 | + uint64_t dsize) |
| 268 | +{ |
| 269 | + int rc; |
| 270 | + |
| 271 | + if (!key || !data) |
| 272 | + return -EINVAL; |
| 273 | + |
| 274 | + rc = opal_secvar_enqueue_update(key, ksize, data, dsize); |
| 275 | + |
| 276 | + return opal_status_to_err(rc); |
| 277 | +} |
| 278 | + |
| 279 | +static const struct secvar_operations opal_secvar_ops = { |
| 280 | + .get = opal_get_variable, |
| 281 | + .get_next = opal_get_next_variable, |
| 282 | + .set = opal_set_variable, |
| 283 | +}; |
| 284 | + |
| 285 | +static int opal_secvar_probe(struct platform_device *pdev) |
| 286 | +{ |
| 287 | + if (!opal_check_token(OPAL_SECVAR_GET) |
| 288 | + || !opal_check_token(OPAL_SECVAR_GET_NEXT) |
| 289 | + || !opal_check_token(OPAL_SECVAR_ENQUEUE_UPDATE)) { |
| 290 | + pr_err("OPAL doesn't support secure variables\n"); |
| 291 | + return -ENODEV; |
| 292 | + } |
| 293 | + |
| 294 | + set_secvar_ops(&opal_secvar_ops); |
| 295 | + |
| 296 | + return 0; |
| 297 | +} |
| 298 | + |
| 299 | +static const struct of_device_id opal_secvar_match[] = { |
| 300 | + { .compatible = "ibm,secvar-backend",}, |
| 301 | + {}, |
| 302 | +}; |
| 303 | + |
| 304 | +static struct platform_driver opal_secvar_driver = { |
| 305 | + .driver = { |
| 306 | + .name = "secvar", |
| 307 | + .of_match_table = opal_secvar_match, |
| 308 | + }, |
| 309 | +}; |
| 310 | + |
| 311 | +static int __init opal_secvar_init(void) |
| 312 | +{ |
| 313 | + return platform_driver_probe(&opal_secvar_driver, opal_secvar_probe); |
| 314 | +} |
| 315 | +device_initcall(opal_secvar_init); |
| 316 | diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c |
| 317 | index 38e90270280b..8355bcd00f93 100644 |
| 318 | --- a/arch/powerpc/platforms/powernv/opal.c |
| 319 | +++ b/arch/powerpc/platforms/powernv/opal.c |
| 320 | @@ -1002,6 +1002,9 @@ static int __init opal_init(void) |
| 321 | /* Initialise OPAL Power control interface */ |
| 322 | opal_power_control_init(); |
| 323 | |
| 324 | + /* Initialize OPAL secure variables */ |
| 325 | + opal_pdev_init("ibm,secvar-backend"); |
| 326 | + |
| 327 | return 0; |
| 328 | } |
| 329 | machine_subsys_initcall(powernv, opal_init); |