All of lore.kernel.org
 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; +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; +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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.