[PATCH v2 1/3] powerpc/pseries: define driver for Platform KeyStore
Greg Joyce
gjoyce at linux.vnet.ibm.com
Fri Jul 29 00:14:51 AEST 2022
Reviewed-by: Greg Joyce <gjoyce at linux.vnet.ibm.com>
Tested-by: Greg Joyce <gjoyce at linux.vnet.ibm.com>
On Sat, 2022-07-23 at 07:30 -0400, Nayna Jain wrote:
> PowerVM provides an isolated Platform Keystore(PKS) storage
> allocation
> for each LPAR with individually managed access controls to store
> sensitive information securely. It provides a new set of hypervisor
> calls for Linux kernel to access PKS storage.
>
> Define POWER LPAR Platform KeyStore(PLPKS) driver using H_CALL
> interface
> to access PKS storage.
>
> Signed-off-by: Nayna Jain <nayna at linux.ibm.com>
> ---
> arch/powerpc/include/asm/hvcall.h | 11 +
> arch/powerpc/platforms/pseries/Kconfig | 13 +
> arch/powerpc/platforms/pseries/Makefile | 1 +
> arch/powerpc/platforms/pseries/plpks.c | 460
> ++++++++++++++++++++++++
> arch/powerpc/platforms/pseries/plpks.h | 71 ++++
> 5 files changed, 556 insertions(+)
> create mode 100644 arch/powerpc/platforms/pseries/plpks.c
> create mode 100644 arch/powerpc/platforms/pseries/plpks.h
>
> diff --git a/arch/powerpc/include/asm/hvcall.h
> b/arch/powerpc/include/asm/hvcall.h
> index d92a20a85395..9f707974af1a 100644
> --- a/arch/powerpc/include/asm/hvcall.h
> +++ b/arch/powerpc/include/asm/hvcall.h
> @@ -79,6 +79,7 @@
> #define H_NOT_ENOUGH_RESOURCES -44
> #define H_R_STATE -45
> #define H_RESCINDED -46
> +#define H_P1 -54
> #define H_P2 -55
> #define H_P3 -56
> #define H_P4 -57
> @@ -97,6 +98,8 @@
> #define H_OP_MODE -73
> #define H_COP_HW -74
> #define H_STATE -75
> +#define H_IN_USE -77
> +#define H_ABORTED -78
> #define H_UNSUPPORTED_FLAG_START -256
> #define H_UNSUPPORTED_FLAG_END -511
> #define H_MULTI_THREADS_ACTIVE -9005
> @@ -321,6 +324,14 @@
> #define H_SCM_UNBIND_ALL 0x3FC
> #define H_SCM_HEALTH 0x400
> #define H_SCM_PERFORMANCE_STATS 0x418
> +#define H_PKS_GET_CONFIG 0x41C
> +#define H_PKS_SET_PASSWORD 0x420
> +#define H_PKS_GEN_PASSWORD 0x424
> +#define H_PKS_WRITE_OBJECT 0x42C
> +#define H_PKS_GEN_KEY 0x430
> +#define H_PKS_READ_OBJECT 0x434
> +#define H_PKS_REMOVE_OBJECT 0x438
> +#define H_PKS_CONFIRM_OBJECT_FLUSHED 0x43C
> #define H_RPT_INVALIDATE 0x448
> #define H_SCM_FLUSH 0x44C
> #define H_GET_ENERGY_SCALE_INFO 0x450
> diff --git a/arch/powerpc/platforms/pseries/Kconfig
> b/arch/powerpc/platforms/pseries/Kconfig
> index f7fd91d153a4..c4a6d4083a7a 100644
> --- a/arch/powerpc/platforms/pseries/Kconfig
> +++ b/arch/powerpc/platforms/pseries/Kconfig
> @@ -142,6 +142,19 @@ config IBMEBUS
> help
> Bus device driver for GX bus based adapters.
>
> +config PSERIES_PLPKS
> + depends on PPC_PSERIES
> + bool "Support for the Platform Key Storage"
> + help
> + PowerVM provides an isolated Platform Keystore(PKS) storage
> + allocation for each LPAR with individually managed access
> + controls to store sensitive information securely. It can be
> + used to store asymmetric public keys or secrets as required
> + by different usecases. Select this config to enable
> + operating system interface to hypervisor to access this
> space.
> +
> + If unsure, select N.
> +
> config PAPR_SCM
> depends on PPC_PSERIES && MEMORY_HOTPLUG && LIBNVDIMM
> tristate "Support for the PAPR Storage Class Memory interface"
> diff --git a/arch/powerpc/platforms/pseries/Makefile
> b/arch/powerpc/platforms/pseries/Makefile
> index 7aaff5323544..14e143b946a3 100644
> --- a/arch/powerpc/platforms/pseries/Makefile
> +++ b/arch/powerpc/platforms/pseries/Makefile
> @@ -28,6 +28,7 @@ obj-$(CONFIG_PAPR_SCM) += papr_scm.o
> obj-$(CONFIG_PPC_SPLPAR) += vphn.o
> obj-$(CONFIG_PPC_SVM) += svm.o
> obj-$(CONFIG_FA_DUMP) += rtas-fadump.o
> +obj-$(CONFIG_PSERIES_PLPKS) += plpks.o
>
> obj-$(CONFIG_SUSPEND) += suspend.o
> obj-$(CONFIG_PPC_VAS) += vas.o vas-sysfs.o
> diff --git a/arch/powerpc/platforms/pseries/plpks.c
> b/arch/powerpc/platforms/pseries/plpks.c
> new file mode 100644
> index 000000000000..52aaa2894606
> --- /dev/null
> +++ b/arch/powerpc/platforms/pseries/plpks.c
> @@ -0,0 +1,460 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * POWER LPAR Platform KeyStore(PLPKS)
> + * Copyright (C) 2022 IBM Corporation
> + * Author: Nayna Jain <nayna at linux.ibm.com>
> + *
> + * Provides access to variables stored in Power LPAR Platform
> KeyStore(PLPKS).
> + */
> +
> +#define pr_fmt(fmt) "plpks: " fmt
> +
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/io.h>
> +#include <linux/printk.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +#include <linux/types.h>
> +#include <asm/hvcall.h>
> +
> +#include "plpks.h"
> +
> +#define PKS_FW_OWNER 0x1
> +#define PKS_BOOTLOADER_OWNER 0x2
> +#define PKS_OS_OWNER 0x3
> +
> +#define LABEL_VERSION 0
> +#define MAX_LABEL_ATTR_SIZE 16
> +#define MAX_NAME_SIZE 239
> +#define MAX_DATA_SIZE 4000
> +
> +#define PKS_FLUSH_MAX_TIMEOUT 5000 //msec
> +#define PKS_FLUSH_SLEEP 10 //msec
> +#define PKS_FLUSH_SLEEP_RANGE 400
> +
> +static u8 *ospassword;
> +static u16 ospasswordlength;
> +
> +// Retrieved with H_PKS_GET_CONFIG
> +static u16 maxpwsize;
> +static u16 maxobjsize;
> +
> +struct plpks_auth {
> + u8 version;
> + u8 consumer;
> + __be64 rsvd0;
> + __be32 rsvd1;
> + __be16 passwordlength;
> + u8 password[];
> +} __packed __aligned(16);
> +
> +struct label_attr {
> + u8 prefix[8];
> + u8 version;
> + u8 os;
> + u8 length;
> + u8 reserved[5];
> +};
> +
> +struct label {
> + struct label_attr attr;
> + u8 name[MAX_NAME_SIZE];
> + size_t size;
> +};
> +
> +static int pseries_status_to_err(int rc)
> +{
> + int err;
> +
> + switch (rc) {
> + case H_SUCCESS:
> + err = 0;
> + break;
> + case H_FUNCTION:
> + err = -ENXIO;
> + break;
> + case H_P1:
> + case H_P2:
> + case H_P3:
> + case H_P4:
> + case H_P5:
> + case H_P6:
> + err = -EINVAL;
> + break;
> + case H_NOT_FOUND:
> + err = -ENOENT;
> + break;
> + case H_BUSY:
> + err = -EBUSY;
> + break;
> + case H_AUTHORITY:
> + err = -EPERM;
> + break;
> + case H_NO_MEM:
> + err = -ENOMEM;
> + break;
> + case H_RESOURCE:
> + err = -EEXIST;
> + break;
> + case H_TOO_BIG:
> + err = -EFBIG;
> + break;
> + case H_STATE:
> + err = -EIO;
> + break;
> + case H_R_STATE:
> + err = -EIO;
> + break;
> + case H_IN_USE:
> + err = -EEXIST;
> + break;
> + case H_ABORTED:
> + err = -EINTR;
> + break;
> + default:
> + err = -EINVAL;
> + }
> +
> + return err;
> +}
> +
> +static int plpks_gen_password(void)
> +{
> + unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 };
> + u8 *password, consumer = PKS_OS_OWNER;
> + int rc;
> +
> + password = kzalloc(maxpwsize, GFP_KERNEL);
> + if (!password)
> + return -ENOMEM;
> +
> + rc = plpar_hcall(H_PKS_GEN_PASSWORD, retbuf, consumer, 0,
> + virt_to_phys(password), maxpwsize);
> +
> + if (!rc) {
> + ospasswordlength = maxpwsize;
> + ospassword = kzalloc(maxpwsize, GFP_KERNEL);
> + if (!ospassword) {
> + kfree(password);
> + return -ENOMEM;
> + }
> + memcpy(ospassword, password, ospasswordlength);
> + } else {
> + if (rc == H_IN_USE) {
> + pr_warn("Password is already set for POWER LPAR
> Platform KeyStore\n");
> + rc = 0;
> + } else {
> + goto out;
> + }
> + }
> +out:
> + kfree(password);
> +
> + return pseries_status_to_err(rc);
> +}
> +
> +static struct plpks_auth *construct_auth(u8 consumer)
> +{
> + struct plpks_auth *auth;
> +
> + if (consumer > PKS_OS_OWNER)
> + return ERR_PTR(-EINVAL);
> +
> + auth = kmalloc(struct_size(auth, password, maxpwsize),
> GFP_KERNEL);
> + if (!auth)
> + return ERR_PTR(-ENOMEM);
> +
> + auth->version = 1;
> + auth->consumer = consumer;
> + auth->rsvd0 = 0;
> + auth->rsvd1 = 0;
> +
> + if (consumer == PKS_FW_OWNER || consumer ==
> PKS_BOOTLOADER_OWNER) {
> + auth->passwordlength = 0;
> + return auth;
> + }
> +
> + memcpy(auth->password, ospassword, ospasswordlength);
> +
> + auth->passwordlength = cpu_to_be16(ospasswordlength);
> +
> + return auth;
> +}
> +
> +/**
> + * Label is combination of label attributes + name.
> + * Label attributes are used internally by kernel and not exposed to
> the user.
> + */
> +static struct label *construct_label(char *component, u8 varos, u8
> *name,
> + u16 namelen)
> +{
> + struct label *label;
> + size_t slen;
> +
> + if (!name || namelen > MAX_NAME_SIZE)
> + return ERR_PTR(-EINVAL);
> +
> + slen = strlen(component);
> + if (component && slen > sizeof(label->attr.prefix))
> + return ERR_PTR(-EINVAL);
> +
> + label = kzalloc(sizeof(*label), GFP_KERNEL);
> + if (!label)
> + return ERR_PTR(-ENOMEM);
> +
> + if (component)
> + memcpy(&label->attr.prefix, component, slen);
> +
> + label->attr.version = LABEL_VERSION;
> + label->attr.os = varos;
> + label->attr.length = MAX_LABEL_ATTR_SIZE;
> + memcpy(&label->name, name, namelen);
> +
> + label->size = sizeof(struct label_attr) + namelen;
> +
> + return label;
> +}
> +
> +static int _plpks_get_config(void)
> +{
> + unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 };
> + struct {
> + u8 version;
> + u8 flags;
> + __be32 rsvd0;
> + __be16 maxpwsize;
> + __be16 maxobjlabelsize;
> + __be16 maxobjsize;
> + __be32 totalsize;
> + __be32 usedspace;
> + __be32 supportedpolicies;
> + __be64 rsvd1;
> + } __packed config;
> + size_t size;
> + int rc;
> +
> + size = sizeof(config);
> +
> + rc = plpar_hcall(H_PKS_GET_CONFIG, retbuf,
> virt_to_phys(&config), size);
> +
> + if (rc != H_SUCCESS)
> + return pseries_status_to_err(rc);
> +
> + maxpwsize = be16_to_cpu(config.maxpwsize);
> + maxobjsize = be16_to_cpu(config.maxobjsize);
> +
> + return 0;
> +}
> +
> +static int plpks_confirm_object_flushed(struct label *label,
> + struct plpks_auth *auth)
> +{
> + unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 };
> + u64 timeout = 0;
> + u8 status;
> + int rc;
> +
> + do {
> + rc = plpar_hcall(H_PKS_CONFIRM_OBJECT_FLUSHED, retbuf,
> + virt_to_phys(auth),
> virt_to_phys(label),
> + label->size);
> +
> + status = retbuf[0];
> + if (rc) {
> + if (rc == H_NOT_FOUND && status == 1)
> + rc = 0;
> + break;
> + }
> +
> + if (!rc && status == 1)
> + break;
> +
> + usleep_range(PKS_FLUSH_SLEEP,
> + PKS_FLUSH_SLEEP + PKS_FLUSH_SLEEP_RANGE);
> + timeout = timeout + PKS_FLUSH_SLEEP;
> + } while (timeout < PKS_FLUSH_MAX_TIMEOUT);
> +
> + rc = pseries_status_to_err(rc);
> +
> + return rc;
> +}
> +
> +int plpks_write_var(struct plpks_var var)
> +{
> + unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 };
> + struct plpks_auth *auth;
> + struct label *label;
> + int rc;
> +
> + if (!var.component || !var.data || var.datalen <= 0 ||
> + var.namelen > MAX_NAME_SIZE || var.datalen > MAX_DATA_SIZE)
> + return -EINVAL;
> +
> + if (var.policy & SIGNEDUPDATE)
> + return -EINVAL;
> +
> + auth = construct_auth(PKS_OS_OWNER);
> + if (IS_ERR(auth))
> + return PTR_ERR(auth);
> +
> + label = construct_label(var.component, var.os, var.name,
> var.namelen);
> + if (IS_ERR(label)) {
> + rc = PTR_ERR(label);
> + goto out;
> + }
> +
> + rc = plpar_hcall(H_PKS_WRITE_OBJECT, retbuf,
> virt_to_phys(auth),
> + virt_to_phys(label), label->size, var.policy,
> + virt_to_phys(var.data), var.datalen);
> +
> + if (!rc)
> + rc = plpks_confirm_object_flushed(label, auth);
> +
> + if (rc)
> + pr_err("Failed to write variable %s for component %s
> with error %d\n",
> + var.name, var.component, rc);
> +
> + rc = pseries_status_to_err(rc);
> + kfree(label);
> +out:
> + kfree(auth);
> +
> + return rc;
> +}
> +
> +int plpks_remove_var(char *component, u8 varos, struct
> plpks_var_name vname)
> +{
> + unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 };
> + struct plpks_auth *auth;
> + struct label *label;
> + int rc;
> +
> + if (!component || vname.namelen > MAX_NAME_SIZE)
> + return -EINVAL;
> +
> + auth = construct_auth(PKS_OS_OWNER);
> + if (IS_ERR(auth))
> + return PTR_ERR(auth);
> +
> + label = construct_label(component, varos, vname.name,
> vname.namelen);
> + if (IS_ERR(label)) {
> + rc = PTR_ERR(label);
> + goto out;
> + }
> +
> + rc = plpar_hcall(H_PKS_REMOVE_OBJECT, retbuf,
> virt_to_phys(auth),
> + virt_to_phys(label), label->size);
> +
> + if (!rc)
> + rc = plpks_confirm_object_flushed(label, auth);
> +
> + if (rc)
> + pr_err("Failed to remove variable %s for component %s
> with error %d\n",
> + vname.name, component, rc);
> +
> + rc = pseries_status_to_err(rc);
> + kfree(label);
> +out:
> + kfree(auth);
> +
> + return rc;
> +}
> +
> +static int plpks_read_var(u8 consumer, struct plpks_var *var)
> +{
> + unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 };
> + struct plpks_auth *auth;
> + struct label *label;
> + u8 *output;
> + int rc;
> +
> + if (var->namelen > MAX_NAME_SIZE)
> + return -EINVAL;
> +
> + auth = construct_auth(PKS_OS_OWNER);
> + if (IS_ERR(auth))
> + return PTR_ERR(auth);
> +
> + label = construct_label(var->component, var->os, var->name,
> + var->namelen);
> + if (IS_ERR(label)) {
> + rc = PTR_ERR(label);
> + goto out_free_auth;
> + }
> +
> + output = kzalloc(maxobjsize, GFP_KERNEL);
> + if (!output) {
> + rc = -ENOMEM;
> + goto out_free_label;
> + }
> +
> + rc = plpar_hcall(H_PKS_READ_OBJECT, retbuf, virt_to_phys(auth),
> + virt_to_phys(label), label->size,
> virt_to_phys(output),
> + maxobjsize);
> +
> + if (rc != H_SUCCESS) {
> + pr_err("Failed to read variable %s for component %s
> with error %d\n",
> + var->name, var->component, rc);
> + rc = pseries_status_to_err(rc);
> + goto out_free_output;
> + }
> +
> + if (var->datalen == 0 || var->datalen > retbuf[0])
> + var->datalen = retbuf[0];
> +
> + var->data = kzalloc(var->datalen, GFP_KERNEL);
> + if (!var->data) {
> + rc = -ENOMEM;
> + goto out_free_output;
> + }
> + var->policy = retbuf[1];
> +
> + memcpy(var->data, output, var->datalen);
> + rc = 0;
> +
> +out_free_output:
> + kfree(output);
> +out_free_label:
> + kfree(label);
> +out_free_auth:
> + kfree(auth);
> +
> + return rc;
> +}
> +
> +int plpks_read_os_var(struct plpks_var *var)
> +{
> + return plpks_read_var(PKS_OS_OWNER, var);
> +}
> +
> +int plpks_read_fw_var(struct plpks_var *var)
> +{
> + return plpks_read_var(PKS_FW_OWNER, var);
> +}
> +
> +int plpks_read_bootloader_var(struct plpks_var *var)
> +{
> + return plpks_read_var(PKS_BOOTLOADER_OWNER, var);
> +}
> +
> +static __init int pseries_plpks_init(void)
> +{
> + int rc;
> +
> + rc = _plpks_get_config();
> +
> + if (rc) {
> + pr_err("POWER LPAR Platform KeyStore is not supported
> or enabled\n");
> + return rc;
> + }
> +
> + rc = plpks_gen_password();
> + if (rc)
> + pr_err("Failed setting POWER LPAR Platform KeyStore
> Password\n");
> + else
> + pr_info("POWER LPAR Platform KeyStore initialized
> successfully\n");
> +
> + return rc;
> +}
> +arch_initcall(pseries_plpks_init);
> diff --git a/arch/powerpc/platforms/pseries/plpks.h
> b/arch/powerpc/platforms/pseries/plpks.h
> new file mode 100644
> index 000000000000..c6a291367bb1
> --- /dev/null
> +++ b/arch/powerpc/platforms/pseries/plpks.h
> @@ -0,0 +1,71 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2022 IBM Corporation
> + * Author: Nayna Jain <nayna at linux.ibm.com>
> + *
> + * Platform keystore for pseries LPAR(PLPKS).
> + */
> +
> +#ifndef _PSERIES_PLPKS_H
> +#define _PSERIES_PLPKS_H
> +
> +#include <linux/types.h>
> +#include <linux/list.h>
> +
> +#define OSSECBOOTAUDIT 0x40000000
> +#define OSSECBOOTENFORCE 0x20000000
> +#define WORLDREADABLE 0x08000000
> +#define SIGNEDUPDATE 0x01000000
> +
> +#define PLPKS_VAR_LINUX 0x01
> +#define PLPKS_VAR_COMMON 0x04
> +
> +struct plpks_var {
> + char *component;
> + u8 *name;
> + u8 *data;
> + u32 policy;
> + u16 namelen;
> + u16 datalen;
> + u8 os;
> +};
> +
> +struct plpks_var_name {
> + u8 *name;
> + u16 namelen;
> +};
> +
> +struct plpks_var_name_list {
> + u32 varcount;
> + struct plpks_var_name varlist[];
> +};
> +
> +/**
> + * Writes the specified var and its data to PKS.
> + * Any caller of PKS driver should present a valid component type
> for
> + * their variable.
> + */
> +int plpks_write_var(struct plpks_var var);
> +
> +/**
> + * Removes the specified var and its data from PKS.
> + */
> +int plpks_remove_var(char *component, u8 varos,
> + struct plpks_var_name vname);
> +
> +/**
> + * Returns the data for the specified os variable.
> + */
> +int plpks_read_os_var(struct plpks_var *var);
> +
> +/**
> + * Returns the data for the specified firmware variable.
> + */
> +int plpks_read_fw_var(struct plpks_var *var);
> +
> +/**
> + * Returns the data for the specified bootloader variable.
> + */
> +int plpks_read_bootloader_var(struct plpks_var *var);
> +
> +#endif
More information about the Linuxppc-dev
mailing list