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
This commit is contained in:
Nayna Jain 2019-11-10 21:10:33 -06:00 committed by Michael Ellerman
parent 39a963b457
commit 9155e2341a
9 changed files with 211 additions and 2 deletions

View File

@ -211,7 +211,10 @@
#define OPAL_MPIPL_UPDATE 173 #define OPAL_MPIPL_UPDATE 173
#define OPAL_MPIPL_REGISTER_TAG 174 #define OPAL_MPIPL_REGISTER_TAG 174
#define OPAL_MPIPL_QUERY_TAG 175 #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_HOLD 1 /* Spin all calls at entry */
#define QUIESCE_REJECT 2 /* Fail all calls with OPAL_BUSY */ #define QUIESCE_REJECT 2 /* Fail all calls with OPAL_BUSY */

View 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_sensor_group_enable(u32 group_hndl, int token, bool enable);
int opal_nx_coproc_init(uint32_t chip_id, uint32_t ct); 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_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_register_tag(enum opal_mpipl_tags tag, u64 addr);
s64 opal_mpipl_query_tag(enum opal_mpipl_tags tag, u64 *addr); s64 opal_mpipl_query_tag(enum opal_mpipl_tags tag, u64 *addr);

View File

@ -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

View File

@ -161,7 +161,7 @@ ifneq ($(CONFIG_PPC_POWERNV)$(CONFIG_PPC_SVM),)
obj-y += ucall.o obj-y += ucall.o
endif 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 # Disable GCOV, KCOV & sanitizers in odd or sensitive code
GCOV_PROFILE_prom_init.o := n GCOV_PROFILE_prom_init.o := n

View File

@ -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;
}

View 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_PPC_VAS) += vas.o vas-window.o vas-debug.o
obj-$(CONFIG_OCXL_BASE) += ocxl.o obj-$(CONFIG_OCXL_BASE) += ocxl.o
obj-$(CONFIG_SCOM_DEBUGFS) += opal-xscom.o obj-$(CONFIG_SCOM_DEBUGFS) += opal-xscom.o
obj-$(CONFIG_PPC_SECURE_BOOT) += opal-secvar.o

View 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_update, OPAL_MPIPL_UPDATE);
OPAL_CALL(opal_mpipl_register_tag, OPAL_MPIPL_REGISTER_TAG); OPAL_CALL(opal_mpipl_register_tag, OPAL_MPIPL_REGISTER_TAG);
OPAL_CALL(opal_mpipl_query_tag, OPAL_MPIPL_QUERY_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);

View File

@ -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);

View File

@ -1002,6 +1002,9 @@ static int __init opal_init(void)
/* Initialise OPAL Power control interface */ /* Initialise OPAL Power control interface */
opal_power_control_init(); opal_power_control_init();
/* Initialize OPAL secure variables */
opal_pdev_init("ibm,secvar-backend");
return 0; return 0;
} }
machine_subsys_initcall(powernv, opal_init); machine_subsys_initcall(powernv, opal_init);