linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] Provide PowerVM LPAR Platform KeyStore driver for Self Encrypting Drives
@ 2022-07-13  0:59 Nayna Jain
  2022-07-13  0:59 ` [PATCH 1/2] powerpc/pseries: define driver for Platform KeyStore Nayna Jain
  2022-07-13  0:59 ` [PATCH 2/2] powerpc/pseries: kernel interfaces to PLPKS platform driver Nayna Jain
  0 siblings, 2 replies; 7+ messages in thread
From: Nayna Jain @ 2022-07-13  0:59 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Nayna Jain, Paul Mackerras, George Wilson, gjoyce

PowerVM provides an isolated Platform KeyStore(PKS)[1] storage allocation
for each partition(LPAR) with individually managed access controls to store
sensitive information securely. The Linux Kernel can access this storage by
interfacing with the hypervisor using a new set of hypervisor calls. 

This storage can be used for multiple purposes. The current two usecases
are:

1. Guest Secure Boot on PowerVM[2]
2. Self Encrypting Drives(SED) on PowerVM[3]

Initially, the PowerVM LPAR Platform KeyStore(PLPKS) driver was defined
as part of RFC patches which included the user interface design for guest
secure boot[2]. While this interface is still in progress, the same driver
is also required for Self Encrypting Drives(SED) support. For this reason,
the driver is being split from the patchset[1] and is now separately posted
with SED arch-specific code.

This patchset provides driver for PowerVM LPAR Platform KeyStore and also
arch-specific code for SED to make use of it.

The patch series[3] is pre-requisite to build Patch 2/2. The PLPKS
driver can be built of its own.

[1]https://community.ibm.com/community/user/power/blogs/chris-engel1/2020/11/20/powervm-introduces-the-platform-keystore
[2]https://lore.kernel.org/linuxppc-dev/20220622215648.96723-1-nayna@linux.ibm.com/
[3]https://lore.kernel.org/keyrings/20220706023935.875994-1-gjoyce@linux.vnet.ibm.com/T/#mc32b51991bf825ec6f90af010998ec7cd2b9624a

Greg Joyce (1):
  powerpc/pseries: kernel interfaces to PLPKS platform driver

Nayna Jain (1):
  powerpc/pseries: define driver for Platform KeyStore

 arch/powerpc/include/asm/hvcall.h             |   9 +
 arch/powerpc/include/asm/plpks.h              |  90 ++++
 arch/powerpc/platforms/pseries/Kconfig        |  13 +
 arch/powerpc/platforms/pseries/Makefile       |   2 +
 arch/powerpc/platforms/pseries/plpks/Makefile |   8 +
 arch/powerpc/platforms/pseries/plpks/plpks.c  | 509 ++++++++++++++++++
 .../platforms/pseries/plpks/plpks_arch_ops.c  | 163 ++++++
 7 files changed, 794 insertions(+)
 create mode 100644 arch/powerpc/include/asm/plpks.h
 create mode 100644 arch/powerpc/platforms/pseries/plpks/Makefile
 create mode 100644 arch/powerpc/platforms/pseries/plpks/plpks.c
 create mode 100644 arch/powerpc/platforms/pseries/plpks/plpks_arch_ops.c

-- 
2.27.0

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/2] powerpc/pseries: define driver for Platform KeyStore
  2022-07-13  0:59 [PATCH 0/2] Provide PowerVM LPAR Platform KeyStore driver for Self Encrypting Drives Nayna Jain
@ 2022-07-13  0:59 ` Nayna Jain
  2022-07-18 20:58   ` Gregory Joyce
                     ` (3 more replies)
  2022-07-13  0:59 ` [PATCH 2/2] powerpc/pseries: kernel interfaces to PLPKS platform driver Nayna Jain
  1 sibling, 4 replies; 7+ messages in thread
From: Nayna Jain @ 2022-07-13  0:59 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Nayna Jain, Paul Mackerras, George Wilson, gjoyce

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 PLPKS driver using H_CALL interface to access PKS storage.

Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
---
 arch/powerpc/include/asm/hvcall.h             |   9 +
 arch/powerpc/include/asm/plpks.h              |  90 ++++
 arch/powerpc/platforms/pseries/Kconfig        |  13 +
 arch/powerpc/platforms/pseries/Makefile       |   2 +
 arch/powerpc/platforms/pseries/plpks/Makefile |   7 +
 arch/powerpc/platforms/pseries/plpks/plpks.c  | 509 ++++++++++++++++++
 6 files changed, 630 insertions(+)
 create mode 100644 arch/powerpc/include/asm/plpks.h
 create mode 100644 arch/powerpc/platforms/pseries/plpks/Makefile
 create mode 100644 arch/powerpc/platforms/pseries/plpks/plpks.c

diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h
index d92a20a85395..24b661b0717c 100644
--- a/arch/powerpc/include/asm/hvcall.h
+++ b/arch/powerpc/include/asm/hvcall.h
@@ -97,6 +97,7 @@
 #define H_OP_MODE	-73
 #define H_COP_HW	-74
 #define H_STATE		-75
+#define H_IN_USE	-77
 #define H_UNSUPPORTED_FLAG_START	-256
 #define H_UNSUPPORTED_FLAG_END		-511
 #define H_MULTI_THREADS_ACTIVE	-9005
@@ -321,6 +322,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/include/asm/plpks.h b/arch/powerpc/include/asm/plpks.h
new file mode 100644
index 000000000000..cf60e53e1f15
--- /dev/null
+++ b/arch/powerpc/include/asm/plpks.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 IBM Corporation
+ * Author: Nayna Jain <nayna@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 os;
+	u8 *name;
+	u16 namelen;
+	u32 policy;
+	u16 datalen;
+	u8 *data;
+};
+
+struct plpks_var_name {
+	u16 namelen;
+	u8  *name;
+};
+
+struct plpks_var_name_list {
+	u32 varcount;
+	struct plpks_var_name varlist[];
+};
+
+struct plpks_config {
+	u8 version;
+	u8 flags;
+	u32 rsvd0;
+	u16 maxpwsize;
+	u16 maxobjlabelsize;
+	u16 maxobjsize;
+	u32 totalsize;
+	u32 usedspace;
+	u32 supportedpolicies;
+	u64 rsvd1;
+} __packed;
+
+/**
+ * Successful return from this API  implies PKS is available.
+ * This is used to initialize kernel driver and user interfaces.
+ */
+struct plpks_config *plpks_get_config(void);
+
+/**
+ * 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
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index f7fd91d153a4..de6efe5d18c2 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
+	tristate "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..d6a9209e08c0 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -37,3 +37,5 @@ obj-$(CONFIG_ARCH_HAS_CC_PLATFORM)	+= cc_platform.o
 # nothing that operates in real mode is safe for KASAN
 KASAN_SANITIZE_ras.o := n
 KASAN_SANITIZE_kexec.o := n
+
+obj-$(CONFIG_PSERIES_PLPKS)      += plpks/
diff --git a/arch/powerpc/platforms/pseries/plpks/Makefile b/arch/powerpc/platforms/pseries/plpks/Makefile
new file mode 100644
index 000000000000..e651ace920db
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/plpks/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2022 IBM Corporation
+# Author: Nayna Jain <nayna@linux.ibm.com>
+#
+
+obj-$(CONFIG_PSERIES_PLPKS)  += plpks.o
diff --git a/arch/powerpc/platforms/pseries/plpks/plpks.c b/arch/powerpc/platforms/pseries/plpks/plpks.c
new file mode 100644
index 000000000000..463ce93f9066
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/plpks/plpks.c
@@ -0,0 +1,509 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * POWER LPAR Platform KeyStore (PLPKS)
+ * Copyright (C) 2022 IBM Corporation
+ * Author: Nayna Jain <nayna@linux.ibm.com>
+ *
+ * Provides access to variables stored in Power LPAR Platform KeyStore(PLPKS).
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <asm/hvcall.h>
+#include <asm/plpks.h>
+#include <asm/unaligned.h>
+#include <asm/machdep.h>
+
+#define MODULE_VERS "1.0"
+#define MODULE_NAME "pseries-plpks"
+
+#define PKS_FW_OWNER   0x1
+#define PKS_BOOTLOADER_OWNER   0x2
+#define PKS_OS_OWNER   0x3
+
+#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 bool configset;
+static struct plpks_config *config;
+static u8 *ospassword;
+static u16 ospasswordlength;
+
+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];
+};
+
+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_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;
+	default:
+		err = -EINVAL;
+	}
+
+	return err;
+}
+
+static int plpks_gen_password(void)
+{
+	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};
+	u8 consumer = PKS_OS_OWNER;
+	int rc;
+
+	ospassword = kzalloc(config->maxpwsize, GFP_KERNEL);
+	if (!ospassword)
+		return -ENOMEM;
+
+	ospasswordlength = config->maxpwsize;
+	rc = plpar_hcall(H_PKS_GEN_PASSWORD,
+			 retbuf,
+			 consumer,
+			 0,
+			 virt_to_phys(ospassword),
+			 config->maxpwsize);
+
+	return pseries_status_to_err(rc);
+}
+
+static int construct_auth(u8 consumer, struct plpks_auth **auth)
+{
+	pr_debug("max password size is %u\n", config->maxpwsize);
+
+	if (!auth || consumer > 3)
+		return -EINVAL;
+
+	*auth = kmalloc(struct_size(*auth, password, config->maxpwsize),
+			GFP_KERNEL);
+	if (!*auth)
+		return -ENOMEM;
+
+	(*auth)->version = 1;
+	(*auth)->consumer = consumer;
+	(*auth)->rsvd0 = 0;
+	(*auth)->rsvd1 = 0;
+	if (consumer == PKS_FW_OWNER || consumer == PKS_BOOTLOADER_OWNER) {
+		pr_debug("consumer is bootloader or firmware\n");
+		(*auth)->passwordlength = 0;
+		return 0;
+	}
+
+	(*auth)->passwordlength = (__force __be16)ospasswordlength;
+
+	memcpy((*auth)->password, ospassword,
+	       flex_array_size(*auth, password,
+	       (__force u16)((*auth)->passwordlength)));
+	(*auth)->passwordlength = cpu_to_be16((__force u16)((*auth)->passwordlength));
+
+	return 0;
+}
+
+/**
+ * Label is combination of label attributes + name.
+ * Label attributes are used internally by kernel and not exposed to the user.
+ */
+static int construct_label(char *component, u8 varos, u8 *name, u16 namelen, u8 **label)
+{
+	int varlen;
+	int len = 0;
+	int llen = 0;
+	int i;
+	int rc = 0;
+	u8 labellength = MAX_LABEL_ATTR_SIZE;
+
+	if (!label)
+		return -EINVAL;
+
+	varlen = namelen + sizeof(struct label_attr);
+	*label = kzalloc(varlen, GFP_KERNEL);
+
+	if (!*label)
+		return -ENOMEM;
+
+	if (component) {
+		len = strlen(component);
+		memcpy(*label, component, len);
+	}
+	llen = len;
+
+	if (component)
+		len = 8 - strlen(component);
+	else
+		len = 8;
+
+	memset(*label + llen, 0, len);
+	llen = llen + len;
+
+	((*label)[llen]) = 0;
+	llen = llen + 1;
+
+	memcpy(*label + llen, &varos, 1);
+	llen = llen + 1;
+
+	memcpy(*label + llen, &labellength, 1);
+	llen = llen + 1;
+
+	memset(*label + llen, 0, 5);
+	llen = llen + 5;
+
+	memcpy(*label + llen, name, namelen);
+	llen = llen + namelen;
+
+	for (i = 0; i < llen; i++)
+		pr_debug("%c", (*label)[i]);
+
+	rc = llen;
+	return rc;
+}
+
+static int _plpks_get_config(void)
+{
+	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};
+	int rc;
+	size_t size = sizeof(struct plpks_config);
+
+	config = kzalloc(size, GFP_KERNEL);
+	if (!config)
+		return -ENOMEM;
+
+	rc = plpar_hcall(H_PKS_GET_CONFIG,
+			 retbuf,
+			 virt_to_phys(config),
+			 size);
+
+	if (rc != H_SUCCESS)
+		return pseries_status_to_err(rc);
+
+	config->rsvd0 = be32_to_cpu((__force __be32)config->rsvd0);
+	config->maxpwsize = be16_to_cpu((__force __be16)config->maxpwsize);
+	config->maxobjlabelsize = be16_to_cpu((__force __be16)config->maxobjlabelsize);
+	config->maxobjsize = be16_to_cpu((__force __be16)config->maxobjsize);
+	config->totalsize = be32_to_cpu((__force __be32)config->totalsize);
+	config->usedspace = be32_to_cpu((__force __be32)config->usedspace);
+	config->supportedpolicies = be32_to_cpu((__force __be32)config->supportedpolicies);
+	config->rsvd1 = be64_to_cpu((__force __be64)config->rsvd1);
+
+	configset = true;
+
+	return 0;
+}
+
+static int plpks_confirm_object_flushed(u8 *label, u16 labellen)
+{
+	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};
+	int rc;
+	u64 timeout = 0;
+	struct plpks_auth *auth;
+	u8 status;
+	int i;
+
+	rc = construct_auth(PKS_OS_OWNER, &auth);
+	if (rc)
+		return rc;
+
+	for (i = 0; i < labellen; i++)
+		pr_debug("%02x ", label[i]);
+
+	do {
+		rc = plpar_hcall(H_PKS_CONFIRM_OBJECT_FLUSHED,
+				 retbuf,
+				 virt_to_phys(auth),
+				 virt_to_phys(label),
+				 labellen);
+
+		status = retbuf[0];
+		if (rc) {
+			pr_info("rc is %d, status is %d\n", rc, status);
+			if (rc == H_NOT_FOUND && status == 1)
+				rc = 0;
+			break;
+		}
+
+		pr_debug("rc is %d, status is %d\n", rc, status);
+
+		if (!rc && status == 1)
+			break;
+
+		usleep_range(PKS_FLUSH_SLEEP, PKS_FLUSH_SLEEP + PKS_FLUSH_SLEEP_RANGE);
+		timeout = timeout + PKS_FLUSH_SLEEP;
+		pr_debug("timeout is %llu\n", timeout);
+
+	} while (timeout < PKS_FLUSH_MAX_TIMEOUT);
+
+	rc = pseries_status_to_err(rc);
+
+	kfree(auth);
+
+	return rc;
+}
+
+int plpks_write_var(struct plpks_var var)
+{
+	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};
+	int rc;
+	u8 *label;
+	u16 varlen;
+	u8 *data = var.data;
+	struct plpks_auth *auth;
+
+	if (!var.component || !data || var.datalen <= 0 ||
+	    var.namelen > MAX_NAME_SIZE ||
+	    var.datalen > MAX_DATA_SIZE)
+		return -EINVAL;
+
+	if (var.policy & SIGNEDUPDATE)
+		return -EINVAL;
+
+	rc = construct_auth(PKS_OS_OWNER, &auth);
+	if (rc)
+		return rc;
+
+	rc = construct_label(var.component, var.os, var.name, var.namelen,
+			     &label);
+	if (rc <= 0)
+		goto out;
+
+	varlen =  rc;
+	pr_debug("Name to be written is of label size %d\n", varlen);
+	rc = plpar_hcall(H_PKS_WRITE_OBJECT,
+			 retbuf,
+			 virt_to_phys(auth),
+			 virt_to_phys(label),
+			 varlen,
+			 var.policy,
+			 virt_to_phys(data),
+			 var.datalen);
+
+	if (!rc)
+		rc = plpks_confirm_object_flushed(label, varlen);
+
+	rc = pseries_status_to_err(rc);
+	kfree(label);
+
+out:
+	kfree(auth);
+
+	return rc;
+}
+EXPORT_SYMBOL(plpks_write_var);
+
+int plpks_remove_var(char *component, u8 varos, struct plpks_var_name vname)
+{
+	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};
+	int rc;
+	u8 *label;
+	u16 varlen;
+	struct plpks_auth *auth;
+
+	if (!component || vname.namelen > MAX_NAME_SIZE)
+		return -EINVAL;
+
+	rc = construct_auth(PKS_OS_OWNER, &auth);
+	if (rc)
+		return rc;
+
+	rc = construct_label(component, varos, vname.name, vname.namelen, &label);
+	if (rc <= 0)
+		goto out;
+
+	varlen = rc;
+	pr_debug("Name to be written is of label size %d\n", varlen);
+	rc = plpar_hcall(H_PKS_REMOVE_OBJECT,
+			 retbuf,
+			 virt_to_phys(auth),
+			 virt_to_phys(label),
+			 varlen);
+
+	if (!rc)
+		rc = plpks_confirm_object_flushed(label, varlen);
+
+	rc = pseries_status_to_err(rc);
+	kfree(label);
+
+out:
+	kfree(auth);
+
+	return rc;
+}
+EXPORT_SYMBOL(plpks_remove_var);
+
+static int plpks_read_var(u8 consumer, struct plpks_var *var)
+{
+	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};
+	int rc;
+	u16 outlen = config->maxobjsize;
+	u8 *label;
+	u8 *out;
+	u16 varlen;
+	struct plpks_auth *auth;
+
+	if (var->namelen > MAX_NAME_SIZE)
+		return -EINVAL;
+
+	rc = construct_auth(PKS_OS_OWNER, &auth);
+	if (rc)
+		return rc;
+
+	rc = construct_label(var->component, var->os, var->name, var->namelen,
+			     &label);
+	if (rc <= 0)
+		goto out;
+
+	varlen = rc;
+	pr_debug("Name to be written is of label size %d\n", varlen);
+	out = kzalloc(outlen, GFP_KERNEL);
+	if (!out)
+		goto out1;
+
+	rc = plpar_hcall(H_PKS_READ_OBJECT,
+			 retbuf,
+			 virt_to_phys(auth),
+			 virt_to_phys(label),
+			 varlen,
+			 virt_to_phys(out),
+			 outlen);
+
+	if (rc != H_SUCCESS) {
+		pr_err("Failed to read %d\n", rc);
+		rc = pseries_status_to_err(rc);
+		goto out2;
+	}
+
+	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 out2;
+	}
+	var->policy = retbuf[1];
+
+	memcpy(var->data, out, var->datalen);
+
+out2:
+	kfree(out);
+
+out1:
+	kfree(label);
+
+out:
+	kfree(auth);
+
+	return rc;
+}
+
+int plpks_read_os_var(struct plpks_var *var)
+{
+	return plpks_read_var(PKS_OS_OWNER, var);
+}
+EXPORT_SYMBOL(plpks_read_os_var);
+
+int plpks_read_fw_var(struct plpks_var *var)
+{
+	return plpks_read_var(PKS_FW_OWNER, var);
+}
+EXPORT_SYMBOL(plpks_read_fw_var);
+
+int plpks_read_bootloader_var(struct plpks_var *var)
+{
+	return plpks_read_var(PKS_BOOTLOADER_OWNER, var);
+}
+EXPORT_SYMBOL(plpks_read_bootloader_var);
+
+struct plpks_config *plpks_get_config(void)
+{
+	if (!configset) {
+		if (_plpks_get_config())
+			return NULL;
+	}
+
+	return config;
+}
+EXPORT_SYMBOL(plpks_get_config);
+
+static __init int pseries_plpks_init(void)
+{
+	int rc = 0;
+
+	rc = _plpks_get_config();
+
+	if (rc) {
+		pr_err("Error initializing plpks\n");
+		return rc;
+	}
+
+	rc = plpks_gen_password();
+	if (rc) {
+		if (rc == H_IN_USE) {
+			rc = 0;
+		} else {
+			pr_err("Failed setting password %d\n", rc);
+			rc = pseries_status_to_err(rc);
+			return rc;
+		}
+	}
+
+	pr_info("POWER LPAR Platform Keystore initialized successfully\n");
+
+	return rc;
+}
+arch_initcall(pseries_plpks_init);
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/2] powerpc/pseries: kernel interfaces to PLPKS platform driver
  2022-07-13  0:59 [PATCH 0/2] Provide PowerVM LPAR Platform KeyStore driver for Self Encrypting Drives Nayna Jain
  2022-07-13  0:59 ` [PATCH 1/2] powerpc/pseries: define driver for Platform KeyStore Nayna Jain
@ 2022-07-13  0:59 ` Nayna Jain
  1 sibling, 0 replies; 7+ messages in thread
From: Nayna Jain @ 2022-07-13  0:59 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Greg Joyce, Paul Mackerras, George Wilson, gjoyce

From: Greg Joyce <gjoyce@linux.vnet.ibm.com>

Add platform specific interfaces arch_read_variable() and
arch_variable() to allow platform agnostic access to platform
variable stores.

Signed-off-by: Greg Joyce <gjoyce@linux.vnet.ibm.com>
---
 arch/powerpc/platforms/pseries/plpks/Makefile |   1 +
 .../platforms/pseries/plpks/plpks_arch_ops.c  | 163 ++++++++++++++++++
 2 files changed, 164 insertions(+)
 create mode 100644 arch/powerpc/platforms/pseries/plpks/plpks_arch_ops.c

diff --git a/arch/powerpc/platforms/pseries/plpks/Makefile b/arch/powerpc/platforms/pseries/plpks/Makefile
index e651ace920db..3e9ce6f16575 100644
--- a/arch/powerpc/platforms/pseries/plpks/Makefile
+++ b/arch/powerpc/platforms/pseries/plpks/Makefile
@@ -5,3 +5,4 @@
 #
 
 obj-$(CONFIG_PSERIES_PLPKS)  += plpks.o
+obj-$(CONFIG_PSERIES_PLPKS)  += plpks_arch_ops.o
diff --git a/arch/powerpc/platforms/pseries/plpks/plpks_arch_ops.c b/arch/powerpc/platforms/pseries/plpks/plpks_arch_ops.c
new file mode 100644
index 000000000000..11cd03da08b7
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/plpks/plpks_arch_ops.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * POWER platform keystore
+ * Copyright (C) 2022 IBM Corporation
+ *
+ * This pseries platform device driver provides access to
+ * variables stored in platform keystore.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/ioctl.h>
+#include <uapi/linux/sed-opal.h>
+#include <linux/sed-opal.h>
+#include <linux/arch_vars.h>
+#include <asm/plpks.h>
+
+/*
+ * variable structure that contains all SED data
+ */
+struct plpks_sed_object_data {
+	u_char version;
+	u_char pad1[7];
+	u_long authority;
+	u_long range;
+	u_int  key_len;
+	u_char key[32];
+};
+
+/*
+ * ext_type values
+ *	00        no extension exists
+ *	01-1F     common
+ *	20-3F     AIX
+ *	40-5F     Linux
+ *	60-7F     IBMi
+ */
+
+/*
+ * This extension is optional for version 1 sed_object_data
+ */
+struct sed_object_extension {
+	u8 ext_type;
+	u8 rsvd[3];
+	u8 ext_data[64];
+};
+
+#define PKS_SED_OBJECT_DATA_V1          1
+#define PKS_SED_MANGLED_LABEL           "/default/pri"
+#define PLPKS_SED_COMPONENT             "sed-opal"
+#define PLPKS_SED_POLICY                WORLDREADABLE
+#define PLPKS_SED_OS_COMMON             4
+
+#ifndef CONFIG_BLK_SED_OPAL
+#define	OPAL_AUTH_KEY                   ""
+#endif
+
+/*
+ * Read the variable data from PKS given the label
+ */
+int arch_read_variable(enum arch_variable_type type, char *varname,
+		       void *varbuf, u_int *varlen)
+{
+	struct plpks_var var;
+	struct plpks_sed_object_data *data;
+	u_int offset = 0;
+	char *buf = (char *)varbuf;
+	int ret;
+
+	var.name = varname;
+	var.namelen = strlen(varname);
+	var.policy = PLPKS_SED_POLICY;
+	var.os = PLPKS_SED_OS_COMMON;
+	var.data = NULL;
+	var.datalen = 0;
+
+	switch (type) {
+	case ARCH_VAR_OPAL_KEY:
+		var.component = PLPKS_SED_COMPONENT;
+		if (strcmp(OPAL_AUTH_KEY, varname) == 0) {
+			var.name = PKS_SED_MANGLED_LABEL;
+			var.namelen = strlen(varname);
+		}
+		offset = offsetof(struct plpks_sed_object_data, key);
+		break;
+	case ARCH_VAR_OTHER:
+		var.component = "";
+		break;
+	}
+
+	ret = plpks_read_os_var(&var);
+	if (ret != 0)
+		return ret;
+
+	if (offset > var.datalen)
+		offset = 0;
+
+	switch (type) {
+	case ARCH_VAR_OPAL_KEY:
+		data = (struct plpks_sed_object_data *)var.data;
+		*varlen = data->key_len;
+		break;
+	case ARCH_VAR_OTHER:
+		*varlen = var.datalen;
+		break;
+	}
+
+	if (var.data) {
+		memcpy(varbuf, var.data + offset, var.datalen - offset);
+		buf[*varlen] = '\0';
+		kfree(var.data);
+	}
+
+	return 0;
+}
+
+/*
+ * Write the variable data to PKS given the label
+ */
+int arch_write_variable(enum arch_variable_type type, char *varname,
+			void *varbuf, u_int varlen)
+{
+	struct plpks_var var;
+	struct plpks_sed_object_data data;
+	struct plpks_var_name vname;
+
+	var.name = varname;
+	var.namelen = strlen(varname);
+	var.policy = PLPKS_SED_POLICY;
+	var.os = PLPKS_SED_OS_COMMON;
+	var.datalen = varlen;
+	var.data = varbuf;
+
+	switch (type) {
+	case ARCH_VAR_OPAL_KEY:
+		var.component = PLPKS_SED_COMPONENT;
+		if (strcmp(OPAL_AUTH_KEY, varname) == 0) {
+			var.name = PKS_SED_MANGLED_LABEL;
+			var.namelen = strlen(varname);
+		}
+		var.datalen = sizeof(struct plpks_sed_object_data);
+		var.data = (u8 *)&data;
+
+		/* initialize SED object */
+		data.version = PKS_SED_OBJECT_DATA_V1;
+		data.authority = 0;
+		data.range = 0;
+		data.key_len = varlen;
+		memcpy(data.key, varbuf, varlen);
+		break;
+	case ARCH_VAR_OTHER:
+		var.component = "";
+		break;
+	}
+
+	/* variable update requires delete first */
+	vname.namelen = var.namelen;
+	vname.name = var.name;
+	(void)plpks_remove_var(PLPKS_SED_COMPONENT, PLPKS_SED_OS_COMMON, vname);
+
+	return plpks_write_var(var);
+}
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] powerpc/pseries: define driver for Platform KeyStore
  2022-07-13  0:59 ` [PATCH 1/2] powerpc/pseries: define driver for Platform KeyStore Nayna Jain
@ 2022-07-18 20:58   ` Gregory Joyce
  2022-07-18 22:30   ` Gregory Joyce
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 7+ messages in thread
From: Gregory Joyce @ 2022-07-18 20:58 UTC (permalink / raw)
  To: Nayna Jain, linuxppc-dev@lists.ozlabs.org; +Cc: George Wilson, Paul Mackerras

[-- Attachment #1: Type: text/plain, Size: 975 bytes --]

Tested-by: Greg Joyce <gjoyce@linux.vnet.ibm.com>

________________________________
From: Nayna Jain <nayna@linux.ibm.com>
Sent: Tuesday, July 12, 2022 7:59 PM
To: linuxppc-dev@lists.ozlabs.org <linuxppc-dev@lists.ozlabs.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>; Benjamin Herrenschmidt <benh@kernel.crashing.org>; Paul Mackerras <paulus@samba.org>; George Wilson <gcwilson@linux.ibm.com>; Gregory Joyce <gjoyce@ibm.com>; Nayna Jain <nayna@linux.ibm.com>
Subject: [PATCH 1/2] powerpc/pseries: define driver for Platform KeyStore

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 PLPKS driver using H_CALL interface to access PKS storage.

Tested-by: Greg Joyce <gjoyce@linux.vnet.ibm.com>
Signed-off-by: Nayna Jain <nayna@linux.ibm.com>




[-- Attachment #2: Type: text/html, Size: 2882 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] powerpc/pseries: define driver for Platform KeyStore
  2022-07-13  0:59 ` [PATCH 1/2] powerpc/pseries: define driver for Platform KeyStore Nayna Jain
  2022-07-18 20:58   ` Gregory Joyce
@ 2022-07-18 22:30   ` Gregory Joyce
  2022-07-19 19:09   ` Eric Richter
  2022-07-20 16:29   ` Murilo Opsfelder Araújo
  3 siblings, 0 replies; 7+ messages in thread
From: Gregory Joyce @ 2022-07-18 22:30 UTC (permalink / raw)
  To: Nayna Jain, linuxppc-dev@lists.ozlabs.org
  Cc: George Wilson, Paul Mackerras, Brian J King

[-- Attachment #1: Type: text/plain, Size: 7440 bytes --]

Comments inline.
Reviewed-by: Greg Joyce <gjoyce@linux.vnet.ibm.com>

________________________________
From: Nayna Jain <nayna@linux.ibm.com>
Sent: Tuesday, July 12, 2022 7:59 PM
To: linuxppc-dev@lists.ozlabs.org <linuxppc-dev@lists.ozlabs.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>; Benjamin Herrenschmidt <benh@kernel.crashing.org>; Paul Mackerras <paulus@samba.org>; George Wilson <gcwilson@linux.ibm.com>; Gregory Joyce <gjoyce@ibm.com>; Nayna Jain <nayna@linux.ibm.com>
Subject: [PATCH 1/2] powerpc/pseries: define driver for Platform KeyStore

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 PLPKS driver using H_CALL interface to access PKS storage.

Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
---


+
+static int construct_auth(u8 consumer, struct plpks_auth **auth)
+{
+       pr_debug("max password size is %u\n", config->maxpwsize);

Are the pr_debugs in this function still needed?

+
+       if (!auth || consumer > 3)
+               return -EINVAL;
+
+       *auth = kmalloc(struct_size(*auth, password, config->maxpwsize),
+                       GFP_KERNEL);
+       if (!*auth)
+               return -ENOMEM;
+
+       (*auth)->version = 1;
+       (*auth)->consumer = consumer;
+       (*auth)->rsvd0 = 0;
+       (*auth)->rsvd1 = 0;
+       if (consumer == PKS_FW_OWNER || consumer == PKS_BOOTLOADER_OWNER) {
+               pr_debug("consumer is bootloader or firmware\n");
+               (*auth)->passwordlength = 0;
+               return 0;
+       }
+
+       (*auth)->passwordlength = (__force __be16)ospasswordlength;
+
+       memcpy((*auth)->password, ospassword,
+              flex_array_size(*auth, password,
+              (__force u16)((*auth)->passwordlength)));
+       (*auth)->passwordlength = cpu_to_be16((__force u16)((*auth)->passwordlength));
+
+       return 0;
+}
+
+/**
+ * Label is combination of label attributes + name.
+ * Label attributes are used internally by kernel and not exposed to the user.
+ */
+static int construct_label(char *component, u8 varos, u8 *name, u16 namelen, u8 **label)
+{
+       int varlen;
+       int len = 0;
+       int llen = 0;
+       int i;
+       int rc = 0;
+       u8 labellength = MAX_LABEL_ATTR_SIZE;
+
+       if (!label)
+               return -EINVAL;
+
+       varlen = namelen + sizeof(struct label_attr);
+       *label = kzalloc(varlen, GFP_KERNEL);
+
+       if (!*label)
+               return -ENOMEM;
+
+       if (component) {
+               len = strlen(component);
+               memcpy(*label, component, len);
+       }
+       llen = len;
+

I guess the 8, 1, and 5 are field lengths. Could they be a define or sizeof?

+       if (component)
+               len = 8 - strlen(component);
+       else
+               len = 8;
+
+       memset(*label + llen, 0, len);
+       llen = llen + len;
+
+       ((*label)[llen]) = 0;
+       llen = llen + 1;
+
+       memcpy(*label + llen, &varos, 1);
+       llen = llen + 1;
+
+       memcpy(*label + llen, &labellength, 1);
+       llen = llen + 1;
+
+       memset(*label + llen, 0, 5);
+       llen = llen + 5;
+
+       memcpy(*label + llen, name, namelen);
+       llen = llen + namelen;
+
+       for (i = 0; i < llen; i++)
+               pr_debug("%c", (*label)[i]);
+
+       rc = llen;
+       return rc;
+}
+
+static int _plpks_get_config(void)
+{
+       unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};
+       int rc;
+       size_t size = sizeof(struct plpks_config);
+
+       config = kzalloc(size, GFP_KERNEL);
+       if (!config)
+               return -ENOMEM;
+
+       rc = plpar_hcall(H_PKS_GET_CONFIG,
+                        retbuf,
+                        virt_to_phys(config),
+                        size);
+
+       if (rc != H_SUCCESS)

Free config before returning the error.

+               return pseries_status_to_err(rc);
+
+       config->rsvd0 = be32_to_cpu((__force __be32)config->rsvd0);
+       config->maxpwsize = be16_to_cpu((__force __be16)config->maxpwsize);
+       config->maxobjlabelsize = be16_to_cpu((__force __be16)config->maxobjlabelsize);
+       config->maxobjsize = be16_to_cpu((__force __be16)config->maxobjsize);
+       config->totalsize = be32_to_cpu((__force __be32)config->totalsize);
+       config->usedspace = be32_to_cpu((__force __be32)config->usedspace);
+       config->supportedpolicies = be32_to_cpu((__force __be32)config->supportedpolicies);
+       config->rsvd1 = be64_to_cpu((__force __be64)config->rsvd1);
+
+       configset = true;
+
+       return 0;
+}
+
+static int plpks_confirm_object_flushed(u8 *label, u16 labellen)
+{
+       unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};
+       int rc;
+       u64 timeout = 0;
+       struct plpks_auth *auth;
+       u8 status;
+       int i;
+

Deleted pr_debugs? I guess this is a general question for all pr_debugs in this file.

+       rc = construct_auth(PKS_OS_OWNER, &auth);
+       if (rc)
+               return rc;
+
+       for (i = 0; i < labellen; i++)
+               pr_debug("%02x ", label[i]);
+
+       do {
+               rc = plpar_hcall(H_PKS_CONFIRM_OBJECT_FLUSHED,
+                                retbuf,
+                                virt_to_phys(auth),
+                                virt_to_phys(label),
+                                labellen);
+
+               status = retbuf[0];
+               if (rc) {
+                       pr_info("rc is %d, status is %d\n", rc, status);
+                       if (rc == H_NOT_FOUND && status == 1)
+                               rc = 0;
+                       break;
+               }
+
+               pr_debug("rc is %d, status is %d\n", rc, status);
+
+               if (!rc && status == 1)
+                       break;
+
+               usleep_range(PKS_FLUSH_SLEEP, PKS_FLUSH_SLEEP + PKS_FLUSH_SLEEP_RANGE);
+               timeout = timeout + PKS_FLUSH_SLEEP;
+               pr_debug("timeout is %llu\n", timeout);
+
+       } while (timeout < PKS_FLUSH_MAX_TIMEOUT);
+
+       rc = pseries_status_to_err(rc);
+
+       kfree(auth);
+
+       return rc;
+}
+

+EXPORT_SYMBOL(plpks_remove_var);
XPORT_SYMBOL(plpks_get_config);
+
+static __init int pseries_plpks_init(void)
+{
+       int rc = 0;
+
+       rc = _plpks_get_config();
+
+       if (rc) {

I think this pr_err would be better if provided more info like the pr_info below.

+               pr_err("Error initializing plpks\n");
+               return rc;
+       }
+
+       rc = plpks_gen_password();
+       if (rc) {
+               if (rc == H_IN_USE) {
+                       rc = 0;
+               } else {

I think this pr_err would be better if provided more info like the pr_info below.

+                       pr_err("Failed setting password %d\n", rc);
+                       rc = pseries_status_to_err(rc);
+                       return rc;
+               }
+       }
+
+       pr_info("POWER LPAR Platform Keystore initialized successfully\n");
+
+       return rc;
+}
+arch_initcall(pseries_plpks_init);
--
2.27.0


[-- Attachment #2: Type: text/html, Size: 17394 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] powerpc/pseries: define driver for Platform KeyStore
  2022-07-13  0:59 ` [PATCH 1/2] powerpc/pseries: define driver for Platform KeyStore Nayna Jain
  2022-07-18 20:58   ` Gregory Joyce
  2022-07-18 22:30   ` Gregory Joyce
@ 2022-07-19 19:09   ` Eric Richter
  2022-07-20 16:29   ` Murilo Opsfelder Araújo
  3 siblings, 0 replies; 7+ messages in thread
From: Eric Richter @ 2022-07-19 19:09 UTC (permalink / raw)
  To: Nayna Jain, linuxppc-dev; +Cc: George Wilson, Paul Mackerras, gjoyce

On 7/12/22 7:59 PM, 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 PLPKS driver using H_CALL interface to access PKS storage.
> 
> Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
> ---
>  arch/powerpc/include/asm/hvcall.h             |   9 +
>  arch/powerpc/include/asm/plpks.h              |  90 ++++
>  arch/powerpc/platforms/pseries/Kconfig        |  13 +
>  arch/powerpc/platforms/pseries/Makefile       |   2 +
>  arch/powerpc/platforms/pseries/plpks/Makefile |   7 +
>  arch/powerpc/platforms/pseries/plpks/plpks.c  | 509 ++++++++++++++++++
>  6 files changed, 630 insertions(+)
>  create mode 100644 arch/powerpc/include/asm/plpks.h
>  create mode 100644 arch/powerpc/platforms/pseries/plpks/Makefile
>  create mode 100644 arch/powerpc/platforms/pseries/plpks/plpks.c
> 
> diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h
> index d92a20a85395..24b661b0717c 100644
> --- a/arch/powerpc/include/asm/hvcall.h
> +++ b/arch/powerpc/include/asm/hvcall.h
> @@ -97,6 +97,7 @@
>  #define H_OP_MODE	-73
>  #define H_COP_HW	-74
>  #define H_STATE		-75
> +#define H_IN_USE	-77
>  #define H_UNSUPPORTED_FLAG_START	-256
>  #define H_UNSUPPORTED_FLAG_END		-511
>  #define H_MULTI_THREADS_ACTIVE	-9005
> @@ -321,6 +322,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/include/asm/plpks.h b/arch/powerpc/include/asm/plpks.h
> new file mode 100644
> index 000000000000..cf60e53e1f15
> --- /dev/null
> +++ b/arch/powerpc/include/asm/plpks.h
> @@ -0,0 +1,90 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2022 IBM Corporation
> + * Author: Nayna Jain <nayna@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 os;
> +	u8 *name;
> +	u16 namelen;
> +	u32 policy;
> +	u16 datalen;
> +	u8 *data;
> +};
> +
> +struct plpks_var_name {
> +	u16 namelen;
> +	u8  *name;
> +};
> +
> +struct plpks_var_name_list {
> +	u32 varcount;
> +	struct plpks_var_name varlist[];
> +};
> +
> +struct plpks_config {
> +	u8 version;
> +	u8 flags;
> +	u32 rsvd0;
> +	u16 maxpwsize;
> +	u16 maxobjlabelsize;
> +	u16 maxobjsize;
> +	u32 totalsize;
> +	u32 usedspace;
> +	u32 supportedpolicies;
> +	u64 rsvd1;
> +} __packed;
> +
> +/**
> + * Successful return from this API  implies PKS is available.
> + * This is used to initialize kernel driver and user interfaces.
> + */
> +struct plpks_config *plpks_get_config(void);
> +
> +/**
> + * 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
> diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
> index f7fd91d153a4..de6efe5d18c2 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
> +	tristate "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..d6a9209e08c0 100644
> --- a/arch/powerpc/platforms/pseries/Makefile
> +++ b/arch/powerpc/platforms/pseries/Makefile
> @@ -37,3 +37,5 @@ obj-$(CONFIG_ARCH_HAS_CC_PLATFORM)	+= cc_platform.o
>  # nothing that operates in real mode is safe for KASAN
>  KASAN_SANITIZE_ras.o := n
>  KASAN_SANITIZE_kexec.o := n
> +
> +obj-$(CONFIG_PSERIES_PLPKS)      += plpks/
> diff --git a/arch/powerpc/platforms/pseries/plpks/Makefile b/arch/powerpc/platforms/pseries/plpks/Makefile
> new file mode 100644
> index 000000000000..e651ace920db
> --- /dev/null
> +++ b/arch/powerpc/platforms/pseries/plpks/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +#
> +# Copyright (C) 2022 IBM Corporation
> +# Author: Nayna Jain <nayna@linux.ibm.com>
> +#
> +
> +obj-$(CONFIG_PSERIES_PLPKS)  += plpks.o
> diff --git a/arch/powerpc/platforms/pseries/plpks/plpks.c b/arch/powerpc/platforms/pseries/plpks/plpks.c
> new file mode 100644
> index 000000000000..463ce93f9066
> --- /dev/null
> +++ b/arch/powerpc/platforms/pseries/plpks/plpks.c
> @@ -0,0 +1,509 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * POWER LPAR Platform KeyStore (PLPKS)
> + * Copyright (C) 2022 IBM Corporation
> + * Author: Nayna Jain <nayna@linux.ibm.com>
> + *
> + * Provides access to variables stored in Power LPAR Platform KeyStore(PLPKS).
> + */
> +
> +#include <linux/module.h>
> +#include <linux/types.h>
> +#include <linux/errno.h>
> +#include <linux/string.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>
> +#include <asm/hvcall.h>
> +#include <asm/plpks.h>
> +#include <asm/unaligned.h>
> +#include <asm/machdep.h>
> +
> +#define MODULE_VERS "1.0"
> +#define MODULE_NAME "pseries-plpks"
> +
> +#define PKS_FW_OWNER   0x1
> +#define PKS_BOOTLOADER_OWNER   0x2
> +#define PKS_OS_OWNER   0x3
> +
> +#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 bool configset;
> +static struct plpks_config *config;
> +static u8 *ospassword;
> +static u16 ospasswordlength;
> +
> +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];
> +};
> +
> +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_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;
> +	default:
> +		err = -EINVAL;
> +	}
> +
> +	return err;
> +}
> +
> +static int plpks_gen_password(void)
> +{
> +	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};
> +	u8 consumer = PKS_OS_OWNER;
> +	int rc;
> +
> +	ospassword = kzalloc(config->maxpwsize, GFP_KERNEL);

This unconditionally allocates over ospassword -- while this function shouldn't
be called twice, it might be a good idea to place a check here anyway.

> +	if (!ospassword)
> +		return -ENOMEM;
> +
> +	ospasswordlength = config->maxpwsize;
> +	rc = plpar_hcall(H_PKS_GEN_PASSWORD,
> +			 retbuf,
> +			 consumer,
> +			 0,
> +			 virt_to_phys(ospassword),
> +			 config->maxpwsize);

Since this hcall can return an error (as we discussed off-list, returns H_IN_USE
if it is called again without a reboot), perhaps move the assignment to ospassword
after this call succeeds.

> +
> +	return pseries_status_to_err(rc);
> +}
> +
> +static int construct_auth(u8 consumer, struct plpks_auth **auth)
> +{
> +	pr_debug("max password size is %u\n", config->maxpwsize);
> +
> +	if (!auth || consumer > 3)
> +		return -EINVAL;
> +
> +	*auth = kmalloc(struct_size(*auth, password, config->maxpwsize),
> +			GFP_KERNEL);
> +	if (!*auth)
> +		return -ENOMEM;
> +
> +	(*auth)->version = 1;
> +	(*auth)->consumer = consumer;
> +	(*auth)->rsvd0 = 0;
> +	(*auth)->rsvd1 = 0;
> +	if (consumer == PKS_FW_OWNER || consumer == PKS_BOOTLOADER_OWNER) {
> +		pr_debug("consumer is bootloader or firmware\n");
> +		(*auth)->passwordlength = 0;
> +		return 0;
> +	}
> +
> +	(*auth)->passwordlength = (__force __be16)ospasswordlength;
> +
> +	memcpy((*auth)->password, ospassword,
> +	       flex_array_size(*auth, password,
> +	       (__force u16)((*auth)->passwordlength)));
> +	(*auth)->passwordlength = cpu_to_be16((__force u16)((*auth)->passwordlength));
> +
> +	return 0;
> +}
> +
> +/**
> + * Label is combination of label attributes + name.
> + * Label attributes are used internally by kernel and not exposed to the user.
> + */
> +static int construct_label(char *component, u8 varos, u8 *name, u16 namelen, u8 **label)
> +{
> +	int varlen;
> +	int len = 0;
> +	int llen = 0;
> +	int i;
> +	int rc = 0;
> +	u8 labellength = MAX_LABEL_ATTR_SIZE;
> +
> +	if (!label)
> +		return -EINVAL;
> +
> +	varlen = namelen + sizeof(struct label_attr);
> +	*label = kzalloc(varlen, GFP_KERNEL);
> +
> +	if (!*label)
> +		return -ENOMEM;
> +
> +	if (component) {
> +		len = strlen(component);
> +		memcpy(*label, component, len);

Is component a known size, or checked elsewhere to know this won't overrun?

> +	}
> +	llen = len;
> +
> +	if (component)
> +		len = 8 - strlen(component);
> +	else
> +		len = 8;
> +
> +	memset(*label + llen, 0, len);
> +	llen = llen + len;
> +
> +	((*label)[llen]) = 0;
> +	llen = llen + 1;
> +
> +	memcpy(*label + llen, &varos, 1);
> +	llen = llen + 1;
> +
> +	memcpy(*label + llen, &labellength, 1);
> +	llen = llen + 1;
> +
> +	memset(*label + llen, 0, 5);
> +	llen = llen + 5;
> +
> +	memcpy(*label + llen, name, namelen);
> +	llen = llen + namelen;
> +
> +	for (i = 0; i < llen; i++)
> +		pr_debug("%c", (*label)[i]);
> +
> +	rc = llen;
> +	return rc;
> +}
> +
> +static int _plpks_get_config(void)
> +{
> +	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};
> +	int rc;
> +	size_t size = sizeof(struct plpks_config);
> +
> +	config = kzalloc(size, GFP_KERNEL);
> +	if (!config)
> +		return -ENOMEM;
> +
> +	rc = plpar_hcall(H_PKS_GET_CONFIG,
> +			 retbuf,
> +			 virt_to_phys(config),
> +			 size);
> +
> +	if (rc != H_SUCCESS)
> +		return pseries_status_to_err(rc);
> +
> +	config->rsvd0 = be32_to_cpu((__force __be32)config->rsvd0);
> +	config->maxpwsize = be16_to_cpu((__force __be16)config->maxpwsize);
> +	config->maxobjlabelsize = be16_to_cpu((__force __be16)config->maxobjlabelsize);
> +	config->maxobjsize = be16_to_cpu((__force __be16)config->maxobjsize);
> +	config->totalsize = be32_to_cpu((__force __be32)config->totalsize);
> +	config->usedspace = be32_to_cpu((__force __be32)config->usedspace);
> +	config->supportedpolicies = be32_to_cpu((__force __be32)config->supportedpolicies);
> +	config->rsvd1 = be64_to_cpu((__force __be64)config->rsvd1);
> +
> +	configset = true;
> +
> +	return 0;
> +}
> +
> +static int plpks_confirm_object_flushed(u8 *label, u16 labellen)
> +{
> +	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};
> +	int rc;
> +	u64 timeout = 0;
> +	struct plpks_auth *auth;
> +	u8 status;
> +	int i;
> +
> +	rc = construct_auth(PKS_OS_OWNER, &auth);
> +	if (rc)
> +		return rc;
> +
> +	for (i = 0; i < labellen; i++)
> +		pr_debug("%02x ", label[i]);
> +
> +	do {
> +		rc = plpar_hcall(H_PKS_CONFIRM_OBJECT_FLUSHED,
> +				 retbuf,
> +				 virt_to_phys(auth),
> +				 virt_to_phys(label),
> +				 labellen);
> +
> +		status = retbuf[0];
> +		if (rc) {
> +			pr_info("rc is %d, status is %d\n", rc, status);
> +			if (rc == H_NOT_FOUND && status == 1)
> +				rc = 0;
> +			break;
> +		}
> +
> +		pr_debug("rc is %d, status is %d\n", rc, status);
> +
> +		if (!rc && status == 1)
> +			break;
> +
> +		usleep_range(PKS_FLUSH_SLEEP, PKS_FLUSH_SLEEP + PKS_FLUSH_SLEEP_RANGE);
> +		timeout = timeout + PKS_FLUSH_SLEEP;
> +		pr_debug("timeout is %llu\n", timeout);
> +
> +	} while (timeout < PKS_FLUSH_MAX_TIMEOUT);
> +
> +	rc = pseries_status_to_err(rc);
> +
> +	kfree(auth);
> +
> +	return rc;
> +}
> +
> +int plpks_write_var(struct plpks_var var)
> +{
> +	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};
> +	int rc;
> +	u8 *label;
> +	u16 varlen;
> +	u8 *data = var.data;
> +	struct plpks_auth *auth;
> +
> +	if (!var.component || !data || var.datalen <= 0 ||
> +	    var.namelen > MAX_NAME_SIZE ||
> +	    var.datalen > MAX_DATA_SIZE)
> +		return -EINVAL;
> +
> +	if (var.policy & SIGNEDUPDATE)
> +		return -EINVAL;
> +
> +	rc = construct_auth(PKS_OS_OWNER, &auth);
> +	if (rc)
> +		return rc;
> +
> +	rc = construct_label(var.component, var.os, var.name, var.namelen,
> +			     &label);
> +	if (rc <= 0)
> +		goto out;
> +
> +	varlen =  rc;
> +	pr_debug("Name to be written is of label size %d\n", varlen);
> +	rc = plpar_hcall(H_PKS_WRITE_OBJECT,
> +			 retbuf,
> +			 virt_to_phys(auth),
> +			 virt_to_phys(label),
> +			 varlen,
> +			 var.policy,
> +			 virt_to_phys(data),
> +			 var.datalen);
> +
> +	if (!rc)
> +		rc = plpks_confirm_object_flushed(label, varlen);
> +
> +	rc = pseries_status_to_err(rc);
> +	kfree(label);
> +
> +out:
> +	kfree(auth);
> +
> +	return rc;
> +}
> +EXPORT_SYMBOL(plpks_write_var);
> +
> +int plpks_remove_var(char *component, u8 varos, struct plpks_var_name vname)
> +{
> +	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};
> +	int rc;
> +	u8 *label;
> +	u16 varlen;
> +	struct plpks_auth *auth;
> +
> +	if (!component || vname.namelen > MAX_NAME_SIZE)
> +		return -EINVAL;
> +
> +	rc = construct_auth(PKS_OS_OWNER, &auth);
> +	if (rc)
> +		return rc;
> +
> +	rc = construct_label(component, varos, vname.name, vname.namelen, &label);
> +	if (rc <= 0)
> +		goto out;
> +
> +	varlen = rc;
> +	pr_debug("Name to be written is of label size %d\n", varlen);
> +	rc = plpar_hcall(H_PKS_REMOVE_OBJECT,
> +			 retbuf,
> +			 virt_to_phys(auth),
> +			 virt_to_phys(label),
> +			 varlen);
> +
> +	if (!rc)
> +		rc = plpks_confirm_object_flushed(label, varlen);
> +
> +	rc = pseries_status_to_err(rc);
> +	kfree(label);
> +
> +out:
> +	kfree(auth);
> +
> +	return rc;
> +}
> +EXPORT_SYMBOL(plpks_remove_var);
> +
> +static int plpks_read_var(u8 consumer, struct plpks_var *var)
> +{
> +	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};
> +	int rc;
> +	u16 outlen = config->maxobjsize;
> +	u8 *label;
> +	u8 *out;

Minor nit: there are three labels in this function with the name "out",
might be clearer to rename this variable to something like "output" instead.

> +	u16 varlen;
> +	struct plpks_auth *auth;
> +
> +	if (var->namelen > MAX_NAME_SIZE)
> +		return -EINVAL;
> +
> +	rc = construct_auth(PKS_OS_OWNER, &auth);
> +	if (rc)
> +		return rc;
> +
> +	rc = construct_label(var->component, var->os, var->name, var->namelen,
> +			     &label);
> +	if (rc <= 0)
> +		goto out;
> +
> +	varlen = rc;
> +	pr_debug("Name to be written is of label size %d\n", varlen);
> +	out = kzalloc(outlen, GFP_KERNEL);
> +	if (!out)
> +		goto out1;
> +
> +	rc = plpar_hcall(H_PKS_READ_OBJECT,
> +			 retbuf,
> +			 virt_to_phys(auth),
> +			 virt_to_phys(label),
> +			 varlen,
> +			 virt_to_phys(out),
> +			 outlen);
> +
> +	if (rc != H_SUCCESS) {
> +		pr_err("Failed to read %d\n", rc);
> +		rc = pseries_status_to_err(rc);
> +		goto out2;
> +	}
> +
> +	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 out2;
> +	}
> +	var->policy = retbuf[1];
> +
> +	memcpy(var->data, out, var->datalen);
> +
> +out2:
> +	kfree(out);
> +
> +out1:
> +	kfree(label);
> +
> +out:
> +	kfree(auth);
> +
> +	return rc;
> +}
> +
> +int plpks_read_os_var(struct plpks_var *var)
> +{
> +	return plpks_read_var(PKS_OS_OWNER, var);
> +}
> +EXPORT_SYMBOL(plpks_read_os_var);
> +
> +int plpks_read_fw_var(struct plpks_var *var)
> +{
> +	return plpks_read_var(PKS_FW_OWNER, var);
> +}
> +EXPORT_SYMBOL(plpks_read_fw_var);
> +
> +int plpks_read_bootloader_var(struct plpks_var *var)
> +{
> +	return plpks_read_var(PKS_BOOTLOADER_OWNER, var);
> +}
> +EXPORT_SYMBOL(plpks_read_bootloader_var);
> +
> +struct plpks_config *plpks_get_config(void)
> +{
> +	if (!configset) {
> +		if (_plpks_get_config())
> +			return NULL;
> +	}
> +
> +	return config;
> +}
> +EXPORT_SYMBOL(plpks_get_config);
> +
> +static __init int pseries_plpks_init(void)
> +{
> +	int rc = 0;
> +
> +	rc = _plpks_get_config();
> +
> +	if (rc) {
> +		pr_err("Error initializing plpks\n");
> +		return rc;
> +	}
> +
> +	rc = plpks_gen_password();
> +	if (rc) {
> +		if (rc == H_IN_USE) {
> +			rc = 0;
> +		} else {
> +			pr_err("Failed setting password %d\n", rc);
> +			rc = pseries_status_to_err(rc);
> +			return rc;
> +		}
> +	}
> +
> +	pr_info("POWER LPAR Platform Keystore initialized successfully\n");
> +
> +	return rc;
> +}
> +arch_initcall(pseries_plpks_init);

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] powerpc/pseries: define driver for Platform KeyStore
  2022-07-13  0:59 ` [PATCH 1/2] powerpc/pseries: define driver for Platform KeyStore Nayna Jain
                     ` (2 preceding siblings ...)
  2022-07-19 19:09   ` Eric Richter
@ 2022-07-20 16:29   ` Murilo Opsfelder Araújo
  3 siblings, 0 replies; 7+ messages in thread
From: Murilo Opsfelder Araújo @ 2022-07-20 16:29 UTC (permalink / raw)
  To: Nayna Jain, linuxppc-dev; +Cc: George Wilson, Paul Mackerras, gjoyce

Hi, Nayna.

Some comments below.

On 7/12/22 21:59, 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.

I think it would be nice to have some consistency as to spaces before
acronymous, e.g.: "Keystore(PKS)" vs. "Keystore (PKS)". The same could
be applied to "Keystore" vs. "KeyStore" vs. "Key Storage".

>
> Define PLPKS driver using H_CALL interface to access PKS storage.
>
> Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
> ---
>   arch/powerpc/include/asm/hvcall.h             |   9 +
>   arch/powerpc/include/asm/plpks.h              |  90 ++++
>   arch/powerpc/platforms/pseries/Kconfig        |  13 +
>   arch/powerpc/platforms/pseries/Makefile       |   2 +
>   arch/powerpc/platforms/pseries/plpks/Makefile |   7 +
>   arch/powerpc/platforms/pseries/plpks/plpks.c  | 509 ++++++++++++++++++
>   6 files changed, 630 insertions(+)
>   create mode 100644 arch/powerpc/include/asm/plpks.h
>   create mode 100644 arch/powerpc/platforms/pseries/plpks/Makefile
>   create mode 100644 arch/powerpc/platforms/pseries/plpks/plpks.c
>
> diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h
> index d92a20a85395..24b661b0717c 100644
> --- a/arch/powerpc/include/asm/hvcall.h
> +++ b/arch/powerpc/include/asm/hvcall.h
> @@ -97,6 +97,7 @@
>   #define H_OP_MODE	-73
>   #define H_COP_HW	-74
>   #define H_STATE		-75
> +#define H_IN_USE	-77
>   #define H_UNSUPPORTED_FLAG_START	-256
>   #define H_UNSUPPORTED_FLAG_END		-511
>   #define H_MULTI_THREADS_ACTIVE	-9005
> @@ -321,6 +322,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/include/asm/plpks.h b/arch/powerpc/include/asm/plpks.h
> new file mode 100644
> index 000000000000..cf60e53e1f15
> --- /dev/null
> +++ b/arch/powerpc/include/asm/plpks.h
> @@ -0,0 +1,90 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2022 IBM Corporation
> + * Author: Nayna Jain <nayna@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 os;
> +	u8 *name;
> +	u16 namelen;
> +	u32 policy;
> +	u16 datalen;
> +	u8 *data;
> +};
> +
> +struct plpks_var_name {
> +	u16 namelen;
> +	u8  *name;
> +};
> +
> +struct plpks_var_name_list {
> +	u32 varcount;
> +	struct plpks_var_name varlist[];
> +};
> +
> +struct plpks_config {
> +	u8 version;
> +	u8 flags;
> +	u32 rsvd0;
> +	u16 maxpwsize;
> +	u16 maxobjlabelsize;
> +	u16 maxobjsize;
> +	u32 totalsize;
> +	u32 usedspace;
> +	u32 supportedpolicies;
> +	u64 rsvd1;
> +} __packed;
> +
> +/**
> + * Successful return from this API  implies PKS is available.
> + * This is used to initialize kernel driver and user interfaces.
> + */
> +struct plpks_config *plpks_get_config(void);
> +
> +/**
> + * 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
> diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
> index f7fd91d153a4..de6efe5d18c2 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
> +	tristate "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..d6a9209e08c0 100644
> --- a/arch/powerpc/platforms/pseries/Makefile
> +++ b/arch/powerpc/platforms/pseries/Makefile
> @@ -37,3 +37,5 @@ obj-$(CONFIG_ARCH_HAS_CC_PLATFORM)	+= cc_platform.o
>   # nothing that operates in real mode is safe for KASAN
>   KASAN_SANITIZE_ras.o := n
>   KASAN_SANITIZE_kexec.o := n
> +
> +obj-$(CONFIG_PSERIES_PLPKS)      += plpks/
> diff --git a/arch/powerpc/platforms/pseries/plpks/Makefile b/arch/powerpc/platforms/pseries/plpks/Makefile
> new file mode 100644
> index 000000000000..e651ace920db
> --- /dev/null
> +++ b/arch/powerpc/platforms/pseries/plpks/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +#
> +# Copyright (C) 2022 IBM Corporation
> +# Author: Nayna Jain <nayna@linux.ibm.com>
> +#
> +
> +obj-$(CONFIG_PSERIES_PLPKS)  += plpks.o
> diff --git a/arch/powerpc/platforms/pseries/plpks/plpks.c b/arch/powerpc/platforms/pseries/plpks/plpks.c
> new file mode 100644
> index 000000000000..463ce93f9066
> --- /dev/null
> +++ b/arch/powerpc/platforms/pseries/plpks/plpks.c
> @@ -0,0 +1,509 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * POWER LPAR Platform KeyStore (PLPKS)
> + * Copyright (C) 2022 IBM Corporation
> + * Author: Nayna Jain <nayna@linux.ibm.com>
> + *
> + * Provides access to variables stored in Power LPAR Platform KeyStore(PLPKS).
> + */
> +
> +#include <linux/module.h>
> +#include <linux/types.h>
> +#include <linux/errno.h>
> +#include <linux/string.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>
> +#include <asm/hvcall.h>
> +#include <asm/plpks.h>
> +#include <asm/unaligned.h>
> +#include <asm/machdep.h>
> +
> +#define MODULE_VERS "1.0"
> +#define MODULE_NAME "pseries-plpks"
> +
> +#define PKS_FW_OWNER   0x1
> +#define PKS_BOOTLOADER_OWNER   0x2
> +#define PKS_OS_OWNER   0x3
> +
> +#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 bool configset;
> +static struct plpks_config *config;
> +static u8 *ospassword;
> +static u16 ospasswordlength;
> +
> +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];
> +};
> +
> +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_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;
> +	default:
> +		err = -EINVAL;
> +	}
> +
> +	return err;
> +}

Shouldn't we handle other platform return codes, e.g.: H_STATE,
H_IN_USE?

> +
> +static int plpks_gen_password(void)
> +{
> +	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};
> +	u8 consumer = PKS_OS_OWNER;
> +	int rc;
> +
> +	ospassword = kzalloc(config->maxpwsize, GFP_KERNEL);
> +	if (!ospassword)
> +		return -ENOMEM;
> +
> +	ospasswordlength = config->maxpwsize;
> +	rc = plpar_hcall(H_PKS_GEN_PASSWORD,
> +			 retbuf,
> +			 consumer,
> +			 0,
> +			 virt_to_phys(ospassword),
> +			 config->maxpwsize);
> +
> +	return pseries_status_to_err(rc);
> +}
> +
> +static int construct_auth(u8 consumer, struct plpks_auth **auth)
> +{
> +	pr_debug("max password size is %u\n", config->maxpwsize);
> +
> +	if (!auth || consumer > 3)
> +		return -EINVAL;
> +
> +	*auth = kmalloc(struct_size(*auth, password, config->maxpwsize),
> +			GFP_KERNEL);
> +	if (!*auth)
> +		return -ENOMEM;
> +
> +	(*auth)->version = 1;
> +	(*auth)->consumer = consumer;
> +	(*auth)->rsvd0 = 0;
> +	(*auth)->rsvd1 = 0;
> +	if (consumer == PKS_FW_OWNER || consumer == PKS_BOOTLOADER_OWNER) {
> +		pr_debug("consumer is bootloader or firmware\n");
> +		(*auth)->passwordlength = 0;
> +		return 0;
> +	}
> +
> +	(*auth)->passwordlength = (__force __be16)ospasswordlength;
> +
> +	memcpy((*auth)->password, ospassword,
> +	       flex_array_size(*auth, password,
> +	       (__force u16)((*auth)->passwordlength)));
> +	(*auth)->passwordlength = cpu_to_be16((__force u16)((*auth)->passwordlength));
> +
> +	return 0;
> +}
> +
> +/**
> + * Label is combination of label attributes + name.
> + * Label attributes are used internally by kernel and not exposed to the user.
> + */
> +static int construct_label(char *component, u8 varos, u8 *name, u16 namelen, u8 **label)
> +{
> +	int varlen;
> +	int len = 0;
> +	int llen = 0;
> +	int i;
> +	int rc = 0;
> +	u8 labellength = MAX_LABEL_ATTR_SIZE;
> +
> +	if (!label)
> +		return -EINVAL;
> +
> +	varlen = namelen + sizeof(struct label_attr);
> +	*label = kzalloc(varlen, GFP_KERNEL);
> +
> +	if (!*label)
> +		return -ENOMEM;
> +
> +	if (component) {
> +		len = strlen(component);
> +		memcpy(*label, component, len);
> +	}
> +	llen = len;
> +
> +	if (component)
> +		len = 8 - strlen(component);
> +	else
> +		len = 8;
> +
> +	memset(*label + llen, 0, len);
> +	llen = llen + len;
> +
> +	((*label)[llen]) = 0;
> +	llen = llen + 1;
> +
> +	memcpy(*label + llen, &varos, 1);
> +	llen = llen + 1;
> +
> +	memcpy(*label + llen, &labellength, 1);
> +	llen = llen + 1;
> +
> +	memset(*label + llen, 0, 5);
> +	llen = llen + 5;
> +
> +	memcpy(*label + llen, name, namelen);
> +	llen = llen + namelen;
> +
> +	for (i = 0; i < llen; i++)
> +		pr_debug("%c", (*label)[i]);
> +
> +	rc = llen;
> +	return rc;
> +}
> +
> +static int _plpks_get_config(void)
> +{
> +	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};
> +	int rc;
> +	size_t size = sizeof(struct plpks_config);
> +
> +	config = kzalloc(size, GFP_KERNEL);
> +	if (!config)
> +		return -ENOMEM;
> +
> +	rc = plpar_hcall(H_PKS_GET_CONFIG,
> +			 retbuf,
> +			 virt_to_phys(config),
> +			 size);
> +
> +	if (rc != H_SUCCESS)
> +		return pseries_status_to_err(rc);
> +
> +	config->rsvd0 = be32_to_cpu((__force __be32)config->rsvd0);
> +	config->maxpwsize = be16_to_cpu((__force __be16)config->maxpwsize);
> +	config->maxobjlabelsize = be16_to_cpu((__force __be16)config->maxobjlabelsize);
> +	config->maxobjsize = be16_to_cpu((__force __be16)config->maxobjsize);
> +	config->totalsize = be32_to_cpu((__force __be32)config->totalsize);
> +	config->usedspace = be32_to_cpu((__force __be32)config->usedspace);
> +	config->supportedpolicies = be32_to_cpu((__force __be32)config->supportedpolicies);
> +	config->rsvd1 = be64_to_cpu((__force __be64)config->rsvd1);
> +
> +	configset = true;
> +
> +	return 0;
> +}
> +
> +static int plpks_confirm_object_flushed(u8 *label, u16 labellen)
> +{
> +	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};
> +	int rc;
> +	u64 timeout = 0;
> +	struct plpks_auth *auth;
> +	u8 status;
> +	int i;
> +
> +	rc = construct_auth(PKS_OS_OWNER, &auth);
> +	if (rc)
> +		return rc;
> +
> +	for (i = 0; i < labellen; i++)
> +		pr_debug("%02x ", label[i]);
> +
> +	do {
> +		rc = plpar_hcall(H_PKS_CONFIRM_OBJECT_FLUSHED,
> +				 retbuf,
> +				 virt_to_phys(auth),
> +				 virt_to_phys(label),
> +				 labellen);
> +
> +		status = retbuf[0];
> +		if (rc) {
> +			pr_info("rc is %d, status is %d\n", rc, status);
> +			if (rc == H_NOT_FOUND && status == 1)
> +				rc = 0;
> +			break;
> +		}
> +
> +		pr_debug("rc is %d, status is %d\n", rc, status);
> +
> +		if (!rc && status == 1)
> +			break;
> +
> +		usleep_range(PKS_FLUSH_SLEEP, PKS_FLUSH_SLEEP + PKS_FLUSH_SLEEP_RANGE);
> +		timeout = timeout + PKS_FLUSH_SLEEP;
> +		pr_debug("timeout is %llu\n", timeout);
> +
> +	} while (timeout < PKS_FLUSH_MAX_TIMEOUT);
> +
> +	rc = pseries_status_to_err(rc);
> +
> +	kfree(auth);
> +
> +	return rc;
> +}
> +
> +int plpks_write_var(struct plpks_var var)
> +{
> +	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};
> +	int rc;
> +	u8 *label;
> +	u16 varlen;
> +	u8 *data = var.data;
> +	struct plpks_auth *auth;
> +
> +	if (!var.component || !data || var.datalen <= 0 ||
> +	    var.namelen > MAX_NAME_SIZE ||
> +	    var.datalen > MAX_DATA_SIZE)
> +		return -EINVAL;
> +
> +	if (var.policy & SIGNEDUPDATE)
> +		return -EINVAL;
> +
> +	rc = construct_auth(PKS_OS_OWNER, &auth);
> +	if (rc)
> +		return rc;
> +
> +	rc = construct_label(var.component, var.os, var.name, var.namelen,
> +			     &label);
> +	if (rc <= 0)
> +		goto out;
> +
> +	varlen =  rc;
> +	pr_debug("Name to be written is of label size %d\n", varlen);
> +	rc = plpar_hcall(H_PKS_WRITE_OBJECT,
> +			 retbuf,
> +			 virt_to_phys(auth),
> +			 virt_to_phys(label),
> +			 varlen,
> +			 var.policy,
> +			 virt_to_phys(data),
> +			 var.datalen);
> +
> +	if (!rc)
> +		rc = plpks_confirm_object_flushed(label, varlen);
> +
> +	rc = pseries_status_to_err(rc);
> +	kfree(label);
> +
> +out:
> +	kfree(auth);
> +
> +	return rc;
> +}
> +EXPORT_SYMBOL(plpks_write_var);
> +
> +int plpks_remove_var(char *component, u8 varos, struct plpks_var_name vname)
> +{
> +	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};
> +	int rc;
> +	u8 *label;
> +	u16 varlen;
> +	struct plpks_auth *auth;
> +
> +	if (!component || vname.namelen > MAX_NAME_SIZE)
> +		return -EINVAL;
> +
> +	rc = construct_auth(PKS_OS_OWNER, &auth);
> +	if (rc)
> +		return rc;
> +
> +	rc = construct_label(component, varos, vname.name, vname.namelen, &label);
> +	if (rc <= 0)
> +		goto out;
> +
> +	varlen = rc;
> +	pr_debug("Name to be written is of label size %d\n", varlen);
> +	rc = plpar_hcall(H_PKS_REMOVE_OBJECT,
> +			 retbuf,
> +			 virt_to_phys(auth),
> +			 virt_to_phys(label),
> +			 varlen);
> +
> +	if (!rc)
> +		rc = plpks_confirm_object_flushed(label, varlen);
> +
> +	rc = pseries_status_to_err(rc);
> +	kfree(label);
> +
> +out:
> +	kfree(auth);
> +
> +	return rc;
> +}
> +EXPORT_SYMBOL(plpks_remove_var);
> +
> +static int plpks_read_var(u8 consumer, struct plpks_var *var)
> +{
> +	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = {0};
> +	int rc;
> +	u16 outlen = config->maxobjsize;
> +	u8 *label;
> +	u8 *out;
> +	u16 varlen;
> +	struct plpks_auth *auth;
> +
> +	if (var->namelen > MAX_NAME_SIZE)
> +		return -EINVAL;
> +
> +	rc = construct_auth(PKS_OS_OWNER, &auth);
> +	if (rc)
> +		return rc;
> +
> +	rc = construct_label(var->component, var->os, var->name, var->namelen,
> +			     &label);
> +	if (rc <= 0)
> +		goto out;
> +
> +	varlen = rc;
> +	pr_debug("Name to be written is of label size %d\n", varlen);
> +	out = kzalloc(outlen, GFP_KERNEL);
> +	if (!out)
> +		goto out1;
> +
> +	rc = plpar_hcall(H_PKS_READ_OBJECT,
> +			 retbuf,
> +			 virt_to_phys(auth),
> +			 virt_to_phys(label),
> +			 varlen,
> +			 virt_to_phys(out),
> +			 outlen);
> +
> +	if (rc != H_SUCCESS) {
> +		pr_err("Failed to read %d\n", rc);
> +		rc = pseries_status_to_err(rc);
> +		goto out2;
> +	}
> +
> +	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 out2;
> +	}
> +	var->policy = retbuf[1];
> +
> +	memcpy(var->data, out, var->datalen);
> +
> +out2:
> +	kfree(out);
> +
> +out1:
> +	kfree(label);
> +
> +out:
> +	kfree(auth);
> +
> +	return rc;
> +}
> +
> +int plpks_read_os_var(struct plpks_var *var)
> +{
> +	return plpks_read_var(PKS_OS_OWNER, var);
> +}
> +EXPORT_SYMBOL(plpks_read_os_var);
> +
> +int plpks_read_fw_var(struct plpks_var *var)
> +{
> +	return plpks_read_var(PKS_FW_OWNER, var);
> +}
> +EXPORT_SYMBOL(plpks_read_fw_var);
> +
> +int plpks_read_bootloader_var(struct plpks_var *var)
> +{
> +	return plpks_read_var(PKS_BOOTLOADER_OWNER, var);
> +}
> +EXPORT_SYMBOL(plpks_read_bootloader_var);
> +
> +struct plpks_config *plpks_get_config(void)
> +{
> +	if (!configset) {
> +		if (_plpks_get_config())
> +			return NULL;
> +	}
> +
> +	return config;
> +}
> +EXPORT_SYMBOL(plpks_get_config);
> +
> +static __init int pseries_plpks_init(void)
> +{
> +	int rc = 0;
> +
> +	rc = _plpks_get_config();
> +
> +	if (rc) {
> +		pr_err("Error initializing plpks\n");
> +		return rc;
> +	}
> +
> +	rc = plpks_gen_password();
> +	if (rc) {
> +		if (rc == H_IN_USE) {

The rc value will never be H_IN_USE here because
pseries_status_to_err() is called within plpks_gen_password() before
returning and the plpar_hcall() return code is not propagated. You
will get -EINVAL here if plpar_hcall() returned H_IN_USE.

I think we could enhance the error handling to print messages like
"PKS not supported", "PKS access blocked due to partition state", and
"PKS password already in use". I bet these messages will help
troubleshooting issues.

> +			rc = 0;
> +		} else {
> +			pr_err("Failed setting password %d\n", rc);
> +			rc = pseries_status_to_err(rc);

Here, pseries_status_to_err() is called again (it was already called
within plpks_gen_password()). After calling it twice, the rc will
always be -EINVAL here, won't it?

> +			return rc;
> +		}
> +	}
> +
> +	pr_info("POWER LPAR Platform Keystore initialized successfully\n");
> +
> +	return rc;
> +}
> +arch_initcall(pseries_plpks_init);

--
Murilo


-- 
Murilo

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2022-07-20 16:30 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-07-13  0:59 [PATCH 0/2] Provide PowerVM LPAR Platform KeyStore driver for Self Encrypting Drives Nayna Jain
2022-07-13  0:59 ` [PATCH 1/2] powerpc/pseries: define driver for Platform KeyStore Nayna Jain
2022-07-18 20:58   ` Gregory Joyce
2022-07-18 22:30   ` Gregory Joyce
2022-07-19 19:09   ` Eric Richter
2022-07-20 16:29   ` Murilo Opsfelder Araújo
2022-07-13  0:59 ` [PATCH 2/2] powerpc/pseries: kernel interfaces to PLPKS platform driver Nayna Jain

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).