All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/7] pseries dynamic secure boot interface using secvar
@ 2022-12-30  4:20 ` Russell Currey
  0 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2022-12-30  4:20 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, ajd, zohar, mpe, Russell Currey

Changes in v2:

    Remove unnecessary config vars from sysfs and document the others,
    thanks to review from Greg.  If we end up needing to expose more, we
    can add them later and update the docs.

    Use sysfs_emit() instead of sprintf() for all sysfs strings

    Change the size of the sysfs binary attributes to include the 8-byte
    flags header, preventing truncation of large writes.

This series exposes an interface to userspace for reading and writing
secure variables contained within the PowerVM LPAR Platform KeyStore
(PLPKS) for the purpose of configuring dynamic secure boot.

This series builds on past work by Nayna Jain[0] in exposing PLPKS
variables to userspace.  Rather than being a generic interface for
interacting with the keystore, however, we use the existing powerpc
secvar infrastructure to only expose objects in the keystore used
for dynamic secure boot.  This has the benefit of leveraging an
existing interface and making the implementation relatively minimal.

This series needs to be applied on top of Andrew's recent bugfix
series[1].

There are a few relevant details to note about the implementation:

 * New additions to the secvar API, format() and max_size()
 * New optional sysfs directory "config/" for arbitrary ASCII variables
 * Some OPAL-specific code has been relocated from secvar-sysfs.c to
	powernv platform code.  Would appreciate any powernv testing!
 * Variable names are fixed and only those used for secure boot are
 	exposed.  This is not a generic PLPKS interface, but also
	doesn't preclude one being added in future.

With this series, both powernv and pseries platforms support dynamic
secure boot through the same interface.

[0]: https://lore.kernel.org/linuxppc-dev/20221106210744.603240-1-nayna@linux.ibm.com/
[1]: https://lore.kernel.org/linuxppc-dev/20221220071626.1426786-1-ajd@linux.ibm.com/

v1: https://lore.kernel.org/linuxppc-dev/20221228072943.429266-1-ruscur@russell.cc/

Russell Currey (7):
  powerpc/pseries: Log hcall return codes for PLPKS debug
  powerpc/secvar: WARN_ON_ONCE() if multiple secvar ops are set
  powerpc/secvar: Use sysfs_emit() instead of sprintf()
  powerpc/secvar: Handle format string in the consumer
  powerpc/secvar: Handle max object size in the consumer
  powerpc/secvar: Extend sysfs to include config vars
  powerpc/pseries: Implement secvars for dynamic secure boot

 Documentation/ABI/testing/sysfs-secvar        |  67 ++++-
 arch/powerpc/include/asm/secvar.h             |   5 +
 arch/powerpc/kernel/secvar-ops.c              |   4 +-
 arch/powerpc/kernel/secvar-sysfs.c            |  78 +++---
 arch/powerpc/platforms/powernv/opal-secvar.c  |  44 ++++
 arch/powerpc/platforms/pseries/Kconfig        |  13 +
 arch/powerpc/platforms/pseries/Makefile       |   4 +-
 arch/powerpc/platforms/pseries/plpks-secvar.c | 245 ++++++++++++++++++
 arch/powerpc/platforms/pseries/plpks.c        |   2 +
 9 files changed, 419 insertions(+), 43 deletions(-)
 create mode 100644 arch/powerpc/platforms/pseries/plpks-secvar.c

-- 
2.38.1


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

* [PATCH v2 0/7] pseries dynamic secure boot interface using secvar
@ 2022-12-30  4:20 ` Russell Currey
  0 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2022-12-30  4:20 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: ajd, gregkh, nayna, linux-kernel, zohar, Russell Currey, gcwilson

Changes in v2:

    Remove unnecessary config vars from sysfs and document the others,
    thanks to review from Greg.  If we end up needing to expose more, we
    can add them later and update the docs.

    Use sysfs_emit() instead of sprintf() for all sysfs strings

    Change the size of the sysfs binary attributes to include the 8-byte
    flags header, preventing truncation of large writes.

This series exposes an interface to userspace for reading and writing
secure variables contained within the PowerVM LPAR Platform KeyStore
(PLPKS) for the purpose of configuring dynamic secure boot.

This series builds on past work by Nayna Jain[0] in exposing PLPKS
variables to userspace.  Rather than being a generic interface for
interacting with the keystore, however, we use the existing powerpc
secvar infrastructure to only expose objects in the keystore used
for dynamic secure boot.  This has the benefit of leveraging an
existing interface and making the implementation relatively minimal.

This series needs to be applied on top of Andrew's recent bugfix
series[1].

There are a few relevant details to note about the implementation:

 * New additions to the secvar API, format() and max_size()
 * New optional sysfs directory "config/" for arbitrary ASCII variables
 * Some OPAL-specific code has been relocated from secvar-sysfs.c to
	powernv platform code.  Would appreciate any powernv testing!
 * Variable names are fixed and only those used for secure boot are
 	exposed.  This is not a generic PLPKS interface, but also
	doesn't preclude one being added in future.

With this series, both powernv and pseries platforms support dynamic
secure boot through the same interface.

[0]: https://lore.kernel.org/linuxppc-dev/20221106210744.603240-1-nayna@linux.ibm.com/
[1]: https://lore.kernel.org/linuxppc-dev/20221220071626.1426786-1-ajd@linux.ibm.com/

v1: https://lore.kernel.org/linuxppc-dev/20221228072943.429266-1-ruscur@russell.cc/

Russell Currey (7):
  powerpc/pseries: Log hcall return codes for PLPKS debug
  powerpc/secvar: WARN_ON_ONCE() if multiple secvar ops are set
  powerpc/secvar: Use sysfs_emit() instead of sprintf()
  powerpc/secvar: Handle format string in the consumer
  powerpc/secvar: Handle max object size in the consumer
  powerpc/secvar: Extend sysfs to include config vars
  powerpc/pseries: Implement secvars for dynamic secure boot

 Documentation/ABI/testing/sysfs-secvar        |  67 ++++-
 arch/powerpc/include/asm/secvar.h             |   5 +
 arch/powerpc/kernel/secvar-ops.c              |   4 +-
 arch/powerpc/kernel/secvar-sysfs.c            |  78 +++---
 arch/powerpc/platforms/powernv/opal-secvar.c  |  44 ++++
 arch/powerpc/platforms/pseries/Kconfig        |  13 +
 arch/powerpc/platforms/pseries/Makefile       |   4 +-
 arch/powerpc/platforms/pseries/plpks-secvar.c | 245 ++++++++++++++++++
 arch/powerpc/platforms/pseries/plpks.c        |   2 +
 9 files changed, 419 insertions(+), 43 deletions(-)
 create mode 100644 arch/powerpc/platforms/pseries/plpks-secvar.c

-- 
2.38.1


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

* [PATCH v2 1/7] powerpc/pseries: Log hcall return codes for PLPKS debug
  2022-12-30  4:20 ` Russell Currey
@ 2022-12-30  4:20   ` Russell Currey
  -1 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2022-12-30  4:20 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, ajd, zohar, mpe, Russell Currey

The plpks code converts hypervisor return codes into their Linux
equivalents so that users can understand them.  Having access to the
original return codes is really useful for debugging, so add a
pr_debug() so we don't lose information from the conversion.

Signed-off-by: Russell Currey <ruscur@russell.cc>
---
 arch/powerpc/platforms/pseries/plpks.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/powerpc/platforms/pseries/plpks.c b/arch/powerpc/platforms/pseries/plpks.c
index 9e4401aabf4f..820218eb894f 100644
--- a/arch/powerpc/platforms/pseries/plpks.c
+++ b/arch/powerpc/platforms/pseries/plpks.c
@@ -131,6 +131,8 @@ static int pseries_status_to_err(int rc)
 		err = -EINVAL;
 	}
 
+	pr_debug("Converted hypervisor code %d to Linux %d\n", rc, err);
+
 	return err;
 }
 
-- 
2.38.1


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

* [PATCH v2 1/7] powerpc/pseries: Log hcall return codes for PLPKS debug
@ 2022-12-30  4:20   ` Russell Currey
  0 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2022-12-30  4:20 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: ajd, gregkh, nayna, linux-kernel, zohar, Russell Currey, gcwilson

The plpks code converts hypervisor return codes into their Linux
equivalents so that users can understand them.  Having access to the
original return codes is really useful for debugging, so add a
pr_debug() so we don't lose information from the conversion.

Signed-off-by: Russell Currey <ruscur@russell.cc>
---
 arch/powerpc/platforms/pseries/plpks.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/powerpc/platforms/pseries/plpks.c b/arch/powerpc/platforms/pseries/plpks.c
index 9e4401aabf4f..820218eb894f 100644
--- a/arch/powerpc/platforms/pseries/plpks.c
+++ b/arch/powerpc/platforms/pseries/plpks.c
@@ -131,6 +131,8 @@ static int pseries_status_to_err(int rc)
 		err = -EINVAL;
 	}
 
+	pr_debug("Converted hypervisor code %d to Linux %d\n", rc, err);
+
 	return err;
 }
 
-- 
2.38.1


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

* [PATCH v2 2/7] powerpc/secvar: WARN_ON_ONCE() if multiple secvar ops are set
  2022-12-30  4:20 ` Russell Currey
@ 2022-12-30  4:20   ` Russell Currey
  -1 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2022-12-30  4:20 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, ajd, zohar, mpe, Russell Currey

The secvar code only supports one consumer at a time.

Multiple consumers aren't possible at this point in time, but we'd want
it to be obvious if it ever could happen.

Signed-off-by: Russell Currey <ruscur@russell.cc>
---
 arch/powerpc/kernel/secvar-ops.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/arch/powerpc/kernel/secvar-ops.c b/arch/powerpc/kernel/secvar-ops.c
index 6a29777d6a2d..aa1b2adc2710 100644
--- a/arch/powerpc/kernel/secvar-ops.c
+++ b/arch/powerpc/kernel/secvar-ops.c
@@ -8,10 +8,12 @@
 
 #include <linux/cache.h>
 #include <asm/secvar.h>
+#include <asm/bug.h>
 
-const struct secvar_operations *secvar_ops __ro_after_init;
+const struct secvar_operations *secvar_ops __ro_after_init = NULL;
 
 void set_secvar_ops(const struct secvar_operations *ops)
 {
+	WARN_ON_ONCE(secvar_ops);
 	secvar_ops = ops;
 }
-- 
2.38.1


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

* [PATCH v2 2/7] powerpc/secvar: WARN_ON_ONCE() if multiple secvar ops are set
@ 2022-12-30  4:20   ` Russell Currey
  0 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2022-12-30  4:20 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: ajd, gregkh, nayna, linux-kernel, zohar, Russell Currey, gcwilson

The secvar code only supports one consumer at a time.

Multiple consumers aren't possible at this point in time, but we'd want
it to be obvious if it ever could happen.

Signed-off-by: Russell Currey <ruscur@russell.cc>
---
 arch/powerpc/kernel/secvar-ops.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/arch/powerpc/kernel/secvar-ops.c b/arch/powerpc/kernel/secvar-ops.c
index 6a29777d6a2d..aa1b2adc2710 100644
--- a/arch/powerpc/kernel/secvar-ops.c
+++ b/arch/powerpc/kernel/secvar-ops.c
@@ -8,10 +8,12 @@
 
 #include <linux/cache.h>
 #include <asm/secvar.h>
+#include <asm/bug.h>
 
-const struct secvar_operations *secvar_ops __ro_after_init;
+const struct secvar_operations *secvar_ops __ro_after_init = NULL;
 
 void set_secvar_ops(const struct secvar_operations *ops)
 {
+	WARN_ON_ONCE(secvar_ops);
 	secvar_ops = ops;
 }
-- 
2.38.1


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

* [PATCH v2 3/7] powerpc/secvar: Use sysfs_emit() instead of sprintf()
  2022-12-30  4:20 ` Russell Currey
@ 2022-12-30  4:20   ` Russell Currey
  -1 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2022-12-30  4:20 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, ajd, zohar, mpe, Russell Currey

The secvar format string and object size sysfs files are both ASCII
text, and should use sysfs_emit().  No functional change.

Suggested-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Russell Currey <ruscur@russell.cc>
---
v2: new

 arch/powerpc/kernel/secvar-sysfs.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/kernel/secvar-sysfs.c b/arch/powerpc/kernel/secvar-sysfs.c
index 1ee4640a2641..02e9fee1552e 100644
--- a/arch/powerpc/kernel/secvar-sysfs.c
+++ b/arch/powerpc/kernel/secvar-sysfs.c
@@ -35,7 +35,7 @@ static ssize_t format_show(struct kobject *kobj, struct kobj_attribute *attr,
 	if (rc)
 		goto out;
 
-	rc = sprintf(buf, "%s\n", format);
+	rc = sysfs_emit(buf, "%s\n", format);
 
 out:
 	of_node_put(node);
@@ -57,7 +57,7 @@ static ssize_t size_show(struct kobject *kobj, struct kobj_attribute *attr,
 		return rc;
 	}
 
-	return sprintf(buf, "%llu\n", dsize);
+	return sysfs_emit(buf, "%llu\n", dsize);
 }
 
 static ssize_t data_read(struct file *filep, struct kobject *kobj,
-- 
2.38.1


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

* [PATCH v2 3/7] powerpc/secvar: Use sysfs_emit() instead of sprintf()
@ 2022-12-30  4:20   ` Russell Currey
  0 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2022-12-30  4:20 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: ajd, gregkh, nayna, linux-kernel, zohar, Russell Currey, gcwilson

The secvar format string and object size sysfs files are both ASCII
text, and should use sysfs_emit().  No functional change.

Suggested-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Russell Currey <ruscur@russell.cc>
---
v2: new

 arch/powerpc/kernel/secvar-sysfs.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/kernel/secvar-sysfs.c b/arch/powerpc/kernel/secvar-sysfs.c
index 1ee4640a2641..02e9fee1552e 100644
--- a/arch/powerpc/kernel/secvar-sysfs.c
+++ b/arch/powerpc/kernel/secvar-sysfs.c
@@ -35,7 +35,7 @@ static ssize_t format_show(struct kobject *kobj, struct kobj_attribute *attr,
 	if (rc)
 		goto out;
 
-	rc = sprintf(buf, "%s\n", format);
+	rc = sysfs_emit(buf, "%s\n", format);
 
 out:
 	of_node_put(node);
@@ -57,7 +57,7 @@ static ssize_t size_show(struct kobject *kobj, struct kobj_attribute *attr,
 		return rc;
 	}
 
-	return sprintf(buf, "%llu\n", dsize);
+	return sysfs_emit(buf, "%llu\n", dsize);
 }
 
 static ssize_t data_read(struct file *filep, struct kobject *kobj,
-- 
2.38.1


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

* [PATCH v2 4/7] powerpc/secvar: Handle format string in the consumer
  2022-12-30  4:20 ` Russell Currey
@ 2022-12-30  4:20   ` Russell Currey
  -1 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2022-12-30  4:20 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, ajd, zohar, mpe, Russell Currey

The code that handles the format string in secvar-sysfs.c is entirely
OPAL specific, so create a new "format" op in secvar_operations to make
the secvar code more generic.  No functional change.

Signed-off-by: Russell Currey <ruscur@russell.cc>
---
 arch/powerpc/include/asm/secvar.h            |  1 +
 arch/powerpc/kernel/secvar-sysfs.c           | 21 +---------------
 arch/powerpc/platforms/powernv/opal-secvar.c | 25 ++++++++++++++++++++
 3 files changed, 27 insertions(+), 20 deletions(-)

diff --git a/arch/powerpc/include/asm/secvar.h b/arch/powerpc/include/asm/secvar.h
index 4cc35b58b986..3b7e5a3625bd 100644
--- a/arch/powerpc/include/asm/secvar.h
+++ b/arch/powerpc/include/asm/secvar.h
@@ -20,6 +20,7 @@ struct secvar_operations {
 			uint64_t keybufsize);
 	int (*set)(const char *key, uint64_t key_len, u8 *data,
 		   uint64_t data_size);
+	ssize_t (*format)(char *buf);
 };
 
 #ifdef CONFIG_PPC_SECURE_BOOT
diff --git a/arch/powerpc/kernel/secvar-sysfs.c b/arch/powerpc/kernel/secvar-sysfs.c
index 02e9fee1552e..190238f51335 100644
--- a/arch/powerpc/kernel/secvar-sysfs.c
+++ b/arch/powerpc/kernel/secvar-sysfs.c
@@ -21,26 +21,7 @@ static struct kset *secvar_kset;
 static ssize_t format_show(struct kobject *kobj, struct kobj_attribute *attr,
 			   char *buf)
 {
-	ssize_t rc = 0;
-	struct device_node *node;
-	const char *format;
-
-	node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
-	if (!of_device_is_available(node)) {
-		rc = -ENODEV;
-		goto out;
-	}
-
-	rc = of_property_read_string(node, "format", &format);
-	if (rc)
-		goto out;
-
-	rc = sysfs_emit(buf, "%s\n", format);
-
-out:
-	of_node_put(node);
-
-	return rc;
+	return secvar_ops->format(buf);
 }
 
 
diff --git a/arch/powerpc/platforms/powernv/opal-secvar.c b/arch/powerpc/platforms/powernv/opal-secvar.c
index 14133e120bdd..5e9de06b2533 100644
--- a/arch/powerpc/platforms/powernv/opal-secvar.c
+++ b/arch/powerpc/platforms/powernv/opal-secvar.c
@@ -101,10 +101,35 @@ static int opal_set_variable(const char *key, uint64_t ksize, u8 *data,
 	return opal_status_to_err(rc);
 }
 
+static ssize_t opal_secvar_format(char *buf)
+{
+	ssize_t rc = 0;
+	struct device_node *node;
+	const char *format;
+
+	node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
+	if (!of_device_is_available(node)) {
+		rc = -ENODEV;
+		goto out;
+	}
+
+	rc = of_property_read_string(node, "format", &format);
+	if (rc)
+		goto out;
+
+	rc = sysfs_emit(buf, "%s\n", format);
+
+out:
+	of_node_put(node);
+
+	return rc;
+}
+
 static const struct secvar_operations opal_secvar_ops = {
 	.get = opal_get_variable,
 	.get_next = opal_get_next_variable,
 	.set = opal_set_variable,
+	.format = opal_secvar_format,
 };
 
 static int opal_secvar_probe(struct platform_device *pdev)
-- 
2.38.1


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

* [PATCH v2 4/7] powerpc/secvar: Handle format string in the consumer
@ 2022-12-30  4:20   ` Russell Currey
  0 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2022-12-30  4:20 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: ajd, gregkh, nayna, linux-kernel, zohar, Russell Currey, gcwilson

The code that handles the format string in secvar-sysfs.c is entirely
OPAL specific, so create a new "format" op in secvar_operations to make
the secvar code more generic.  No functional change.

Signed-off-by: Russell Currey <ruscur@russell.cc>
---
 arch/powerpc/include/asm/secvar.h            |  1 +
 arch/powerpc/kernel/secvar-sysfs.c           | 21 +---------------
 arch/powerpc/platforms/powernv/opal-secvar.c | 25 ++++++++++++++++++++
 3 files changed, 27 insertions(+), 20 deletions(-)

diff --git a/arch/powerpc/include/asm/secvar.h b/arch/powerpc/include/asm/secvar.h
index 4cc35b58b986..3b7e5a3625bd 100644
--- a/arch/powerpc/include/asm/secvar.h
+++ b/arch/powerpc/include/asm/secvar.h
@@ -20,6 +20,7 @@ struct secvar_operations {
 			uint64_t keybufsize);
 	int (*set)(const char *key, uint64_t key_len, u8 *data,
 		   uint64_t data_size);
+	ssize_t (*format)(char *buf);
 };
 
 #ifdef CONFIG_PPC_SECURE_BOOT
diff --git a/arch/powerpc/kernel/secvar-sysfs.c b/arch/powerpc/kernel/secvar-sysfs.c
index 02e9fee1552e..190238f51335 100644
--- a/arch/powerpc/kernel/secvar-sysfs.c
+++ b/arch/powerpc/kernel/secvar-sysfs.c
@@ -21,26 +21,7 @@ static struct kset *secvar_kset;
 static ssize_t format_show(struct kobject *kobj, struct kobj_attribute *attr,
 			   char *buf)
 {
-	ssize_t rc = 0;
-	struct device_node *node;
-	const char *format;
-
-	node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
-	if (!of_device_is_available(node)) {
-		rc = -ENODEV;
-		goto out;
-	}
-
-	rc = of_property_read_string(node, "format", &format);
-	if (rc)
-		goto out;
-
-	rc = sysfs_emit(buf, "%s\n", format);
-
-out:
-	of_node_put(node);
-
-	return rc;
+	return secvar_ops->format(buf);
 }
 
 
diff --git a/arch/powerpc/platforms/powernv/opal-secvar.c b/arch/powerpc/platforms/powernv/opal-secvar.c
index 14133e120bdd..5e9de06b2533 100644
--- a/arch/powerpc/platforms/powernv/opal-secvar.c
+++ b/arch/powerpc/platforms/powernv/opal-secvar.c
@@ -101,10 +101,35 @@ static int opal_set_variable(const char *key, uint64_t ksize, u8 *data,
 	return opal_status_to_err(rc);
 }
 
+static ssize_t opal_secvar_format(char *buf)
+{
+	ssize_t rc = 0;
+	struct device_node *node;
+	const char *format;
+
+	node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
+	if (!of_device_is_available(node)) {
+		rc = -ENODEV;
+		goto out;
+	}
+
+	rc = of_property_read_string(node, "format", &format);
+	if (rc)
+		goto out;
+
+	rc = sysfs_emit(buf, "%s\n", format);
+
+out:
+	of_node_put(node);
+
+	return rc;
+}
+
 static const struct secvar_operations opal_secvar_ops = {
 	.get = opal_get_variable,
 	.get_next = opal_get_next_variable,
 	.set = opal_set_variable,
+	.format = opal_secvar_format,
 };
 
 static int opal_secvar_probe(struct platform_device *pdev)
-- 
2.38.1


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

* [PATCH v2 5/7] powerpc/secvar: Handle max object size in the consumer
  2022-12-30  4:20 ` Russell Currey
@ 2022-12-30  4:20   ` Russell Currey
  -1 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2022-12-30  4:20 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, ajd, zohar, mpe, Russell Currey

Currently the max object size is handled in the core secvar code with an
entirely OPAL-specific implementation, so create a new max_size() op and
move the existing implementation into the powernv platform.  Should be
no functional change.

Signed-off-by: Russell Currey <ruscur@russell.cc>
---
 arch/powerpc/include/asm/secvar.h            |  1 +
 arch/powerpc/kernel/secvar-sysfs.c           | 17 +++--------------
 arch/powerpc/platforms/powernv/opal-secvar.c | 19 +++++++++++++++++++
 3 files changed, 23 insertions(+), 14 deletions(-)

diff --git a/arch/powerpc/include/asm/secvar.h b/arch/powerpc/include/asm/secvar.h
index 3b7e5a3625bd..92d2c051918b 100644
--- a/arch/powerpc/include/asm/secvar.h
+++ b/arch/powerpc/include/asm/secvar.h
@@ -21,6 +21,7 @@ struct secvar_operations {
 	int (*set)(const char *key, uint64_t key_len, u8 *data,
 		   uint64_t data_size);
 	ssize_t (*format)(char *buf);
+	int (*max_size)(uint64_t *max_size);
 };
 
 #ifdef CONFIG_PPC_SECURE_BOOT
diff --git a/arch/powerpc/kernel/secvar-sysfs.c b/arch/powerpc/kernel/secvar-sysfs.c
index 190238f51335..aa1daec480e1 100644
--- a/arch/powerpc/kernel/secvar-sysfs.c
+++ b/arch/powerpc/kernel/secvar-sysfs.c
@@ -122,27 +122,16 @@ static struct kobj_type secvar_ktype = {
 static int update_kobj_size(void)
 {
 
-	struct device_node *node;
 	u64 varsize;
-	int rc = 0;
+	int rc = secvar_ops->max_size(&varsize);
 
-	node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
-	if (!of_device_is_available(node)) {
-		rc = -ENODEV;
-		goto out;
-	}
-
-	rc = of_property_read_u64(node, "max-var-size", &varsize);
 	if (rc)
-		goto out;
+		return rc;
 
 	data_attr.size = varsize;
 	update_attr.size = varsize;
 
-out:
-	of_node_put(node);
-
-	return rc;
+	return 0;
 }
 
 static int secvar_sysfs_load(void)
diff --git a/arch/powerpc/platforms/powernv/opal-secvar.c b/arch/powerpc/platforms/powernv/opal-secvar.c
index 5e9de06b2533..07260460e966 100644
--- a/arch/powerpc/platforms/powernv/opal-secvar.c
+++ b/arch/powerpc/platforms/powernv/opal-secvar.c
@@ -125,11 +125,30 @@ static ssize_t opal_secvar_format(char *buf)
 	return rc;
 }
 
+static int opal_secvar_max_size(uint64_t *max_size)
+{
+	int rc;
+	struct device_node *node;
+
+	node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
+	if (!of_device_is_available(node)) {
+		rc = -ENODEV;
+		goto out;
+	}
+
+	rc = of_property_read_u64(node, "max-var-size", max_size);
+
+out:
+	of_node_put(node);
+	return rc;
+}
+
 static const struct secvar_operations opal_secvar_ops = {
 	.get = opal_get_variable,
 	.get_next = opal_get_next_variable,
 	.set = opal_set_variable,
 	.format = opal_secvar_format,
+	.max_size = opal_secvar_max_size,
 };
 
 static int opal_secvar_probe(struct platform_device *pdev)
-- 
2.38.1


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

* [PATCH v2 5/7] powerpc/secvar: Handle max object size in the consumer
@ 2022-12-30  4:20   ` Russell Currey
  0 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2022-12-30  4:20 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: ajd, gregkh, nayna, linux-kernel, zohar, Russell Currey, gcwilson

Currently the max object size is handled in the core secvar code with an
entirely OPAL-specific implementation, so create a new max_size() op and
move the existing implementation into the powernv platform.  Should be
no functional change.

Signed-off-by: Russell Currey <ruscur@russell.cc>
---
 arch/powerpc/include/asm/secvar.h            |  1 +
 arch/powerpc/kernel/secvar-sysfs.c           | 17 +++--------------
 arch/powerpc/platforms/powernv/opal-secvar.c | 19 +++++++++++++++++++
 3 files changed, 23 insertions(+), 14 deletions(-)

diff --git a/arch/powerpc/include/asm/secvar.h b/arch/powerpc/include/asm/secvar.h
index 3b7e5a3625bd..92d2c051918b 100644
--- a/arch/powerpc/include/asm/secvar.h
+++ b/arch/powerpc/include/asm/secvar.h
@@ -21,6 +21,7 @@ struct secvar_operations {
 	int (*set)(const char *key, uint64_t key_len, u8 *data,
 		   uint64_t data_size);
 	ssize_t (*format)(char *buf);
+	int (*max_size)(uint64_t *max_size);
 };
 
 #ifdef CONFIG_PPC_SECURE_BOOT
diff --git a/arch/powerpc/kernel/secvar-sysfs.c b/arch/powerpc/kernel/secvar-sysfs.c
index 190238f51335..aa1daec480e1 100644
--- a/arch/powerpc/kernel/secvar-sysfs.c
+++ b/arch/powerpc/kernel/secvar-sysfs.c
@@ -122,27 +122,16 @@ static struct kobj_type secvar_ktype = {
 static int update_kobj_size(void)
 {
 
-	struct device_node *node;
 	u64 varsize;
-	int rc = 0;
+	int rc = secvar_ops->max_size(&varsize);
 
-	node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
-	if (!of_device_is_available(node)) {
-		rc = -ENODEV;
-		goto out;
-	}
-
-	rc = of_property_read_u64(node, "max-var-size", &varsize);
 	if (rc)
-		goto out;
+		return rc;
 
 	data_attr.size = varsize;
 	update_attr.size = varsize;
 
-out:
-	of_node_put(node);
-
-	return rc;
+	return 0;
 }
 
 static int secvar_sysfs_load(void)
diff --git a/arch/powerpc/platforms/powernv/opal-secvar.c b/arch/powerpc/platforms/powernv/opal-secvar.c
index 5e9de06b2533..07260460e966 100644
--- a/arch/powerpc/platforms/powernv/opal-secvar.c
+++ b/arch/powerpc/platforms/powernv/opal-secvar.c
@@ -125,11 +125,30 @@ static ssize_t opal_secvar_format(char *buf)
 	return rc;
 }
 
+static int opal_secvar_max_size(uint64_t *max_size)
+{
+	int rc;
+	struct device_node *node;
+
+	node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
+	if (!of_device_is_available(node)) {
+		rc = -ENODEV;
+		goto out;
+	}
+
+	rc = of_property_read_u64(node, "max-var-size", max_size);
+
+out:
+	of_node_put(node);
+	return rc;
+}
+
 static const struct secvar_operations opal_secvar_ops = {
 	.get = opal_get_variable,
 	.get_next = opal_get_next_variable,
 	.set = opal_set_variable,
 	.format = opal_secvar_format,
+	.max_size = opal_secvar_max_size,
 };
 
 static int opal_secvar_probe(struct platform_device *pdev)
-- 
2.38.1


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

* [PATCH v2 6/7] powerpc/secvar: Extend sysfs to include config vars
  2022-12-30  4:20 ` Russell Currey
@ 2022-12-30  4:20   ` Russell Currey
  -1 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2022-12-30  4:20 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, ajd, zohar, mpe, Russell Currey

The forthcoming pseries consumer of the secvar API wants to expose a
number of config variables.  Allowing secvar implementations to provide
their own sysfs attributes makes it easy for consumers to expose what
they need to.

This is not being used by the OPAL secvar implementation at present, and
the config directory will not be created if no attributes are set.

Signed-off-by: Russell Currey <ruscur@russell.cc>
---
I played around with adding an API call to facilitate a more generic
key/value interface for config variables and it seemed like unnecessary
complexity.  I think this is cleaner.  If there was ever a secvar
interface other than sysfs we'd have to rework it, though.

 arch/powerpc/include/asm/secvar.h  |  3 +++
 arch/powerpc/kernel/secvar-sysfs.c | 40 ++++++++++++++++++++++++++----
 2 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/arch/powerpc/include/asm/secvar.h b/arch/powerpc/include/asm/secvar.h
index 92d2c051918b..250e7066b6da 100644
--- a/arch/powerpc/include/asm/secvar.h
+++ b/arch/powerpc/include/asm/secvar.h
@@ -10,6 +10,7 @@
 
 #include <linux/types.h>
 #include <linux/errno.h>
+#include <linux/sysfs.h>
 
 extern const struct secvar_operations *secvar_ops;
 
@@ -27,10 +28,12 @@ struct secvar_operations {
 #ifdef CONFIG_PPC_SECURE_BOOT
 
 extern void set_secvar_ops(const struct secvar_operations *ops);
+extern void set_secvar_config_attrs(const struct attribute **attrs);
 
 #else
 
 static inline void set_secvar_ops(const struct secvar_operations *ops) { }
+static inline void set_secvar_config_attrs(const struct attribute **attrs) { }
 
 #endif
 
diff --git a/arch/powerpc/kernel/secvar-sysfs.c b/arch/powerpc/kernel/secvar-sysfs.c
index aa1daec480e1..ad1e1d72d2ae 100644
--- a/arch/powerpc/kernel/secvar-sysfs.c
+++ b/arch/powerpc/kernel/secvar-sysfs.c
@@ -15,9 +15,17 @@
 
 #define NAME_MAX_SIZE	   1024
 
+const struct attribute **secvar_config_attrs __ro_after_init = NULL;
+
 static struct kobject *secvar_kobj;
 static struct kset *secvar_kset;
 
+void set_secvar_config_attrs(const struct attribute **attrs)
+{
+	WARN_ON_ONCE(secvar_config_attrs);
+	secvar_config_attrs = attrs;
+}
+
 static ssize_t format_show(struct kobject *kobj, struct kobj_attribute *attr,
 			   char *buf)
 {
@@ -134,6 +142,16 @@ static int update_kobj_size(void)
 	return 0;
 }
 
+static int secvar_sysfs_config(struct kobject *kobj)
+{
+	struct attribute_group config_group = {
+		.name = "config",
+		.attrs = (struct attribute **)secvar_config_attrs,
+	};
+
+	return sysfs_create_group(kobj, &config_group);
+}
+
 static int secvar_sysfs_load(void)
 {
 	char *name;
@@ -196,26 +214,38 @@ static int secvar_sysfs_init(void)
 
 	rc = sysfs_create_file(secvar_kobj, &format_attr.attr);
 	if (rc) {
-		kobject_put(secvar_kobj);
-		return -ENOMEM;
+		pr_err("secvar: Failed to create format object\n");
+		rc = -ENOMEM;
+		goto err;
 	}
 
 	secvar_kset = kset_create_and_add("vars", NULL, secvar_kobj);
 	if (!secvar_kset) {
 		pr_err("secvar: sysfs kobject registration failed.\n");
-		kobject_put(secvar_kobj);
-		return -ENOMEM;
+		rc = -ENOMEM;
+		goto err;
 	}
 
 	rc = update_kobj_size();
 	if (rc) {
 		pr_err("Cannot read the size of the attribute\n");
-		return rc;
+		goto err;
+	}
+
+	if (secvar_config_attrs) {
+		rc = secvar_sysfs_config(secvar_kobj);
+		if (rc) {
+			pr_err("secvar: Failed to create config directory\n");
+			goto err;
+		}
 	}
 
 	secvar_sysfs_load();
 
 	return 0;
+err:
+	kobject_put(secvar_kobj);
+	return rc;
 }
 
 late_initcall(secvar_sysfs_init);
-- 
2.38.1


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

* [PATCH v2 6/7] powerpc/secvar: Extend sysfs to include config vars
@ 2022-12-30  4:20   ` Russell Currey
  0 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2022-12-30  4:20 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: ajd, gregkh, nayna, linux-kernel, zohar, Russell Currey, gcwilson

The forthcoming pseries consumer of the secvar API wants to expose a
number of config variables.  Allowing secvar implementations to provide
their own sysfs attributes makes it easy for consumers to expose what
they need to.

This is not being used by the OPAL secvar implementation at present, and
the config directory will not be created if no attributes are set.

Signed-off-by: Russell Currey <ruscur@russell.cc>
---
I played around with adding an API call to facilitate a more generic
key/value interface for config variables and it seemed like unnecessary
complexity.  I think this is cleaner.  If there was ever a secvar
interface other than sysfs we'd have to rework it, though.

 arch/powerpc/include/asm/secvar.h  |  3 +++
 arch/powerpc/kernel/secvar-sysfs.c | 40 ++++++++++++++++++++++++++----
 2 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/arch/powerpc/include/asm/secvar.h b/arch/powerpc/include/asm/secvar.h
index 92d2c051918b..250e7066b6da 100644
--- a/arch/powerpc/include/asm/secvar.h
+++ b/arch/powerpc/include/asm/secvar.h
@@ -10,6 +10,7 @@
 
 #include <linux/types.h>
 #include <linux/errno.h>
+#include <linux/sysfs.h>
 
 extern const struct secvar_operations *secvar_ops;
 
@@ -27,10 +28,12 @@ struct secvar_operations {
 #ifdef CONFIG_PPC_SECURE_BOOT
 
 extern void set_secvar_ops(const struct secvar_operations *ops);
+extern void set_secvar_config_attrs(const struct attribute **attrs);
 
 #else
 
 static inline void set_secvar_ops(const struct secvar_operations *ops) { }
+static inline void set_secvar_config_attrs(const struct attribute **attrs) { }
 
 #endif
 
diff --git a/arch/powerpc/kernel/secvar-sysfs.c b/arch/powerpc/kernel/secvar-sysfs.c
index aa1daec480e1..ad1e1d72d2ae 100644
--- a/arch/powerpc/kernel/secvar-sysfs.c
+++ b/arch/powerpc/kernel/secvar-sysfs.c
@@ -15,9 +15,17 @@
 
 #define NAME_MAX_SIZE	   1024
 
+const struct attribute **secvar_config_attrs __ro_after_init = NULL;
+
 static struct kobject *secvar_kobj;
 static struct kset *secvar_kset;
 
+void set_secvar_config_attrs(const struct attribute **attrs)
+{
+	WARN_ON_ONCE(secvar_config_attrs);
+	secvar_config_attrs = attrs;
+}
+
 static ssize_t format_show(struct kobject *kobj, struct kobj_attribute *attr,
 			   char *buf)
 {
@@ -134,6 +142,16 @@ static int update_kobj_size(void)
 	return 0;
 }
 
+static int secvar_sysfs_config(struct kobject *kobj)
+{
+	struct attribute_group config_group = {
+		.name = "config",
+		.attrs = (struct attribute **)secvar_config_attrs,
+	};
+
+	return sysfs_create_group(kobj, &config_group);
+}
+
 static int secvar_sysfs_load(void)
 {
 	char *name;
@@ -196,26 +214,38 @@ static int secvar_sysfs_init(void)
 
 	rc = sysfs_create_file(secvar_kobj, &format_attr.attr);
 	if (rc) {
-		kobject_put(secvar_kobj);
-		return -ENOMEM;
+		pr_err("secvar: Failed to create format object\n");
+		rc = -ENOMEM;
+		goto err;
 	}
 
 	secvar_kset = kset_create_and_add("vars", NULL, secvar_kobj);
 	if (!secvar_kset) {
 		pr_err("secvar: sysfs kobject registration failed.\n");
-		kobject_put(secvar_kobj);
-		return -ENOMEM;
+		rc = -ENOMEM;
+		goto err;
 	}
 
 	rc = update_kobj_size();
 	if (rc) {
 		pr_err("Cannot read the size of the attribute\n");
-		return rc;
+		goto err;
+	}
+
+	if (secvar_config_attrs) {
+		rc = secvar_sysfs_config(secvar_kobj);
+		if (rc) {
+			pr_err("secvar: Failed to create config directory\n");
+			goto err;
+		}
 	}
 
 	secvar_sysfs_load();
 
 	return 0;
+err:
+	kobject_put(secvar_kobj);
+	return rc;
 }
 
 late_initcall(secvar_sysfs_init);
-- 
2.38.1


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

* [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
  2022-12-30  4:20 ` Russell Currey
@ 2022-12-30  4:20   ` Russell Currey
  -1 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2022-12-30  4:20 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, ajd, zohar, mpe, Russell Currey

The pseries platform can support dynamic secure boot (i.e. secure boot
using user-defined keys) using variables contained with the PowerVM LPAR
Platform KeyStore (PLPKS).  Using the powerpc secvar API, expose the
relevant variables for pseries dynamic secure boot through the existing
secvar filesystem layout.

The relevant variables for dynamic secure boot are signed in the
keystore, and can only be modified using the H_PKS_SIGNED_UPDATE hcall.
Object labels in the keystore are encoded using ucs2 format.  With our
fixed variable names we don't have to care about encoding outside of the
necessary byte padding.

When a user writes to a variable, the first 8 bytes of data must contain
the signed update flags as defined by the hypervisor.

When a user reads a variable, the first 4 bytes of data contain the
policies defined for the object.

Limitations exist due to the underlying implementation of sysfs binary
attributes, as is the case for the OPAL secvar implementation -
partial writes are unsupported and writes cannot be larger than PAGE_SIZE.

Co-developed-by: Nayna Jain <nayna@linux.ibm.com>
Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
Co-developed-by: Andrew Donnellan <ajd@linux.ibm.com>
Signed-off-by: Andrew Donnellan <ajd@linux.ibm.com>
Signed-off-by: Russell Currey <ruscur@russell.cc>
---
v2: Remove unnecessary config vars from sysfs and document the others,
    thanks to review from Greg.  If we end up needing to expose more, we
    can add them later and update the docs.

    Use sysfs_emit() instead of sprintf(), thanks to Greg.

    Change the size of the sysfs binary attributes to include the 8-byte
    flags header, preventing truncation of large writes.

 Documentation/ABI/testing/sysfs-secvar        |  67 ++++-
 arch/powerpc/platforms/pseries/Kconfig        |  13 +
 arch/powerpc/platforms/pseries/Makefile       |   4 +-
 arch/powerpc/platforms/pseries/plpks-secvar.c | 245 ++++++++++++++++++
 4 files changed, 326 insertions(+), 3 deletions(-)
 create mode 100644 arch/powerpc/platforms/pseries/plpks-secvar.c

diff --git a/Documentation/ABI/testing/sysfs-secvar b/Documentation/ABI/testing/sysfs-secvar
index feebb8c57294..466a8cb92b92 100644
--- a/Documentation/ABI/testing/sysfs-secvar
+++ b/Documentation/ABI/testing/sysfs-secvar
@@ -34,7 +34,7 @@ Description:	An integer representation of the size of the content of the
 
 What:		/sys/firmware/secvar/vars/<variable_name>/data
 Date:		August 2019
-Contact:	Nayna Jain h<nayna@linux.ibm.com>
+Contact:	Nayna Jain <nayna@linux.ibm.com>
 Description:	A read-only file containing the value of the variable. The size
 		of the file represents the maximum size of the variable data.
 
@@ -44,3 +44,68 @@ Contact:	Nayna Jain <nayna@linux.ibm.com>
 Description:	A write-only file that is used to submit the new value for the
 		variable. The size of the file represents the maximum size of
 		the variable data that can be written.
+
+What:		/sys/firmware/secvar/config
+Date:		December 2022
+Contact:	Nayna Jain <nayna@linux.ibm.com>
+Description:	This optional directory contains read-only config attributes as
+		defined by the secure variable implementation.  All data is in
+		ASCII format. The directory is only created if the backing
+		implementation provides variables to populate it, which at
+		present is only PLPKS on the pseries platform.
+
+What:		/sys/firmware/secvar/config/version
+Date:		December 2022
+Contact:	Nayna Jain <nayna@linux.ibm.com>
+Description:	RO file, only present if the secvar implementation is PLPKS.
+
+		Contains the config version as reported by the hypervisor in
+		ASCII decimal format.
+
+What:		/sys/firmware/secvar/config/max_object_size
+Date:		December 2022
+Contact:	Nayna Jain <nayna@linux.ibm.com>
+Description:	RO file, only present if the secvar implementation is PLPKS.
+
+		Contains the maximum allowed size of objects in the keystore
+		in bytes, represented in ASCII decimal format.
+
+		This is not necessarily the same as the max size that can be
+		written to an update file as writes can contain more than
+		object data, you should use the size of the update file for
+		that purpose.
+
+What:		/sys/firmware/secvar/config/total_size
+Date:		December 2022
+Contact:	Nayna Jain <nayna@linux.ibm.com>
+Description:	RO file, only present if the secvar implementation is PLPKS.
+
+		Contains the total size of the PLPKS in bytes, represented in
+		ASCII decimal format.
+
+What:		/sys/firmware/secvar/config/used_space
+Date:		December 2022
+Contact:	Nayna Jain <nayna@linux.ibm.com>
+Description:	RO file, only present if the secvar implementation is PLPKS.
+
+		Contains the current space consumed of the PLPKS in bytes,
+		represented in ASCII decimal format.
+
+What:		/sys/firmware/secvar/config/supported_policies
+Date:		December 2022
+Contact:	Nayna Jain <nayna@linux.ibm.com>
+Description:	RO file, only present if the secvar implementation is PLPKS.
+
+		Contains a bitmask of supported policy flags by the hypervisor,
+		represented as an 8 byte hexadecimal ASCII string.  Consult the
+		hypervisor documentation for what these flags are.
+
+What:		/sys/firmware/secvar/config/signed_update_algorithms
+Date:		December 2022
+Contact:	Nayna Jain <nayna@linux.ibm.com>
+Description:	RO file, only present if the secvar implementation is PLPKS.
+
+		Contains a bitmask of flags indicating which algorithms the
+		hypervisor supports objects to be signed with when modifying
+		secvars, represented as a 16 byte hexadecimal ASCII string.
+		Consult the hypervisor documentation for what these flags mean.
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index a3b4d99567cb..94e08c405d50 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -162,6 +162,19 @@ config PSERIES_PLPKS
 
 	  If unsure, select N.
 
+config PSERIES_PLPKS_SECVAR
+	depends on PSERIES_PLPKS
+	depends on PPC_SECURE_BOOT
+	bool "Support for the PLPKS secvar interface"
+	help
+	  PowerVM can support dynamic secure boot with user-defined keys
+	  through the PLPKS. Keystore objects used in dynamic secure boot
+	  can be exposed to the kernel and userspace through the powerpc
+	  secvar infrastructure. Select this to enable the PLPKS backend
+	  for secvars for use in pseries dynamic secure boot.
+
+	  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 92310202bdd7..807756991f9d 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -27,8 +27,8 @@ obj-$(CONFIG_PAPR_SCM)		+= papr_scm.o
 obj-$(CONFIG_PPC_SPLPAR)	+= vphn.o
 obj-$(CONFIG_PPC_SVM)		+= svm.o
 obj-$(CONFIG_FA_DUMP)		+= rtas-fadump.o
-obj-$(CONFIG_PSERIES_PLPKS) += plpks.o
-
+obj-$(CONFIG_PSERIES_PLPKS)	+= plpks.o
+obj-$(CONFIG_PSERIES_PLPKS_SECVAR)	+= plpks-secvar.o
 obj-$(CONFIG_SUSPEND)		+= suspend.o
 obj-$(CONFIG_PPC_VAS)		+= vas.o vas-sysfs.o
 
diff --git a/arch/powerpc/platforms/pseries/plpks-secvar.c b/arch/powerpc/platforms/pseries/plpks-secvar.c
new file mode 100644
index 000000000000..8298f039bef4
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/plpks-secvar.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Secure variable implementation using the PowerVM LPAR Platform KeyStore (PLPKS)
+ *
+ * Copyright 2022, IBM Corporation
+ * Authors: Russell Currey
+ *          Andrew Donnellan
+ *          Nayna Jain
+ */
+
+#define pr_fmt(fmt) "secvar: "fmt
+
+#include <linux/printk.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/kobject.h>
+#include <asm/secvar.h>
+#include "plpks.h"
+
+// Config attributes for sysfs
+#define PLPKS_CONFIG_ATTR(name, fmt, func)			\
+	static ssize_t name##_show(struct kobject *kobj,	\
+				   struct kobj_attribute *attr,	\
+				   char *buf)			\
+	{							\
+		return sysfs_emit(buf, fmt, func());		\
+	}							\
+	static struct kobj_attribute attr_##name = __ATTR_RO(name)
+
+PLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version);
+PLPKS_CONFIG_ATTR(max_object_size, "%u\n", plpks_get_maxobjectsize);
+PLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize);
+PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);
+PLPKS_CONFIG_ATTR(supported_policies, "%08x\n", plpks_get_supportedpolicies);
+PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n", plpks_get_signedupdatealgorithms);
+
+static const struct attribute *config_attrs[] = {
+	&attr_version.attr,
+	&attr_max_object_size.attr,
+	&attr_total_size.attr,
+	&attr_used_space.attr,
+	&attr_supported_policies.attr,
+	&attr_signed_update_algorithms.attr,
+	NULL,
+};
+
+static u16 get_ucs2name(const char *name, uint8_t **ucs2_name)
+{
+	int namelen = strlen(name) * 2;
+	*ucs2_name = kzalloc(namelen, GFP_KERNEL);
+
+	if (!*ucs2_name)
+		return 0;
+
+	for (int i = 0; name[i]; i++) {
+		(*ucs2_name)[i * 2] = name[i];
+		(*ucs2_name)[i * 2 + 1] = '\0';
+	}
+
+	return namelen;
+}
+
+static u32 get_policy(const char *name)
+{
+	if ((strcmp(name, "db") == 0) ||
+	    (strcmp(name, "dbx") == 0) ||
+	    (strcmp(name, "grubdb") == 0) ||
+	    (strcmp(name, "sbat") == 0))
+		return (WORLDREADABLE | SIGNEDUPDATE);
+	else
+		return SIGNEDUPDATE;
+}
+
+#define PLPKS_SECVAR_COUNT 8
+static char *var_names[PLPKS_SECVAR_COUNT] = {
+	"PK",
+	"KEK",
+	"db",
+	"dbx",
+	"grubdb",
+	"sbat",
+	"moduledb",
+	"trustedcadb",
+};
+
+static int plpks_get_variable(const char *key, uint64_t key_len,
+			      u8 *data, uint64_t *data_size)
+{
+	struct plpks_var var = {0};
+	u16 ucs2_namelen;
+	u8 *ucs2_name;
+	int rc = 0;
+
+	ucs2_namelen = get_ucs2name(key, &ucs2_name);
+	if (!ucs2_namelen)
+		return -ENOMEM;
+
+	var.name = ucs2_name;
+	var.namelen = ucs2_namelen;
+	var.os = PLPKS_VAR_LINUX;
+	rc = plpks_read_os_var(&var);
+
+	if (rc)
+		goto err;
+
+	*data_size = var.datalen + sizeof(var.policy);
+
+	// We can be called with data = NULL to just get the object size.
+	if (data) {
+		memcpy(data, &var.policy, sizeof(var.policy));
+		memcpy(data + sizeof(var.policy), var.data, var.datalen);
+	}
+
+	kfree(var.data);
+err:
+	kfree(ucs2_name);
+	return rc;
+}
+
+static int plpks_set_variable(const char *key, uint64_t key_len,
+			      u8 *data, uint64_t data_size)
+{
+	struct plpks_var var = {0};
+	u16 ucs2_namelen;
+	u8 *ucs2_name;
+	int rc = 0;
+	u64 flags;
+
+	// Secure variables need to be prefixed with 8 bytes of flags.
+	// We only want to perform the write if we have at least one byte of data.
+	if (data_size <= sizeof(flags))
+		return -EINVAL;
+
+	ucs2_namelen = get_ucs2name(key, &ucs2_name);
+	if (!ucs2_namelen)
+		return -ENOMEM;
+
+	memcpy(&flags, data, sizeof(flags));
+
+	var.datalen = data_size - sizeof(flags);
+	var.data = kzalloc(var.datalen, GFP_KERNEL);
+	if (!var.data) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	memcpy(var.data, data + sizeof(flags), var.datalen);
+
+	var.name = ucs2_name;
+	var.namelen = ucs2_namelen;
+	var.os = PLPKS_VAR_LINUX;
+	var.policy = get_policy(key);
+
+	rc = plpks_signed_update_var(var, flags);
+
+	kfree(var.data);
+err:
+	kfree(ucs2_name);
+	return rc;
+}
+
+/*
+ * get_next() in the secvar API is designed for the OPAL API.
+ * If *key is 0, it returns the first variable in the keystore.
+ * Otherwise, you pass the name of a key and it returns next in line.
+ *
+ * We're going to cheat here - since we have fixed keys and don't care about
+ * key_len, we can just use it as an index.
+ */
+static int plpks_get_next_variable(const char *key, uint64_t *key_len, uint64_t keybufsize)
+{
+	if (!key || !key_len)
+		return -EINVAL;
+
+	if (*key_len >= PLPKS_SECVAR_COUNT)
+		return -ENOENT;
+
+	if (strscpy((char *)key, var_names[(*key_len)++], keybufsize) < 0)
+		return -E2BIG;
+
+	return 0;
+}
+
+// PLPKS dynamic secure boot doesn't give us a format string in the same way OPAL does.
+// Instead, report the format using the SB_VERSION variable in the keystore.
+static ssize_t plpks_secvar_format(char *buf)
+{
+	struct plpks_var var = {0};
+	ssize_t ret;
+
+	var.component = NULL;
+	// Only the signed variables have ucs2-encoded names, this one doesn't
+	var.name = "SB_VERSION";
+	var.namelen = 10;
+	var.datalen = 0;
+	var.data = NULL;
+
+	// Unlike the other vars, SB_VERSION is owned by firmware instead of the OS
+	ret = plpks_read_fw_var(&var);
+	if (ret) {
+		if (ret == -ENOENT)
+			return sysfs_emit(buf, "ibm,plpks-sb-unknown\n");
+
+		pr_err("Error %ld reading SB_VERSION from firmware\n", ret);
+		return ret;
+	}
+
+	// Hypervisor defines SB_VERSION as a "1 byte unsigned integer value"
+	ret = sysfs_emit(buf, "ibm,plpks-sb-%hhu\n", var.data[0]);
+
+	kfree(var.data);
+	return ret;
+}
+
+static int plpks_max_size(uint64_t *max_size)
+{
+	// The max object size reported by the hypervisor is accurate for the
+	// object itself, but we use the first 8 bytes of data on write as the
+	// signed update flags, so the max size a user can write is larger.
+	*max_size = (uint64_t)plpks_get_maxobjectsize() + 8;
+
+	return 0;
+}
+
+
+static const struct secvar_operations plpks_secvar_ops = {
+	.get = plpks_get_variable,
+	.get_next = plpks_get_next_variable,
+	.set = plpks_set_variable,
+	.format = plpks_secvar_format,
+	.max_size = plpks_max_size,
+};
+
+static int plpks_secvar_init(void)
+{
+	if (!plpks_is_available())
+		return -ENODEV;
+
+	set_secvar_ops(&plpks_secvar_ops);
+	set_secvar_config_attrs(config_attrs);
+	return 0;
+}
+device_initcall(plpks_secvar_init);
-- 
2.38.1


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

* [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
@ 2022-12-30  4:20   ` Russell Currey
  0 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2022-12-30  4:20 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: ajd, gregkh, nayna, linux-kernel, zohar, Russell Currey, gcwilson

The pseries platform can support dynamic secure boot (i.e. secure boot
using user-defined keys) using variables contained with the PowerVM LPAR
Platform KeyStore (PLPKS).  Using the powerpc secvar API, expose the
relevant variables for pseries dynamic secure boot through the existing
secvar filesystem layout.

The relevant variables for dynamic secure boot are signed in the
keystore, and can only be modified using the H_PKS_SIGNED_UPDATE hcall.
Object labels in the keystore are encoded using ucs2 format.  With our
fixed variable names we don't have to care about encoding outside of the
necessary byte padding.

When a user writes to a variable, the first 8 bytes of data must contain
the signed update flags as defined by the hypervisor.

When a user reads a variable, the first 4 bytes of data contain the
policies defined for the object.

Limitations exist due to the underlying implementation of sysfs binary
attributes, as is the case for the OPAL secvar implementation -
partial writes are unsupported and writes cannot be larger than PAGE_SIZE.

Co-developed-by: Nayna Jain <nayna@linux.ibm.com>
Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
Co-developed-by: Andrew Donnellan <ajd@linux.ibm.com>
Signed-off-by: Andrew Donnellan <ajd@linux.ibm.com>
Signed-off-by: Russell Currey <ruscur@russell.cc>
---
v2: Remove unnecessary config vars from sysfs and document the others,
    thanks to review from Greg.  If we end up needing to expose more, we
    can add them later and update the docs.

    Use sysfs_emit() instead of sprintf(), thanks to Greg.

    Change the size of the sysfs binary attributes to include the 8-byte
    flags header, preventing truncation of large writes.

 Documentation/ABI/testing/sysfs-secvar        |  67 ++++-
 arch/powerpc/platforms/pseries/Kconfig        |  13 +
 arch/powerpc/platforms/pseries/Makefile       |   4 +-
 arch/powerpc/platforms/pseries/plpks-secvar.c | 245 ++++++++++++++++++
 4 files changed, 326 insertions(+), 3 deletions(-)
 create mode 100644 arch/powerpc/platforms/pseries/plpks-secvar.c

diff --git a/Documentation/ABI/testing/sysfs-secvar b/Documentation/ABI/testing/sysfs-secvar
index feebb8c57294..466a8cb92b92 100644
--- a/Documentation/ABI/testing/sysfs-secvar
+++ b/Documentation/ABI/testing/sysfs-secvar
@@ -34,7 +34,7 @@ Description:	An integer representation of the size of the content of the
 
 What:		/sys/firmware/secvar/vars/<variable_name>/data
 Date:		August 2019
-Contact:	Nayna Jain h<nayna@linux.ibm.com>
+Contact:	Nayna Jain <nayna@linux.ibm.com>
 Description:	A read-only file containing the value of the variable. The size
 		of the file represents the maximum size of the variable data.
 
@@ -44,3 +44,68 @@ Contact:	Nayna Jain <nayna@linux.ibm.com>
 Description:	A write-only file that is used to submit the new value for the
 		variable. The size of the file represents the maximum size of
 		the variable data that can be written.
+
+What:		/sys/firmware/secvar/config
+Date:		December 2022
+Contact:	Nayna Jain <nayna@linux.ibm.com>
+Description:	This optional directory contains read-only config attributes as
+		defined by the secure variable implementation.  All data is in
+		ASCII format. The directory is only created if the backing
+		implementation provides variables to populate it, which at
+		present is only PLPKS on the pseries platform.
+
+What:		/sys/firmware/secvar/config/version
+Date:		December 2022
+Contact:	Nayna Jain <nayna@linux.ibm.com>
+Description:	RO file, only present if the secvar implementation is PLPKS.
+
+		Contains the config version as reported by the hypervisor in
+		ASCII decimal format.
+
+What:		/sys/firmware/secvar/config/max_object_size
+Date:		December 2022
+Contact:	Nayna Jain <nayna@linux.ibm.com>
+Description:	RO file, only present if the secvar implementation is PLPKS.
+
+		Contains the maximum allowed size of objects in the keystore
+		in bytes, represented in ASCII decimal format.
+
+		This is not necessarily the same as the max size that can be
+		written to an update file as writes can contain more than
+		object data, you should use the size of the update file for
+		that purpose.
+
+What:		/sys/firmware/secvar/config/total_size
+Date:		December 2022
+Contact:	Nayna Jain <nayna@linux.ibm.com>
+Description:	RO file, only present if the secvar implementation is PLPKS.
+
+		Contains the total size of the PLPKS in bytes, represented in
+		ASCII decimal format.
+
+What:		/sys/firmware/secvar/config/used_space
+Date:		December 2022
+Contact:	Nayna Jain <nayna@linux.ibm.com>
+Description:	RO file, only present if the secvar implementation is PLPKS.
+
+		Contains the current space consumed of the PLPKS in bytes,
+		represented in ASCII decimal format.
+
+What:		/sys/firmware/secvar/config/supported_policies
+Date:		December 2022
+Contact:	Nayna Jain <nayna@linux.ibm.com>
+Description:	RO file, only present if the secvar implementation is PLPKS.
+
+		Contains a bitmask of supported policy flags by the hypervisor,
+		represented as an 8 byte hexadecimal ASCII string.  Consult the
+		hypervisor documentation for what these flags are.
+
+What:		/sys/firmware/secvar/config/signed_update_algorithms
+Date:		December 2022
+Contact:	Nayna Jain <nayna@linux.ibm.com>
+Description:	RO file, only present if the secvar implementation is PLPKS.
+
+		Contains a bitmask of flags indicating which algorithms the
+		hypervisor supports objects to be signed with when modifying
+		secvars, represented as a 16 byte hexadecimal ASCII string.
+		Consult the hypervisor documentation for what these flags mean.
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index a3b4d99567cb..94e08c405d50 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -162,6 +162,19 @@ config PSERIES_PLPKS
 
 	  If unsure, select N.
 
+config PSERIES_PLPKS_SECVAR
+	depends on PSERIES_PLPKS
+	depends on PPC_SECURE_BOOT
+	bool "Support for the PLPKS secvar interface"
+	help
+	  PowerVM can support dynamic secure boot with user-defined keys
+	  through the PLPKS. Keystore objects used in dynamic secure boot
+	  can be exposed to the kernel and userspace through the powerpc
+	  secvar infrastructure. Select this to enable the PLPKS backend
+	  for secvars for use in pseries dynamic secure boot.
+
+	  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 92310202bdd7..807756991f9d 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -27,8 +27,8 @@ obj-$(CONFIG_PAPR_SCM)		+= papr_scm.o
 obj-$(CONFIG_PPC_SPLPAR)	+= vphn.o
 obj-$(CONFIG_PPC_SVM)		+= svm.o
 obj-$(CONFIG_FA_DUMP)		+= rtas-fadump.o
-obj-$(CONFIG_PSERIES_PLPKS) += plpks.o
-
+obj-$(CONFIG_PSERIES_PLPKS)	+= plpks.o
+obj-$(CONFIG_PSERIES_PLPKS_SECVAR)	+= plpks-secvar.o
 obj-$(CONFIG_SUSPEND)		+= suspend.o
 obj-$(CONFIG_PPC_VAS)		+= vas.o vas-sysfs.o
 
diff --git a/arch/powerpc/platforms/pseries/plpks-secvar.c b/arch/powerpc/platforms/pseries/plpks-secvar.c
new file mode 100644
index 000000000000..8298f039bef4
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/plpks-secvar.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Secure variable implementation using the PowerVM LPAR Platform KeyStore (PLPKS)
+ *
+ * Copyright 2022, IBM Corporation
+ * Authors: Russell Currey
+ *          Andrew Donnellan
+ *          Nayna Jain
+ */
+
+#define pr_fmt(fmt) "secvar: "fmt
+
+#include <linux/printk.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/kobject.h>
+#include <asm/secvar.h>
+#include "plpks.h"
+
+// Config attributes for sysfs
+#define PLPKS_CONFIG_ATTR(name, fmt, func)			\
+	static ssize_t name##_show(struct kobject *kobj,	\
+				   struct kobj_attribute *attr,	\
+				   char *buf)			\
+	{							\
+		return sysfs_emit(buf, fmt, func());		\
+	}							\
+	static struct kobj_attribute attr_##name = __ATTR_RO(name)
+
+PLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version);
+PLPKS_CONFIG_ATTR(max_object_size, "%u\n", plpks_get_maxobjectsize);
+PLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize);
+PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);
+PLPKS_CONFIG_ATTR(supported_policies, "%08x\n", plpks_get_supportedpolicies);
+PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n", plpks_get_signedupdatealgorithms);
+
+static const struct attribute *config_attrs[] = {
+	&attr_version.attr,
+	&attr_max_object_size.attr,
+	&attr_total_size.attr,
+	&attr_used_space.attr,
+	&attr_supported_policies.attr,
+	&attr_signed_update_algorithms.attr,
+	NULL,
+};
+
+static u16 get_ucs2name(const char *name, uint8_t **ucs2_name)
+{
+	int namelen = strlen(name) * 2;
+	*ucs2_name = kzalloc(namelen, GFP_KERNEL);
+
+	if (!*ucs2_name)
+		return 0;
+
+	for (int i = 0; name[i]; i++) {
+		(*ucs2_name)[i * 2] = name[i];
+		(*ucs2_name)[i * 2 + 1] = '\0';
+	}
+
+	return namelen;
+}
+
+static u32 get_policy(const char *name)
+{
+	if ((strcmp(name, "db") == 0) ||
+	    (strcmp(name, "dbx") == 0) ||
+	    (strcmp(name, "grubdb") == 0) ||
+	    (strcmp(name, "sbat") == 0))
+		return (WORLDREADABLE | SIGNEDUPDATE);
+	else
+		return SIGNEDUPDATE;
+}
+
+#define PLPKS_SECVAR_COUNT 8
+static char *var_names[PLPKS_SECVAR_COUNT] = {
+	"PK",
+	"KEK",
+	"db",
+	"dbx",
+	"grubdb",
+	"sbat",
+	"moduledb",
+	"trustedcadb",
+};
+
+static int plpks_get_variable(const char *key, uint64_t key_len,
+			      u8 *data, uint64_t *data_size)
+{
+	struct plpks_var var = {0};
+	u16 ucs2_namelen;
+	u8 *ucs2_name;
+	int rc = 0;
+
+	ucs2_namelen = get_ucs2name(key, &ucs2_name);
+	if (!ucs2_namelen)
+		return -ENOMEM;
+
+	var.name = ucs2_name;
+	var.namelen = ucs2_namelen;
+	var.os = PLPKS_VAR_LINUX;
+	rc = plpks_read_os_var(&var);
+
+	if (rc)
+		goto err;
+
+	*data_size = var.datalen + sizeof(var.policy);
+
+	// We can be called with data = NULL to just get the object size.
+	if (data) {
+		memcpy(data, &var.policy, sizeof(var.policy));
+		memcpy(data + sizeof(var.policy), var.data, var.datalen);
+	}
+
+	kfree(var.data);
+err:
+	kfree(ucs2_name);
+	return rc;
+}
+
+static int plpks_set_variable(const char *key, uint64_t key_len,
+			      u8 *data, uint64_t data_size)
+{
+	struct plpks_var var = {0};
+	u16 ucs2_namelen;
+	u8 *ucs2_name;
+	int rc = 0;
+	u64 flags;
+
+	// Secure variables need to be prefixed with 8 bytes of flags.
+	// We only want to perform the write if we have at least one byte of data.
+	if (data_size <= sizeof(flags))
+		return -EINVAL;
+
+	ucs2_namelen = get_ucs2name(key, &ucs2_name);
+	if (!ucs2_namelen)
+		return -ENOMEM;
+
+	memcpy(&flags, data, sizeof(flags));
+
+	var.datalen = data_size - sizeof(flags);
+	var.data = kzalloc(var.datalen, GFP_KERNEL);
+	if (!var.data) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	memcpy(var.data, data + sizeof(flags), var.datalen);
+
+	var.name = ucs2_name;
+	var.namelen = ucs2_namelen;
+	var.os = PLPKS_VAR_LINUX;
+	var.policy = get_policy(key);
+
+	rc = plpks_signed_update_var(var, flags);
+
+	kfree(var.data);
+err:
+	kfree(ucs2_name);
+	return rc;
+}
+
+/*
+ * get_next() in the secvar API is designed for the OPAL API.
+ * If *key is 0, it returns the first variable in the keystore.
+ * Otherwise, you pass the name of a key and it returns next in line.
+ *
+ * We're going to cheat here - since we have fixed keys and don't care about
+ * key_len, we can just use it as an index.
+ */
+static int plpks_get_next_variable(const char *key, uint64_t *key_len, uint64_t keybufsize)
+{
+	if (!key || !key_len)
+		return -EINVAL;
+
+	if (*key_len >= PLPKS_SECVAR_COUNT)
+		return -ENOENT;
+
+	if (strscpy((char *)key, var_names[(*key_len)++], keybufsize) < 0)
+		return -E2BIG;
+
+	return 0;
+}
+
+// PLPKS dynamic secure boot doesn't give us a format string in the same way OPAL does.
+// Instead, report the format using the SB_VERSION variable in the keystore.
+static ssize_t plpks_secvar_format(char *buf)
+{
+	struct plpks_var var = {0};
+	ssize_t ret;
+
+	var.component = NULL;
+	// Only the signed variables have ucs2-encoded names, this one doesn't
+	var.name = "SB_VERSION";
+	var.namelen = 10;
+	var.datalen = 0;
+	var.data = NULL;
+
+	// Unlike the other vars, SB_VERSION is owned by firmware instead of the OS
+	ret = plpks_read_fw_var(&var);
+	if (ret) {
+		if (ret == -ENOENT)
+			return sysfs_emit(buf, "ibm,plpks-sb-unknown\n");
+
+		pr_err("Error %ld reading SB_VERSION from firmware\n", ret);
+		return ret;
+	}
+
+	// Hypervisor defines SB_VERSION as a "1 byte unsigned integer value"
+	ret = sysfs_emit(buf, "ibm,plpks-sb-%hhu\n", var.data[0]);
+
+	kfree(var.data);
+	return ret;
+}
+
+static int plpks_max_size(uint64_t *max_size)
+{
+	// The max object size reported by the hypervisor is accurate for the
+	// object itself, but we use the first 8 bytes of data on write as the
+	// signed update flags, so the max size a user can write is larger.
+	*max_size = (uint64_t)plpks_get_maxobjectsize() + 8;
+
+	return 0;
+}
+
+
+static const struct secvar_operations plpks_secvar_ops = {
+	.get = plpks_get_variable,
+	.get_next = plpks_get_next_variable,
+	.set = plpks_set_variable,
+	.format = plpks_secvar_format,
+	.max_size = plpks_max_size,
+};
+
+static int plpks_secvar_init(void)
+{
+	if (!plpks_is_available())
+		return -ENODEV;
+
+	set_secvar_ops(&plpks_secvar_ops);
+	set_secvar_config_attrs(config_attrs);
+	return 0;
+}
+device_initcall(plpks_secvar_init);
-- 
2.38.1


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

* Re: [PATCH v2 1/7] powerpc/pseries: Log hcall return codes for PLPKS debug
  2022-12-30  4:20   ` Russell Currey
@ 2023-01-04  4:45     ` Andrew Donnellan
  -1 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-04  4:45 UTC (permalink / raw)
  To: Russell Currey, linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, zohar, mpe

On Fri, 2022-12-30 at 15:20 +1100, Russell Currey wrote:
> The plpks code converts hypervisor return codes into their Linux
> equivalents so that users can understand them.  Having access to the
> original return codes is really useful for debugging, so add a
> pr_debug() so we don't lose information from the conversion.
> 
> Signed-off-by: Russell Currey <ruscur@russell.cc>

This is indeed useful to have.

Reviewed-by: Andrew Donnellan <ajd@linux.ibm.com>
Tested-by: Andrew Donnellan <ajd@linux.ibm.com>

> ---
>  arch/powerpc/platforms/pseries/plpks.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/arch/powerpc/platforms/pseries/plpks.c
> b/arch/powerpc/platforms/pseries/plpks.c
> index 9e4401aabf4f..820218eb894f 100644
> --- a/arch/powerpc/platforms/pseries/plpks.c
> +++ b/arch/powerpc/platforms/pseries/plpks.c
> @@ -131,6 +131,8 @@ static int pseries_status_to_err(int rc)
>                 err = -EINVAL;
>         }
>  
> +       pr_debug("Converted hypervisor code %d to Linux %d\n", rc,
> err);
> +
>         return err;
>  }
>  

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 1/7] powerpc/pseries: Log hcall return codes for PLPKS debug
@ 2023-01-04  4:45     ` Andrew Donnellan
  0 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-04  4:45 UTC (permalink / raw)
  To: Russell Currey, linuxppc-dev; +Cc: gregkh, nayna, linux-kernel, zohar, gcwilson

On Fri, 2022-12-30 at 15:20 +1100, Russell Currey wrote:
> The plpks code converts hypervisor return codes into their Linux
> equivalents so that users can understand them.  Having access to the
> original return codes is really useful for debugging, so add a
> pr_debug() so we don't lose information from the conversion.
> 
> Signed-off-by: Russell Currey <ruscur@russell.cc>

This is indeed useful to have.

Reviewed-by: Andrew Donnellan <ajd@linux.ibm.com>
Tested-by: Andrew Donnellan <ajd@linux.ibm.com>

> ---
>  arch/powerpc/platforms/pseries/plpks.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/arch/powerpc/platforms/pseries/plpks.c
> b/arch/powerpc/platforms/pseries/plpks.c
> index 9e4401aabf4f..820218eb894f 100644
> --- a/arch/powerpc/platforms/pseries/plpks.c
> +++ b/arch/powerpc/platforms/pseries/plpks.c
> @@ -131,6 +131,8 @@ static int pseries_status_to_err(int rc)
>                 err = -EINVAL;
>         }
>  
> +       pr_debug("Converted hypervisor code %d to Linux %d\n", rc,
> err);
> +
>         return err;
>  }
>  

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 2/7] powerpc/secvar: WARN_ON_ONCE() if multiple secvar ops are set
  2022-12-30  4:20   ` Russell Currey
@ 2023-01-04  7:10     ` Andrew Donnellan
  -1 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-04  7:10 UTC (permalink / raw)
  To: Russell Currey, linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, zohar, mpe

On Fri, 2022-12-30 at 15:20 +1100, Russell Currey wrote:
> The secvar code only supports one consumer at a time.
> 
> Multiple consumers aren't possible at this point in time, but we'd
> want
> it to be obvious if it ever could happen.
> 
> Signed-off-by: Russell Currey <ruscur@russell.cc>

This seems sensible - there aren't any cases where set_secvar_ops()
should be called twice.

> ---
>  arch/powerpc/kernel/secvar-ops.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/powerpc/kernel/secvar-ops.c
> b/arch/powerpc/kernel/secvar-ops.c
> index 6a29777d6a2d..aa1b2adc2710 100644
> --- a/arch/powerpc/kernel/secvar-ops.c
> +++ b/arch/powerpc/kernel/secvar-ops.c
> @@ -8,10 +8,12 @@
>  
>  #include <linux/cache.h>
>  #include <asm/secvar.h>
> +#include <asm/bug.h>
>  
> -const struct secvar_operations *secvar_ops __ro_after_init;
> +const struct secvar_operations *secvar_ops __ro_after_init = NULL;

I think this is implicitly NULL, but it's fine to make it explicit.

Reviewed-by: Andrew Donnellan <ajd@linux.ibm.com>

>  
>  void set_secvar_ops(const struct secvar_operations *ops)
>  {
> +       WARN_ON_ONCE(secvar_ops);
>         secvar_ops = ops;
>  }

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 2/7] powerpc/secvar: WARN_ON_ONCE() if multiple secvar ops are set
@ 2023-01-04  7:10     ` Andrew Donnellan
  0 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-04  7:10 UTC (permalink / raw)
  To: Russell Currey, linuxppc-dev; +Cc: gregkh, nayna, linux-kernel, zohar, gcwilson

On Fri, 2022-12-30 at 15:20 +1100, Russell Currey wrote:
> The secvar code only supports one consumer at a time.
> 
> Multiple consumers aren't possible at this point in time, but we'd
> want
> it to be obvious if it ever could happen.
> 
> Signed-off-by: Russell Currey <ruscur@russell.cc>

This seems sensible - there aren't any cases where set_secvar_ops()
should be called twice.

> ---
>  arch/powerpc/kernel/secvar-ops.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/powerpc/kernel/secvar-ops.c
> b/arch/powerpc/kernel/secvar-ops.c
> index 6a29777d6a2d..aa1b2adc2710 100644
> --- a/arch/powerpc/kernel/secvar-ops.c
> +++ b/arch/powerpc/kernel/secvar-ops.c
> @@ -8,10 +8,12 @@
>  
>  #include <linux/cache.h>
>  #include <asm/secvar.h>
> +#include <asm/bug.h>
>  
> -const struct secvar_operations *secvar_ops __ro_after_init;
> +const struct secvar_operations *secvar_ops __ro_after_init = NULL;

I think this is implicitly NULL, but it's fine to make it explicit.

Reviewed-by: Andrew Donnellan <ajd@linux.ibm.com>

>  
>  void set_secvar_ops(const struct secvar_operations *ops)
>  {
> +       WARN_ON_ONCE(secvar_ops);
>         secvar_ops = ops;
>  }

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 3/7] powerpc/secvar: Use sysfs_emit() instead of sprintf()
  2022-12-30  4:20   ` Russell Currey
@ 2023-01-04  7:12     ` Andrew Donnellan
  -1 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-04  7:12 UTC (permalink / raw)
  To: Russell Currey, linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, zohar, mpe

On Fri, 2022-12-30 at 15:20 +1100, Russell Currey wrote:
> The secvar format string and object size sysfs files are both ASCII
> text, and should use sysfs_emit().  No functional change.
> 
> Suggested-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Signed-off-by: Russell Currey <ruscur@russell.cc>

LGTM

Reviewed-by: Andrew Donnellan <ajd@linux.ibm.com>

> ---
> v2: new
> 
>  arch/powerpc/kernel/secvar-sysfs.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/powerpc/kernel/secvar-sysfs.c
> b/arch/powerpc/kernel/secvar-sysfs.c
> index 1ee4640a2641..02e9fee1552e 100644
> --- a/arch/powerpc/kernel/secvar-sysfs.c
> +++ b/arch/powerpc/kernel/secvar-sysfs.c
> @@ -35,7 +35,7 @@ static ssize_t format_show(struct kobject *kobj,
> struct kobj_attribute *attr,
>         if (rc)
>                 goto out;
>  
> -       rc = sprintf(buf, "%s\n", format);
> +       rc = sysfs_emit(buf, "%s\n", format);
>  
>  out:
>         of_node_put(node);
> @@ -57,7 +57,7 @@ static ssize_t size_show(struct kobject *kobj,
> struct kobj_attribute *attr,
>                 return rc;
>         }
>  
> -       return sprintf(buf, "%llu\n", dsize);
> +       return sysfs_emit(buf, "%llu\n", dsize);
>  }
>  
>  static ssize_t data_read(struct file *filep, struct kobject *kobj,

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 3/7] powerpc/secvar: Use sysfs_emit() instead of sprintf()
@ 2023-01-04  7:12     ` Andrew Donnellan
  0 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-04  7:12 UTC (permalink / raw)
  To: Russell Currey, linuxppc-dev; +Cc: gregkh, nayna, linux-kernel, zohar, gcwilson

On Fri, 2022-12-30 at 15:20 +1100, Russell Currey wrote:
> The secvar format string and object size sysfs files are both ASCII
> text, and should use sysfs_emit().  No functional change.
> 
> Suggested-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Signed-off-by: Russell Currey <ruscur@russell.cc>

LGTM

Reviewed-by: Andrew Donnellan <ajd@linux.ibm.com>

> ---
> v2: new
> 
>  arch/powerpc/kernel/secvar-sysfs.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/powerpc/kernel/secvar-sysfs.c
> b/arch/powerpc/kernel/secvar-sysfs.c
> index 1ee4640a2641..02e9fee1552e 100644
> --- a/arch/powerpc/kernel/secvar-sysfs.c
> +++ b/arch/powerpc/kernel/secvar-sysfs.c
> @@ -35,7 +35,7 @@ static ssize_t format_show(struct kobject *kobj,
> struct kobj_attribute *attr,
>         if (rc)
>                 goto out;
>  
> -       rc = sprintf(buf, "%s\n", format);
> +       rc = sysfs_emit(buf, "%s\n", format);
>  
>  out:
>         of_node_put(node);
> @@ -57,7 +57,7 @@ static ssize_t size_show(struct kobject *kobj,
> struct kobj_attribute *attr,
>                 return rc;
>         }
>  
> -       return sprintf(buf, "%llu\n", dsize);
> +       return sysfs_emit(buf, "%llu\n", dsize);
>  }
>  
>  static ssize_t data_read(struct file *filep, struct kobject *kobj,

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 4/7] powerpc/secvar: Handle format string in the consumer
  2022-12-30  4:20   ` Russell Currey
@ 2023-01-04  7:31     ` Andrew Donnellan
  -1 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-04  7:31 UTC (permalink / raw)
  To: Russell Currey, linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, zohar, mpe

On Fri, 2022-12-30 at 15:20 +1100, Russell Currey wrote:
> The code that handles the format string in secvar-sysfs.c is entirely
> OPAL specific, so create a new "format" op in secvar_operations to
> make
> the secvar code more generic.  No functional change.
> 
> Signed-off-by: Russell Currey <ruscur@russell.cc>

LGTM

Reviewed-by: Andrew Donnellan <ajd@linux.ibm.com>

> ---
>  arch/powerpc/include/asm/secvar.h            |  1 +
>  arch/powerpc/kernel/secvar-sysfs.c           | 21 +---------------
>  arch/powerpc/platforms/powernv/opal-secvar.c | 25
> ++++++++++++++++++++
>  3 files changed, 27 insertions(+), 20 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/secvar.h
> b/arch/powerpc/include/asm/secvar.h
> index 4cc35b58b986..3b7e5a3625bd 100644
> --- a/arch/powerpc/include/asm/secvar.h
> +++ b/arch/powerpc/include/asm/secvar.h
> @@ -20,6 +20,7 @@ struct secvar_operations {
>                         uint64_t keybufsize);
>         int (*set)(const char *key, uint64_t key_len, u8 *data,
>                    uint64_t data_size);
> +       ssize_t (*format)(char *buf);
>  };
>  
>  #ifdef CONFIG_PPC_SECURE_BOOT
> diff --git a/arch/powerpc/kernel/secvar-sysfs.c
> b/arch/powerpc/kernel/secvar-sysfs.c
> index 02e9fee1552e..190238f51335 100644
> --- a/arch/powerpc/kernel/secvar-sysfs.c
> +++ b/arch/powerpc/kernel/secvar-sysfs.c
> @@ -21,26 +21,7 @@ static struct kset *secvar_kset;
>  static ssize_t format_show(struct kobject *kobj, struct
> kobj_attribute *attr,
>                            char *buf)
>  {
> -       ssize_t rc = 0;
> -       struct device_node *node;
> -       const char *format;
> -
> -       node = of_find_compatible_node(NULL, NULL, "ibm,secvar-
> backend");
> -       if (!of_device_is_available(node)) {
> -               rc = -ENODEV;
> -               goto out;
> -       }
> -
> -       rc = of_property_read_string(node, "format", &format);
> -       if (rc)
> -               goto out;
> -
> -       rc = sysfs_emit(buf, "%s\n", format);
> -
> -out:
> -       of_node_put(node);
> -
> -       return rc;
> +       return secvar_ops->format(buf);
>  }
>  
>  
> diff --git a/arch/powerpc/platforms/powernv/opal-secvar.c
> b/arch/powerpc/platforms/powernv/opal-secvar.c
> index 14133e120bdd..5e9de06b2533 100644
> --- a/arch/powerpc/platforms/powernv/opal-secvar.c
> +++ b/arch/powerpc/platforms/powernv/opal-secvar.c
> @@ -101,10 +101,35 @@ static int opal_set_variable(const char *key,
> uint64_t ksize, u8 *data,
>         return opal_status_to_err(rc);
>  }
>  
> +static ssize_t opal_secvar_format(char *buf)
> +{
> +       ssize_t rc = 0;
> +       struct device_node *node;
> +       const char *format;
> +
> +       node = of_find_compatible_node(NULL, NULL, "ibm,secvar-
> backend");
> +       if (!of_device_is_available(node)) {
> +               rc = -ENODEV;
> +               goto out;
> +       }
> +
> +       rc = of_property_read_string(node, "format", &format);
> +       if (rc)
> +               goto out;
> +
> +       rc = sysfs_emit(buf, "%s\n", format);
> +
> +out:
> +       of_node_put(node);
> +
> +       return rc;
> +}
> +
>  static const struct secvar_operations opal_secvar_ops = {
>         .get = opal_get_variable,
>         .get_next = opal_get_next_variable,
>         .set = opal_set_variable,
> +       .format = opal_secvar_format,
>  };
>  
>  static int opal_secvar_probe(struct platform_device *pdev)

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 4/7] powerpc/secvar: Handle format string in the consumer
@ 2023-01-04  7:31     ` Andrew Donnellan
  0 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-04  7:31 UTC (permalink / raw)
  To: Russell Currey, linuxppc-dev; +Cc: gregkh, nayna, linux-kernel, zohar, gcwilson

On Fri, 2022-12-30 at 15:20 +1100, Russell Currey wrote:
> The code that handles the format string in secvar-sysfs.c is entirely
> OPAL specific, so create a new "format" op in secvar_operations to
> make
> the secvar code more generic.  No functional change.
> 
> Signed-off-by: Russell Currey <ruscur@russell.cc>

LGTM

Reviewed-by: Andrew Donnellan <ajd@linux.ibm.com>

> ---
>  arch/powerpc/include/asm/secvar.h            |  1 +
>  arch/powerpc/kernel/secvar-sysfs.c           | 21 +---------------
>  arch/powerpc/platforms/powernv/opal-secvar.c | 25
> ++++++++++++++++++++
>  3 files changed, 27 insertions(+), 20 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/secvar.h
> b/arch/powerpc/include/asm/secvar.h
> index 4cc35b58b986..3b7e5a3625bd 100644
> --- a/arch/powerpc/include/asm/secvar.h
> +++ b/arch/powerpc/include/asm/secvar.h
> @@ -20,6 +20,7 @@ struct secvar_operations {
>                         uint64_t keybufsize);
>         int (*set)(const char *key, uint64_t key_len, u8 *data,
>                    uint64_t data_size);
> +       ssize_t (*format)(char *buf);
>  };
>  
>  #ifdef CONFIG_PPC_SECURE_BOOT
> diff --git a/arch/powerpc/kernel/secvar-sysfs.c
> b/arch/powerpc/kernel/secvar-sysfs.c
> index 02e9fee1552e..190238f51335 100644
> --- a/arch/powerpc/kernel/secvar-sysfs.c
> +++ b/arch/powerpc/kernel/secvar-sysfs.c
> @@ -21,26 +21,7 @@ static struct kset *secvar_kset;
>  static ssize_t format_show(struct kobject *kobj, struct
> kobj_attribute *attr,
>                            char *buf)
>  {
> -       ssize_t rc = 0;
> -       struct device_node *node;
> -       const char *format;
> -
> -       node = of_find_compatible_node(NULL, NULL, "ibm,secvar-
> backend");
> -       if (!of_device_is_available(node)) {
> -               rc = -ENODEV;
> -               goto out;
> -       }
> -
> -       rc = of_property_read_string(node, "format", &format);
> -       if (rc)
> -               goto out;
> -
> -       rc = sysfs_emit(buf, "%s\n", format);
> -
> -out:
> -       of_node_put(node);
> -
> -       return rc;
> +       return secvar_ops->format(buf);
>  }
>  
>  
> diff --git a/arch/powerpc/platforms/powernv/opal-secvar.c
> b/arch/powerpc/platforms/powernv/opal-secvar.c
> index 14133e120bdd..5e9de06b2533 100644
> --- a/arch/powerpc/platforms/powernv/opal-secvar.c
> +++ b/arch/powerpc/platforms/powernv/opal-secvar.c
> @@ -101,10 +101,35 @@ static int opal_set_variable(const char *key,
> uint64_t ksize, u8 *data,
>         return opal_status_to_err(rc);
>  }
>  
> +static ssize_t opal_secvar_format(char *buf)
> +{
> +       ssize_t rc = 0;
> +       struct device_node *node;
> +       const char *format;
> +
> +       node = of_find_compatible_node(NULL, NULL, "ibm,secvar-
> backend");
> +       if (!of_device_is_available(node)) {
> +               rc = -ENODEV;
> +               goto out;
> +       }
> +
> +       rc = of_property_read_string(node, "format", &format);
> +       if (rc)
> +               goto out;
> +
> +       rc = sysfs_emit(buf, "%s\n", format);
> +
> +out:
> +       of_node_put(node);
> +
> +       return rc;
> +}
> +
>  static const struct secvar_operations opal_secvar_ops = {
>         .get = opal_get_variable,
>         .get_next = opal_get_next_variable,
>         .set = opal_set_variable,
> +       .format = opal_secvar_format,
>  };
>  
>  static int opal_secvar_probe(struct platform_device *pdev)

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 5/7] powerpc/secvar: Handle max object size in the consumer
  2022-12-30  4:20   ` Russell Currey
@ 2023-01-04  7:50     ` Andrew Donnellan
  -1 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-04  7:50 UTC (permalink / raw)
  To: Russell Currey, linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, zohar, mpe

On Fri, 2022-12-30 at 15:20 +1100, Russell Currey wrote:
> Currently the max object size is handled in the core secvar code with
> an
> entirely OPAL-specific implementation, so create a new max_size() op
> and
> move the existing implementation into the powernv platform.  Should
> be
> no functional change.
> 
> Signed-off-by: Russell Currey <ruscur@russell.cc>

LGTM

Reviewed-by: Andrew Donnellan <ajd@linux.ibm.com>

> ---
>  arch/powerpc/include/asm/secvar.h            |  1 +
>  arch/powerpc/kernel/secvar-sysfs.c           | 17 +++--------------
>  arch/powerpc/platforms/powernv/opal-secvar.c | 19
> +++++++++++++++++++
>  3 files changed, 23 insertions(+), 14 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/secvar.h
> b/arch/powerpc/include/asm/secvar.h
> index 3b7e5a3625bd..92d2c051918b 100644
> --- a/arch/powerpc/include/asm/secvar.h
> +++ b/arch/powerpc/include/asm/secvar.h
> @@ -21,6 +21,7 @@ struct secvar_operations {
>         int (*set)(const char *key, uint64_t key_len, u8 *data,
>                    uint64_t data_size);
>         ssize_t (*format)(char *buf);
> +       int (*max_size)(uint64_t *max_size);
>  };
>  
>  #ifdef CONFIG_PPC_SECURE_BOOT
> diff --git a/arch/powerpc/kernel/secvar-sysfs.c
> b/arch/powerpc/kernel/secvar-sysfs.c
> index 190238f51335..aa1daec480e1 100644
> --- a/arch/powerpc/kernel/secvar-sysfs.c
> +++ b/arch/powerpc/kernel/secvar-sysfs.c
> @@ -122,27 +122,16 @@ static struct kobj_type secvar_ktype = {
>  static int update_kobj_size(void)
>  {
>  
> -       struct device_node *node;
>         u64 varsize;
> -       int rc = 0;
> +       int rc = secvar_ops->max_size(&varsize);
>  
> -       node = of_find_compatible_node(NULL, NULL, "ibm,secvar-
> backend");
> -       if (!of_device_is_available(node)) {
> -               rc = -ENODEV;
> -               goto out;
> -       }
> -
> -       rc = of_property_read_u64(node, "max-var-size", &varsize);
>         if (rc)
> -               goto out;
> +               return rc;
>  
>         data_attr.size = varsize;
>         update_attr.size = varsize;
>  
> -out:
> -       of_node_put(node);
> -
> -       return rc;
> +       return 0;
>  }
>  
>  static int secvar_sysfs_load(void)
> diff --git a/arch/powerpc/platforms/powernv/opal-secvar.c
> b/arch/powerpc/platforms/powernv/opal-secvar.c
> index 5e9de06b2533..07260460e966 100644
> --- a/arch/powerpc/platforms/powernv/opal-secvar.c
> +++ b/arch/powerpc/platforms/powernv/opal-secvar.c
> @@ -125,11 +125,30 @@ static ssize_t opal_secvar_format(char *buf)
>         return rc;
>  }
>  
> +static int opal_secvar_max_size(uint64_t *max_size)
> +{
> +       int rc;
> +       struct device_node *node;
> +
> +       node = of_find_compatible_node(NULL, NULL, "ibm,secvar-
> backend");
> +       if (!of_device_is_available(node)) {
> +               rc = -ENODEV;
> +               goto out;
> +       }
> +
> +       rc = of_property_read_u64(node, "max-var-size", max_size);
> +
> +out:
> +       of_node_put(node);
> +       return rc;
> +}
> +
>  static const struct secvar_operations opal_secvar_ops = {
>         .get = opal_get_variable,
>         .get_next = opal_get_next_variable,
>         .set = opal_set_variable,
>         .format = opal_secvar_format,
> +       .max_size = opal_secvar_max_size,
>  };
>  
>  static int opal_secvar_probe(struct platform_device *pdev)

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 5/7] powerpc/secvar: Handle max object size in the consumer
@ 2023-01-04  7:50     ` Andrew Donnellan
  0 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-04  7:50 UTC (permalink / raw)
  To: Russell Currey, linuxppc-dev; +Cc: gregkh, nayna, linux-kernel, zohar, gcwilson

On Fri, 2022-12-30 at 15:20 +1100, Russell Currey wrote:
> Currently the max object size is handled in the core secvar code with
> an
> entirely OPAL-specific implementation, so create a new max_size() op
> and
> move the existing implementation into the powernv platform.  Should
> be
> no functional change.
> 
> Signed-off-by: Russell Currey <ruscur@russell.cc>

LGTM

Reviewed-by: Andrew Donnellan <ajd@linux.ibm.com>

> ---
>  arch/powerpc/include/asm/secvar.h            |  1 +
>  arch/powerpc/kernel/secvar-sysfs.c           | 17 +++--------------
>  arch/powerpc/platforms/powernv/opal-secvar.c | 19
> +++++++++++++++++++
>  3 files changed, 23 insertions(+), 14 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/secvar.h
> b/arch/powerpc/include/asm/secvar.h
> index 3b7e5a3625bd..92d2c051918b 100644
> --- a/arch/powerpc/include/asm/secvar.h
> +++ b/arch/powerpc/include/asm/secvar.h
> @@ -21,6 +21,7 @@ struct secvar_operations {
>         int (*set)(const char *key, uint64_t key_len, u8 *data,
>                    uint64_t data_size);
>         ssize_t (*format)(char *buf);
> +       int (*max_size)(uint64_t *max_size);
>  };
>  
>  #ifdef CONFIG_PPC_SECURE_BOOT
> diff --git a/arch/powerpc/kernel/secvar-sysfs.c
> b/arch/powerpc/kernel/secvar-sysfs.c
> index 190238f51335..aa1daec480e1 100644
> --- a/arch/powerpc/kernel/secvar-sysfs.c
> +++ b/arch/powerpc/kernel/secvar-sysfs.c
> @@ -122,27 +122,16 @@ static struct kobj_type secvar_ktype = {
>  static int update_kobj_size(void)
>  {
>  
> -       struct device_node *node;
>         u64 varsize;
> -       int rc = 0;
> +       int rc = secvar_ops->max_size(&varsize);
>  
> -       node = of_find_compatible_node(NULL, NULL, "ibm,secvar-
> backend");
> -       if (!of_device_is_available(node)) {
> -               rc = -ENODEV;
> -               goto out;
> -       }
> -
> -       rc = of_property_read_u64(node, "max-var-size", &varsize);
>         if (rc)
> -               goto out;
> +               return rc;
>  
>         data_attr.size = varsize;
>         update_attr.size = varsize;
>  
> -out:
> -       of_node_put(node);
> -
> -       return rc;
> +       return 0;
>  }
>  
>  static int secvar_sysfs_load(void)
> diff --git a/arch/powerpc/platforms/powernv/opal-secvar.c
> b/arch/powerpc/platforms/powernv/opal-secvar.c
> index 5e9de06b2533..07260460e966 100644
> --- a/arch/powerpc/platforms/powernv/opal-secvar.c
> +++ b/arch/powerpc/platforms/powernv/opal-secvar.c
> @@ -125,11 +125,30 @@ static ssize_t opal_secvar_format(char *buf)
>         return rc;
>  }
>  
> +static int opal_secvar_max_size(uint64_t *max_size)
> +{
> +       int rc;
> +       struct device_node *node;
> +
> +       node = of_find_compatible_node(NULL, NULL, "ibm,secvar-
> backend");
> +       if (!of_device_is_available(node)) {
> +               rc = -ENODEV;
> +               goto out;
> +       }
> +
> +       rc = of_property_read_u64(node, "max-var-size", max_size);
> +
> +out:
> +       of_node_put(node);
> +       return rc;
> +}
> +
>  static const struct secvar_operations opal_secvar_ops = {
>         .get = opal_get_variable,
>         .get_next = opal_get_next_variable,
>         .set = opal_set_variable,
>         .format = opal_secvar_format,
> +       .max_size = opal_secvar_max_size,
>  };
>  
>  static int opal_secvar_probe(struct platform_device *pdev)

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 6/7] powerpc/secvar: Extend sysfs to include config vars
  2022-12-30  4:20   ` Russell Currey
@ 2023-01-05  7:28     ` Andrew Donnellan
  -1 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-05  7:28 UTC (permalink / raw)
  To: Russell Currey, linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, zohar, mpe

On Fri, 2022-12-30 at 15:20 +1100, Russell Currey wrote:
> The forthcoming pseries consumer of the secvar API wants to expose a
> number of config variables.  Allowing secvar implementations to
> provide
> their own sysfs attributes makes it easy for consumers to expose what
> they need to.
> 
> This is not being used by the OPAL secvar implementation at present,
> and
> the config directory will not be created if no attributes are set.
> 
> Signed-off-by: Russell Currey <ruscur@russell.cc>

Minor comments below, but regardless:

Reviewed-by: Andrew Donnellan <ajd@linux.ibm.com>

> ---
> I played around with adding an API call to facilitate a more generic
> key/value interface for config variables and it seemed like
> unnecessary
> complexity.  I think this is cleaner.  If there was ever a secvar
> interface other than sysfs we'd have to rework it, though.

I concur, this can be dealt with if/when the secvar interface is
exposed by some other means than sysfs.

> 
>  arch/powerpc/include/asm/secvar.h  |  3 +++
>  arch/powerpc/kernel/secvar-sysfs.c | 40 ++++++++++++++++++++++++++--
> --
>  2 files changed, 38 insertions(+), 5 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/secvar.h
> b/arch/powerpc/include/asm/secvar.h
> index 92d2c051918b..250e7066b6da 100644
> --- a/arch/powerpc/include/asm/secvar.h
> +++ b/arch/powerpc/include/asm/secvar.h
> @@ -10,6 +10,7 @@
>  
>  #include <linux/types.h>
>  #include <linux/errno.h>
> +#include <linux/sysfs.h>
>  
>  extern const struct secvar_operations *secvar_ops;
>  
> @@ -27,10 +28,12 @@ struct secvar_operations {
>  #ifdef CONFIG_PPC_SECURE_BOOT
>  
>  extern void set_secvar_ops(const struct secvar_operations *ops);
> +extern void set_secvar_config_attrs(const struct attribute **attrs);
>  
>  #else
>  
>  static inline void set_secvar_ops(const struct secvar_operations
> *ops) { }
> +static inline void set_secvar_config_attrs(const struct attribute
> **attrs) { }
>  
>  #endif
>  
> diff --git a/arch/powerpc/kernel/secvar-sysfs.c
> b/arch/powerpc/kernel/secvar-sysfs.c
> index aa1daec480e1..ad1e1d72d2ae 100644
> --- a/arch/powerpc/kernel/secvar-sysfs.c
> +++ b/arch/powerpc/kernel/secvar-sysfs.c
> @@ -15,9 +15,17 @@
>  
>  #define NAME_MAX_SIZE     1024
>  
> +const struct attribute **secvar_config_attrs __ro_after_init = NULL;
> +
>  static struct kobject *secvar_kobj;
>  static struct kset *secvar_kset;
>  
> +void set_secvar_config_attrs(const struct attribute **attrs)
> +{
> +       WARN_ON_ONCE(secvar_config_attrs);
> +       secvar_config_attrs = attrs;
> +}
> +
>  static ssize_t format_show(struct kobject *kobj, struct
> kobj_attribute *attr,
>                            char *buf)
>  {
> @@ -134,6 +142,16 @@ static int update_kobj_size(void)
>         return 0;
>  }
>  
> +static int secvar_sysfs_config(struct kobject *kobj)
> +{
> +       struct attribute_group config_group = {
> +               .name = "config",
> +               .attrs = (struct attribute **)secvar_config_attrs,
> +       };

I was slightly concerned that you're putting this on the stack, but it
doesn't appear that sysfs_create_group() keeps any references to the
group around after it creates all the files, so I think this is fine.

> +
> +       return sysfs_create_group(kobj, &config_group);
> +}
> +
>  static int secvar_sysfs_load(void)
>  {
>         char *name;
> @@ -196,26 +214,38 @@ static int secvar_sysfs_init(void)
>  
>         rc = sysfs_create_file(secvar_kobj, &format_attr.attr);
>         if (rc) {
> -               kobject_put(secvar_kobj);
> -               return -ENOMEM;
> +               pr_err("secvar: Failed to create format object\n");

This file defines pr_fmt, so the secvar: prefix here can go away,
though I notice that is the case for all the existing prints in this
function too.

> +               rc = -ENOMEM;
> +               goto err;
>         }
>  
>         secvar_kset = kset_create_and_add("vars", NULL, secvar_kobj);
>         if (!secvar_kset) {
>                 pr_err("secvar: sysfs kobject registration
> failed.\n");
> -               kobject_put(secvar_kobj);
> -               return -ENOMEM;
> +               rc = -ENOMEM;
> +               goto err;
>         }
>  
>         rc = update_kobj_size();
>         if (rc) {
>                 pr_err("Cannot read the size of the attribute\n");
> -               return rc;
> +               goto err;
> +       }
> +
> +       if (secvar_config_attrs) {
> +               rc = secvar_sysfs_config(secvar_kobj);
> +               if (rc) {
> +                       pr_err("secvar: Failed to create config
> directory\n");

Same comment as above

> +                       goto err;
> +               }
>         }
>  
>         secvar_sysfs_load();
>  
>         return 0;
> +err:
> +       kobject_put(secvar_kobj);
> +       return rc;
>  }
>  
>  late_initcall(secvar_sysfs_init);

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 6/7] powerpc/secvar: Extend sysfs to include config vars
@ 2023-01-05  7:28     ` Andrew Donnellan
  0 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-05  7:28 UTC (permalink / raw)
  To: Russell Currey, linuxppc-dev; +Cc: gregkh, nayna, linux-kernel, zohar, gcwilson

On Fri, 2022-12-30 at 15:20 +1100, Russell Currey wrote:
> The forthcoming pseries consumer of the secvar API wants to expose a
> number of config variables.  Allowing secvar implementations to
> provide
> their own sysfs attributes makes it easy for consumers to expose what
> they need to.
> 
> This is not being used by the OPAL secvar implementation at present,
> and
> the config directory will not be created if no attributes are set.
> 
> Signed-off-by: Russell Currey <ruscur@russell.cc>

Minor comments below, but regardless:

Reviewed-by: Andrew Donnellan <ajd@linux.ibm.com>

> ---
> I played around with adding an API call to facilitate a more generic
> key/value interface for config variables and it seemed like
> unnecessary
> complexity.  I think this is cleaner.  If there was ever a secvar
> interface other than sysfs we'd have to rework it, though.

I concur, this can be dealt with if/when the secvar interface is
exposed by some other means than sysfs.

> 
>  arch/powerpc/include/asm/secvar.h  |  3 +++
>  arch/powerpc/kernel/secvar-sysfs.c | 40 ++++++++++++++++++++++++++--
> --
>  2 files changed, 38 insertions(+), 5 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/secvar.h
> b/arch/powerpc/include/asm/secvar.h
> index 92d2c051918b..250e7066b6da 100644
> --- a/arch/powerpc/include/asm/secvar.h
> +++ b/arch/powerpc/include/asm/secvar.h
> @@ -10,6 +10,7 @@
>  
>  #include <linux/types.h>
>  #include <linux/errno.h>
> +#include <linux/sysfs.h>
>  
>  extern const struct secvar_operations *secvar_ops;
>  
> @@ -27,10 +28,12 @@ struct secvar_operations {
>  #ifdef CONFIG_PPC_SECURE_BOOT
>  
>  extern void set_secvar_ops(const struct secvar_operations *ops);
> +extern void set_secvar_config_attrs(const struct attribute **attrs);
>  
>  #else
>  
>  static inline void set_secvar_ops(const struct secvar_operations
> *ops) { }
> +static inline void set_secvar_config_attrs(const struct attribute
> **attrs) { }
>  
>  #endif
>  
> diff --git a/arch/powerpc/kernel/secvar-sysfs.c
> b/arch/powerpc/kernel/secvar-sysfs.c
> index aa1daec480e1..ad1e1d72d2ae 100644
> --- a/arch/powerpc/kernel/secvar-sysfs.c
> +++ b/arch/powerpc/kernel/secvar-sysfs.c
> @@ -15,9 +15,17 @@
>  
>  #define NAME_MAX_SIZE     1024
>  
> +const struct attribute **secvar_config_attrs __ro_after_init = NULL;
> +
>  static struct kobject *secvar_kobj;
>  static struct kset *secvar_kset;
>  
> +void set_secvar_config_attrs(const struct attribute **attrs)
> +{
> +       WARN_ON_ONCE(secvar_config_attrs);
> +       secvar_config_attrs = attrs;
> +}
> +
>  static ssize_t format_show(struct kobject *kobj, struct
> kobj_attribute *attr,
>                            char *buf)
>  {
> @@ -134,6 +142,16 @@ static int update_kobj_size(void)
>         return 0;
>  }
>  
> +static int secvar_sysfs_config(struct kobject *kobj)
> +{
> +       struct attribute_group config_group = {
> +               .name = "config",
> +               .attrs = (struct attribute **)secvar_config_attrs,
> +       };

I was slightly concerned that you're putting this on the stack, but it
doesn't appear that sysfs_create_group() keeps any references to the
group around after it creates all the files, so I think this is fine.

> +
> +       return sysfs_create_group(kobj, &config_group);
> +}
> +
>  static int secvar_sysfs_load(void)
>  {
>         char *name;
> @@ -196,26 +214,38 @@ static int secvar_sysfs_init(void)
>  
>         rc = sysfs_create_file(secvar_kobj, &format_attr.attr);
>         if (rc) {
> -               kobject_put(secvar_kobj);
> -               return -ENOMEM;
> +               pr_err("secvar: Failed to create format object\n");

This file defines pr_fmt, so the secvar: prefix here can go away,
though I notice that is the case for all the existing prints in this
function too.

> +               rc = -ENOMEM;
> +               goto err;
>         }
>  
>         secvar_kset = kset_create_and_add("vars", NULL, secvar_kobj);
>         if (!secvar_kset) {
>                 pr_err("secvar: sysfs kobject registration
> failed.\n");
> -               kobject_put(secvar_kobj);
> -               return -ENOMEM;
> +               rc = -ENOMEM;
> +               goto err;
>         }
>  
>         rc = update_kobj_size();
>         if (rc) {
>                 pr_err("Cannot read the size of the attribute\n");
> -               return rc;
> +               goto err;
> +       }
> +
> +       if (secvar_config_attrs) {
> +               rc = secvar_sysfs_config(secvar_kobj);
> +               if (rc) {
> +                       pr_err("secvar: Failed to create config
> directory\n");

Same comment as above

> +                       goto err;
> +               }
>         }
>  
>         secvar_sysfs_load();
>  
>         return 0;
> +err:
> +       kobject_put(secvar_kobj);
> +       return rc;
>  }
>  
>  late_initcall(secvar_sysfs_init);

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
  2022-12-30  4:20   ` Russell Currey
@ 2023-01-05  8:15     ` Andrew Donnellan
  -1 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-05  8:15 UTC (permalink / raw)
  To: Russell Currey, linuxppc-dev; +Cc: gregkh, nayna, linux-kernel, zohar, gcwilson

On Fri, 2022-12-30 at 15:20 +1100, Russell Currey wrote:
> The pseries platform can support dynamic secure boot (i.e. secure
> boot
> using user-defined keys) using variables contained with the PowerVM
> LPAR
> Platform KeyStore (PLPKS).  Using the powerpc secvar API, expose the
> relevant variables for pseries dynamic secure boot through the
> existing
> secvar filesystem layout.
> 
> The relevant variables for dynamic secure boot are signed in the
> keystore, and can only be modified using the H_PKS_SIGNED_UPDATE
> hcall.
> Object labels in the keystore are encoded using ucs2 format.  With
> our
> fixed variable names we don't have to care about encoding outside of
> the
> necessary byte padding.
> 
> When a user writes to a variable, the first 8 bytes of data must
> contain
> the signed update flags as defined by the hypervisor.
> 
> When a user reads a variable, the first 4 bytes of data contain the
> policies defined for the object.
> 
> Limitations exist due to the underlying implementation of sysfs
> binary
> attributes, as is the case for the OPAL secvar implementation -
> partial writes are unsupported and writes cannot be larger than
> PAGE_SIZE.

The PAGE_SIZE limit, in practice, will be a major limitation with 4K
pages (we expect several of the variables to regularly be larger than
4K) but won't be much of an issue for 64K (that's all the storage space
the hypervisor will give you anyway).

In a previous internal version, we printed a message when PAGE_SIZE <
plpks_get_maxobjectsize(), might be worth still doing that?

> 
> Co-developed-by: Nayna Jain <nayna@linux.ibm.com>
> Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
> Co-developed-by: Andrew Donnellan <ajd@linux.ibm.com>
> Signed-off-by: Andrew Donnellan <ajd@linux.ibm.com>
> Signed-off-by: Russell Currey <ruscur@russell.cc>

Some minor comments for v3 on a patch which already carries my
signoff...

> ---
> v2: Remove unnecessary config vars from sysfs and document the
> others,
>     thanks to review from Greg.  If we end up needing to expose more,
> we
>     can add them later and update the docs.
> 
>     Use sysfs_emit() instead of sprintf(), thanks to Greg.
> 
>     Change the size of the sysfs binary attributes to include the 8-
> byte
>     flags header, preventing truncation of large writes.
> 
>  Documentation/ABI/testing/sysfs-secvar        |  67 ++++-
>  arch/powerpc/platforms/pseries/Kconfig        |  13 +
>  arch/powerpc/platforms/pseries/Makefile       |   4 +-
>  arch/powerpc/platforms/pseries/plpks-secvar.c | 245
> ++++++++++++++++++
>  4 files changed, 326 insertions(+), 3 deletions(-)
>  create mode 100644 arch/powerpc/platforms/pseries/plpks-secvar.c
> 
> diff --git a/Documentation/ABI/testing/sysfs-secvar
> b/Documentation/ABI/testing/sysfs-secvar
> index feebb8c57294..466a8cb92b92 100644
> --- a/Documentation/ABI/testing/sysfs-secvar
> +++ b/Documentation/ABI/testing/sysfs-secvar
> @@ -34,7 +34,7 @@ Description:  An integer representation of the size
> of the content of the
>  
>  What:          /sys/firmware/secvar/vars/<variable_name>/data
>  Date:          August 2019
> -Contact:       Nayna Jain h<nayna@linux.ibm.com>
> +Contact:       Nayna Jain <nayna@linux.ibm.com>
>  Description:   A read-only file containing the value of the
> variable. The size
>                 of the file represents the maximum size of the
> variable data.
>  
> @@ -44,3 +44,68 @@ Contact:     Nayna Jain <nayna@linux.ibm.com>
>  Description:   A write-only file that is used to submit the new
> value for the
>                 variable. The size of the file represents the maximum
> size of
>                 the variable data that can be written.
> +
> +What:          /sys/firmware/secvar/config
> +Date:          December 2022
> +Contact:       Nayna Jain <nayna@linux.ibm.com>
> +Description:   This optional directory contains read-only config
> attributes as
> +               defined by the secure variable implementation.  All
> data is in
> +               ASCII format. The directory is only created if the
> backing
> +               implementation provides variables to populate it,
> which at
> +               present is only PLPKS on the pseries platform.
> +
> +What:          /sys/firmware/secvar/config/version
> +Date:          December 2022
> +Contact:       Nayna Jain <nayna@linux.ibm.com>
> +Description:   RO file, only present if the secvar implementation is
> PLPKS.
> +
> +               Contains the config version as reported by the
> hypervisor in
> +               ASCII decimal format.
> +
> +What:          /sys/firmware/secvar/config/max_object_size
> +Date:          December 2022
> +Contact:       Nayna Jain <nayna@linux.ibm.com>
> +Description:   RO file, only present if the secvar implementation is
> PLPKS.
> +
> +               Contains the maximum allowed size of objects in the
> keystore
> +               in bytes, represented in ASCII decimal format.
> +
> +               This is not necessarily the same as the max size that
> can be
> +               written to an update file as writes can contain more
> than
> +               object data, you should use the size of the update
> file for
> +               that purpose.
> +
> +What:          /sys/firmware/secvar/config/total_size
> +Date:          December 2022
> +Contact:       Nayna Jain <nayna@linux.ibm.com>
> +Description:   RO file, only present if the secvar implementation is
> PLPKS.
> +
> +               Contains the total size of the PLPKS in bytes,
> represented in
> +               ASCII decimal format.
> +
> +What:          /sys/firmware/secvar/config/used_space
> +Date:          December 2022
> +Contact:       Nayna Jain <nayna@linux.ibm.com>
> +Description:   RO file, only present if the secvar implementation is
> PLPKS.
> +
> +               Contains the current space consumed of the PLPKS in
> bytes,
> +               represented in ASCII decimal format.

Note that presently, this isn't actually updated when the user writes
objects. I suppose we could fix this.

> +
> +What:          /sys/firmware/secvar/config/supported_policies
> +Date:          December 2022
> +Contact:       Nayna Jain <nayna@linux.ibm.com>
> +Description:   RO file, only present if the secvar implementation is
> PLPKS.
> +
> +               Contains a bitmask of supported policy flags by the
> hypervisor,
> +               represented as an 8 byte hexadecimal ASCII string. 
> Consult the
> +               hypervisor documentation for what these flags are.
> +
> +What:          /sys/firmware/secvar/config/signed_update_algorithms
> +Date:          December 2022
> +Contact:       Nayna Jain <nayna@linux.ibm.com>
> +Description:   RO file, only present if the secvar implementation is
> PLPKS.
> +
> +               Contains a bitmask of flags indicating which
> algorithms the
> +               hypervisor supports objects to be signed with when
> modifying
> +               secvars, represented as a 16 byte hexadecimal ASCII
> string.
> +               Consult the hypervisor documentation for what these
> flags mean.
> diff --git a/arch/powerpc/platforms/pseries/Kconfig
> b/arch/powerpc/platforms/pseries/Kconfig
> index a3b4d99567cb..94e08c405d50 100644
> --- a/arch/powerpc/platforms/pseries/Kconfig
> +++ b/arch/powerpc/platforms/pseries/Kconfig
> @@ -162,6 +162,19 @@ config PSERIES_PLPKS
>  
>           If unsure, select N.
>  
> +config PSERIES_PLPKS_SECVAR
> +       depends on PSERIES_PLPKS

PSERIES_PLPKS has no use on its own without the secvar frontend. We
should either just have one option, or for future-proofing purposes
turn this depends into a select and get PSERIES_PLPKS out of
menuconfig.

> +       depends on PPC_SECURE_BOOT

FWIW, starting from a pseries_le_defconfig, turning on all the options
that are required to get to PPC_SECURE_BOOT was annoying. I'd like to
have PSERIES_PLPKS_SECVAR enabled in the defconfig but it involves
switching on quite a lot.

> +       bool "Support for the PLPKS secvar interface"
> +       help
> +         PowerVM can support dynamic secure boot with user-defined
> keys
> +         through the PLPKS. Keystore objects used in dynamic secure
> boot

We should also expand PLPKS to PowerVM LPAR Platform KeyStore.

> +         can be exposed to the kernel and userspace through the
> powerpc
> +         secvar infrastructure. Select this to enable the PLPKS
> backend
> +         for secvars for use in pseries dynamic secure boot.
> +
> +         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 92310202bdd7..807756991f9d 100644
> --- a/arch/powerpc/platforms/pseries/Makefile
> +++ b/arch/powerpc/platforms/pseries/Makefile
> @@ -27,8 +27,8 @@ obj-$(CONFIG_PAPR_SCM)                += papr_scm.o
>  obj-$(CONFIG_PPC_SPLPAR)       += vphn.o
>  obj-$(CONFIG_PPC_SVM)          += svm.o
>  obj-$(CONFIG_FA_DUMP)          += rtas-fadump.o
> -obj-$(CONFIG_PSERIES_PLPKS) += plpks.o
> -
> +obj-$(CONFIG_PSERIES_PLPKS)    += plpks.o
> +obj-$(CONFIG_PSERIES_PLPKS_SECVAR)     += plpks-secvar.o
>  obj-$(CONFIG_SUSPEND)          += suspend.o
>  obj-$(CONFIG_PPC_VAS)          += vas.o vas-sysfs.o
>  
> diff --git a/arch/powerpc/platforms/pseries/plpks-secvar.c
> b/arch/powerpc/platforms/pseries/plpks-secvar.c
> new file mode 100644
> index 000000000000..8298f039bef4
> --- /dev/null
> +++ b/arch/powerpc/platforms/pseries/plpks-secvar.c
> @@ -0,0 +1,245 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Secure variable implementation using the PowerVM LPAR Platform
> KeyStore (PLPKS)
> + *
> + * Copyright 2022, IBM Corporation

And by the time this gets merged, 2023

> + * Authors: Russell Currey
> + *          Andrew Donnellan
> + *          Nayna Jain
> + */
> +
> +#define pr_fmt(fmt) "secvar: "fmt
> +
> +#include <linux/printk.h>
> +#include <linux/init.h>
> +#include <linux/types.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +#include <linux/kobject.h>
> +#include <asm/secvar.h>
> +#include "plpks.h"
> +
> +// Config attributes for sysfs
> +#define PLPKS_CONFIG_ATTR(name, fmt, func)                     \
> +       static ssize_t name##_show(struct kobject *kobj,        \
> +                                  struct kobj_attribute *attr, \
> +                                  char *buf)                   \
> +       {                                                       \
> +               return sysfs_emit(buf, fmt, func());            \
> +       }                                                       \
> +       static struct kobj_attribute attr_##name = __ATTR_RO(name)
> +
> +PLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version);
> +PLPKS_CONFIG_ATTR(max_object_size, "%u\n", plpks_get_maxobjectsize);
> +PLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize);
> +PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);
> +PLPKS_CONFIG_ATTR(supported_policies, "%08x\n",
> plpks_get_supportedpolicies);
> +PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n",
> plpks_get_signedupdatealgorithms);
> +
> +static const struct attribute *config_attrs[] = {
> +       &attr_version.attr,
> +       &attr_max_object_size.attr,
> +       &attr_total_size.attr,
> +       &attr_used_space.attr,
> +       &attr_supported_policies.attr,
> +       &attr_signed_update_algorithms.attr,
> +       NULL,
> +};
> +
> +static u16 get_ucs2name(const char *name, uint8_t **ucs2_name)
> +{
> +       int namelen = strlen(name) * 2;
> +       *ucs2_name = kzalloc(namelen, GFP_KERNEL);
> +
> +       if (!*ucs2_name)
> +               return 0;
> +
> +       for (int i = 0; name[i]; i++) {
> +               (*ucs2_name)[i * 2] = name[i];
> +               (*ucs2_name)[i * 2 + 1] = '\0';
> +       }
> +
> +       return namelen;
> +}
> +
> +static u32 get_policy(const char *name)
> +{
> +       if ((strcmp(name, "db") == 0) ||
> +           (strcmp(name, "dbx") == 0) ||
> +           (strcmp(name, "grubdb") == 0) ||
> +           (strcmp(name, "sbat") == 0))
> +               return (WORLDREADABLE | SIGNEDUPDATE);
> +       else
> +               return SIGNEDUPDATE;
> +}
> +
> +#define PLPKS_SECVAR_COUNT 8
> +static char *var_names[PLPKS_SECVAR_COUNT] = {
> +       "PK",
> +       "KEK",
> +       "db",
> +       "dbx",
> +       "grubdb",
> +       "sbat",
> +       "moduledb",
> +       "trustedcadb",
> +};
> +
> +static int plpks_get_variable(const char *key, uint64_t key_len,
> +                             u8 *data, uint64_t *data_size)
> +{
> +       struct plpks_var var = {0};
> +       u16 ucs2_namelen;
> +       u8 *ucs2_name;
> +       int rc = 0;
> +
> +       ucs2_namelen = get_ucs2name(key, &ucs2_name);
> +       if (!ucs2_namelen)
> +               return -ENOMEM;
> +
> +       var.name = ucs2_name;
> +       var.namelen = ucs2_namelen;
> +       var.os = PLPKS_VAR_LINUX;
> +       rc = plpks_read_os_var(&var);
> +
> +       if (rc)
> +               goto err;
> +
> +       *data_size = var.datalen + sizeof(var.policy);
> +
> +       // We can be called with data = NULL to just get the object
> size.
> +       if (data) {
> +               memcpy(data, &var.policy, sizeof(var.policy));
> +               memcpy(data + sizeof(var.policy), var.data,
> var.datalen);
> +       }
> +
> +       kfree(var.data);
> +err:
> +       kfree(ucs2_name);
> +       return rc;
> +}
> +
> +static int plpks_set_variable(const char *key, uint64_t key_len,
> +                             u8 *data, uint64_t data_size)
> +{
> +       struct plpks_var var = {0};
> +       u16 ucs2_namelen;
> +       u8 *ucs2_name;
> +       int rc = 0;
> +       u64 flags;
> +
> +       // Secure variables need to be prefixed with 8 bytes of
> flags.
> +       // We only want to perform the write if we have at least one
> byte of data.
> +       if (data_size <= sizeof(flags))
> +               return -EINVAL;
> +
> +       ucs2_namelen = get_ucs2name(key, &ucs2_name);
> +       if (!ucs2_namelen)
> +               return -ENOMEM;
> +
> +       memcpy(&flags, data, sizeof(flags));
> +
> +       var.datalen = data_size - sizeof(flags);
> +       var.data = kzalloc(var.datalen, GFP_KERNEL);
> +       if (!var.data) {
> +               rc = -ENOMEM;
> +               goto err;
> +       }
> +
> +       memcpy(var.data, data + sizeof(flags), var.datalen);
> +
> +       var.name = ucs2_name;
> +       var.namelen = ucs2_namelen;
> +       var.os = PLPKS_VAR_LINUX;
> +       var.policy = get_policy(key);
> +
> +       rc = plpks_signed_update_var(var, flags);
> +
> +       kfree(var.data);
> +err:
> +       kfree(ucs2_name);
> +       return rc;
> +}
> +
> +/*
> + * get_next() in the secvar API is designed for the OPAL API.
> + * If *key is 0, it returns the first variable in the keystore.
> + * Otherwise, you pass the name of a key and it returns next in
> line.
> + *
> + * We're going to cheat here - since we have fixed keys and don't
> care about
> + * key_len, we can just use it as an index.

This is kinda gross.

> + */

Inconsistent comment style

> +static int plpks_get_next_variable(const char *key, uint64_t
> *key_len, uint64_t keybufsize)
> +{
> +       if (!key || !key_len)
> +               return -EINVAL;
> +
> +       if (*key_len >= PLPKS_SECVAR_COUNT)
> +               return -ENOENT;
> +
> +       if (strscpy((char *)key, var_names[(*key_len)++], keybufsize)
> < 0)

Not sure how I feel about the increment being buried in here

> +               return -E2BIG;
> +
> +       return 0;
> +}
> +
> +// PLPKS dynamic secure boot doesn't give us a format string in the
> same way OPAL does.
> +// Instead, report the format using the SB_VERSION variable in the
> keystore.
> +static ssize_t plpks_secvar_format(char *buf)
> +{
> +       struct plpks_var var = {0};
> +       ssize_t ret;
> +
> +       var.component = NULL;
> +       // Only the signed variables have ucs2-encoded names, this
> one doesn't
> +       var.name = "SB_VERSION";
> +       var.namelen = 10;
> +       var.datalen = 0;
> +       var.data = NULL;
> +
> +       // Unlike the other vars, SB_VERSION is owned by firmware
> instead of the OS
> +       ret = plpks_read_fw_var(&var);
> +       if (ret) {
> +               if (ret == -ENOENT)
> +                       return sysfs_emit(buf, "ibm,plpks-sb-
> unknown\n");
> +
> +               pr_err("Error %ld reading SB_VERSION from
> firmware\n", ret);
> +               return ret;
> +       }
> +
> +       // Hypervisor defines SB_VERSION as a "1 byte unsigned
> integer value"
> +       ret = sysfs_emit(buf, "ibm,plpks-sb-%hhu\n", var.data[0]);
> +
> +       kfree(var.data);
> +       return ret;
> +}
> +
> +static int plpks_max_size(uint64_t *max_size)
> +{
> +       // The max object size reported by the hypervisor is accurate
> for the
> +       // object itself, but we use the first 8 bytes of data on
> write as the
> +       // signed update flags, so the max size a user can write is
> larger.
> +       *max_size = (uint64_t)plpks_get_maxobjectsize() + 8;
> +
> +       return 0;
> +}
> +
> +
> +static const struct secvar_operations plpks_secvar_ops = {
> +       .get = plpks_get_variable,
> +       .get_next = plpks_get_next_variable,
> +       .set = plpks_set_variable,
> +       .format = plpks_secvar_format,
> +       .max_size = plpks_max_size,
> +};
> +
> +static int plpks_secvar_init(void)
> +{
> +       if (!plpks_is_available())
> +               return -ENODEV;
> +
> +       set_secvar_ops(&plpks_secvar_ops);
> +       set_secvar_config_attrs(config_attrs);
> +       return 0;
> +}
> +device_initcall(plpks_secvar_init);

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
@ 2023-01-05  8:15     ` Andrew Donnellan
  0 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-05  8:15 UTC (permalink / raw)
  To: Russell Currey, linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, zohar, mpe

On Fri, 2022-12-30 at 15:20 +1100, Russell Currey wrote:
> The pseries platform can support dynamic secure boot (i.e. secure
> boot
> using user-defined keys) using variables contained with the PowerVM
> LPAR
> Platform KeyStore (PLPKS).  Using the powerpc secvar API, expose the
> relevant variables for pseries dynamic secure boot through the
> existing
> secvar filesystem layout.
> 
> The relevant variables for dynamic secure boot are signed in the
> keystore, and can only be modified using the H_PKS_SIGNED_UPDATE
> hcall.
> Object labels in the keystore are encoded using ucs2 format.  With
> our
> fixed variable names we don't have to care about encoding outside of
> the
> necessary byte padding.
> 
> When a user writes to a variable, the first 8 bytes of data must
> contain
> the signed update flags as defined by the hypervisor.
> 
> When a user reads a variable, the first 4 bytes of data contain the
> policies defined for the object.
> 
> Limitations exist due to the underlying implementation of sysfs
> binary
> attributes, as is the case for the OPAL secvar implementation -
> partial writes are unsupported and writes cannot be larger than
> PAGE_SIZE.

The PAGE_SIZE limit, in practice, will be a major limitation with 4K
pages (we expect several of the variables to regularly be larger than
4K) but won't be much of an issue for 64K (that's all the storage space
the hypervisor will give you anyway).

In a previous internal version, we printed a message when PAGE_SIZE <
plpks_get_maxobjectsize(), might be worth still doing that?

> 
> Co-developed-by: Nayna Jain <nayna@linux.ibm.com>
> Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
> Co-developed-by: Andrew Donnellan <ajd@linux.ibm.com>
> Signed-off-by: Andrew Donnellan <ajd@linux.ibm.com>
> Signed-off-by: Russell Currey <ruscur@russell.cc>

Some minor comments for v3 on a patch which already carries my
signoff...

> ---
> v2: Remove unnecessary config vars from sysfs and document the
> others,
>     thanks to review from Greg.  If we end up needing to expose more,
> we
>     can add them later and update the docs.
> 
>     Use sysfs_emit() instead of sprintf(), thanks to Greg.
> 
>     Change the size of the sysfs binary attributes to include the 8-
> byte
>     flags header, preventing truncation of large writes.
> 
>  Documentation/ABI/testing/sysfs-secvar        |  67 ++++-
>  arch/powerpc/platforms/pseries/Kconfig        |  13 +
>  arch/powerpc/platforms/pseries/Makefile       |   4 +-
>  arch/powerpc/platforms/pseries/plpks-secvar.c | 245
> ++++++++++++++++++
>  4 files changed, 326 insertions(+), 3 deletions(-)
>  create mode 100644 arch/powerpc/platforms/pseries/plpks-secvar.c
> 
> diff --git a/Documentation/ABI/testing/sysfs-secvar
> b/Documentation/ABI/testing/sysfs-secvar
> index feebb8c57294..466a8cb92b92 100644
> --- a/Documentation/ABI/testing/sysfs-secvar
> +++ b/Documentation/ABI/testing/sysfs-secvar
> @@ -34,7 +34,7 @@ Description:  An integer representation of the size
> of the content of the
>  
>  What:          /sys/firmware/secvar/vars/<variable_name>/data
>  Date:          August 2019
> -Contact:       Nayna Jain h<nayna@linux.ibm.com>
> +Contact:       Nayna Jain <nayna@linux.ibm.com>
>  Description:   A read-only file containing the value of the
> variable. The size
>                 of the file represents the maximum size of the
> variable data.
>  
> @@ -44,3 +44,68 @@ Contact:     Nayna Jain <nayna@linux.ibm.com>
>  Description:   A write-only file that is used to submit the new
> value for the
>                 variable. The size of the file represents the maximum
> size of
>                 the variable data that can be written.
> +
> +What:          /sys/firmware/secvar/config
> +Date:          December 2022
> +Contact:       Nayna Jain <nayna@linux.ibm.com>
> +Description:   This optional directory contains read-only config
> attributes as
> +               defined by the secure variable implementation.  All
> data is in
> +               ASCII format. The directory is only created if the
> backing
> +               implementation provides variables to populate it,
> which at
> +               present is only PLPKS on the pseries platform.
> +
> +What:          /sys/firmware/secvar/config/version
> +Date:          December 2022
> +Contact:       Nayna Jain <nayna@linux.ibm.com>
> +Description:   RO file, only present if the secvar implementation is
> PLPKS.
> +
> +               Contains the config version as reported by the
> hypervisor in
> +               ASCII decimal format.
> +
> +What:          /sys/firmware/secvar/config/max_object_size
> +Date:          December 2022
> +Contact:       Nayna Jain <nayna@linux.ibm.com>
> +Description:   RO file, only present if the secvar implementation is
> PLPKS.
> +
> +               Contains the maximum allowed size of objects in the
> keystore
> +               in bytes, represented in ASCII decimal format.
> +
> +               This is not necessarily the same as the max size that
> can be
> +               written to an update file as writes can contain more
> than
> +               object data, you should use the size of the update
> file for
> +               that purpose.
> +
> +What:          /sys/firmware/secvar/config/total_size
> +Date:          December 2022
> +Contact:       Nayna Jain <nayna@linux.ibm.com>
> +Description:   RO file, only present if the secvar implementation is
> PLPKS.
> +
> +               Contains the total size of the PLPKS in bytes,
> represented in
> +               ASCII decimal format.
> +
> +What:          /sys/firmware/secvar/config/used_space
> +Date:          December 2022
> +Contact:       Nayna Jain <nayna@linux.ibm.com>
> +Description:   RO file, only present if the secvar implementation is
> PLPKS.
> +
> +               Contains the current space consumed of the PLPKS in
> bytes,
> +               represented in ASCII decimal format.

Note that presently, this isn't actually updated when the user writes
objects. I suppose we could fix this.

> +
> +What:          /sys/firmware/secvar/config/supported_policies
> +Date:          December 2022
> +Contact:       Nayna Jain <nayna@linux.ibm.com>
> +Description:   RO file, only present if the secvar implementation is
> PLPKS.
> +
> +               Contains a bitmask of supported policy flags by the
> hypervisor,
> +               represented as an 8 byte hexadecimal ASCII string. 
> Consult the
> +               hypervisor documentation for what these flags are.
> +
> +What:          /sys/firmware/secvar/config/signed_update_algorithms
> +Date:          December 2022
> +Contact:       Nayna Jain <nayna@linux.ibm.com>
> +Description:   RO file, only present if the secvar implementation is
> PLPKS.
> +
> +               Contains a bitmask of flags indicating which
> algorithms the
> +               hypervisor supports objects to be signed with when
> modifying
> +               secvars, represented as a 16 byte hexadecimal ASCII
> string.
> +               Consult the hypervisor documentation for what these
> flags mean.
> diff --git a/arch/powerpc/platforms/pseries/Kconfig
> b/arch/powerpc/platforms/pseries/Kconfig
> index a3b4d99567cb..94e08c405d50 100644
> --- a/arch/powerpc/platforms/pseries/Kconfig
> +++ b/arch/powerpc/platforms/pseries/Kconfig
> @@ -162,6 +162,19 @@ config PSERIES_PLPKS
>  
>           If unsure, select N.
>  
> +config PSERIES_PLPKS_SECVAR
> +       depends on PSERIES_PLPKS

PSERIES_PLPKS has no use on its own without the secvar frontend. We
should either just have one option, or for future-proofing purposes
turn this depends into a select and get PSERIES_PLPKS out of
menuconfig.

> +       depends on PPC_SECURE_BOOT

FWIW, starting from a pseries_le_defconfig, turning on all the options
that are required to get to PPC_SECURE_BOOT was annoying. I'd like to
have PSERIES_PLPKS_SECVAR enabled in the defconfig but it involves
switching on quite a lot.

> +       bool "Support for the PLPKS secvar interface"
> +       help
> +         PowerVM can support dynamic secure boot with user-defined
> keys
> +         through the PLPKS. Keystore objects used in dynamic secure
> boot

We should also expand PLPKS to PowerVM LPAR Platform KeyStore.

> +         can be exposed to the kernel and userspace through the
> powerpc
> +         secvar infrastructure. Select this to enable the PLPKS
> backend
> +         for secvars for use in pseries dynamic secure boot.
> +
> +         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 92310202bdd7..807756991f9d 100644
> --- a/arch/powerpc/platforms/pseries/Makefile
> +++ b/arch/powerpc/platforms/pseries/Makefile
> @@ -27,8 +27,8 @@ obj-$(CONFIG_PAPR_SCM)                += papr_scm.o
>  obj-$(CONFIG_PPC_SPLPAR)       += vphn.o
>  obj-$(CONFIG_PPC_SVM)          += svm.o
>  obj-$(CONFIG_FA_DUMP)          += rtas-fadump.o
> -obj-$(CONFIG_PSERIES_PLPKS) += plpks.o
> -
> +obj-$(CONFIG_PSERIES_PLPKS)    += plpks.o
> +obj-$(CONFIG_PSERIES_PLPKS_SECVAR)     += plpks-secvar.o
>  obj-$(CONFIG_SUSPEND)          += suspend.o
>  obj-$(CONFIG_PPC_VAS)          += vas.o vas-sysfs.o
>  
> diff --git a/arch/powerpc/platforms/pseries/plpks-secvar.c
> b/arch/powerpc/platforms/pseries/plpks-secvar.c
> new file mode 100644
> index 000000000000..8298f039bef4
> --- /dev/null
> +++ b/arch/powerpc/platforms/pseries/plpks-secvar.c
> @@ -0,0 +1,245 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Secure variable implementation using the PowerVM LPAR Platform
> KeyStore (PLPKS)
> + *
> + * Copyright 2022, IBM Corporation

And by the time this gets merged, 2023

> + * Authors: Russell Currey
> + *          Andrew Donnellan
> + *          Nayna Jain
> + */
> +
> +#define pr_fmt(fmt) "secvar: "fmt
> +
> +#include <linux/printk.h>
> +#include <linux/init.h>
> +#include <linux/types.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +#include <linux/kobject.h>
> +#include <asm/secvar.h>
> +#include "plpks.h"
> +
> +// Config attributes for sysfs
> +#define PLPKS_CONFIG_ATTR(name, fmt, func)                     \
> +       static ssize_t name##_show(struct kobject *kobj,        \
> +                                  struct kobj_attribute *attr, \
> +                                  char *buf)                   \
> +       {                                                       \
> +               return sysfs_emit(buf, fmt, func());            \
> +       }                                                       \
> +       static struct kobj_attribute attr_##name = __ATTR_RO(name)
> +
> +PLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version);
> +PLPKS_CONFIG_ATTR(max_object_size, "%u\n", plpks_get_maxobjectsize);
> +PLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize);
> +PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);
> +PLPKS_CONFIG_ATTR(supported_policies, "%08x\n",
> plpks_get_supportedpolicies);
> +PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n",
> plpks_get_signedupdatealgorithms);
> +
> +static const struct attribute *config_attrs[] = {
> +       &attr_version.attr,
> +       &attr_max_object_size.attr,
> +       &attr_total_size.attr,
> +       &attr_used_space.attr,
> +       &attr_supported_policies.attr,
> +       &attr_signed_update_algorithms.attr,
> +       NULL,
> +};
> +
> +static u16 get_ucs2name(const char *name, uint8_t **ucs2_name)
> +{
> +       int namelen = strlen(name) * 2;
> +       *ucs2_name = kzalloc(namelen, GFP_KERNEL);
> +
> +       if (!*ucs2_name)
> +               return 0;
> +
> +       for (int i = 0; name[i]; i++) {
> +               (*ucs2_name)[i * 2] = name[i];
> +               (*ucs2_name)[i * 2 + 1] = '\0';
> +       }
> +
> +       return namelen;
> +}
> +
> +static u32 get_policy(const char *name)
> +{
> +       if ((strcmp(name, "db") == 0) ||
> +           (strcmp(name, "dbx") == 0) ||
> +           (strcmp(name, "grubdb") == 0) ||
> +           (strcmp(name, "sbat") == 0))
> +               return (WORLDREADABLE | SIGNEDUPDATE);
> +       else
> +               return SIGNEDUPDATE;
> +}
> +
> +#define PLPKS_SECVAR_COUNT 8
> +static char *var_names[PLPKS_SECVAR_COUNT] = {
> +       "PK",
> +       "KEK",
> +       "db",
> +       "dbx",
> +       "grubdb",
> +       "sbat",
> +       "moduledb",
> +       "trustedcadb",
> +};
> +
> +static int plpks_get_variable(const char *key, uint64_t key_len,
> +                             u8 *data, uint64_t *data_size)
> +{
> +       struct plpks_var var = {0};
> +       u16 ucs2_namelen;
> +       u8 *ucs2_name;
> +       int rc = 0;
> +
> +       ucs2_namelen = get_ucs2name(key, &ucs2_name);
> +       if (!ucs2_namelen)
> +               return -ENOMEM;
> +
> +       var.name = ucs2_name;
> +       var.namelen = ucs2_namelen;
> +       var.os = PLPKS_VAR_LINUX;
> +       rc = plpks_read_os_var(&var);
> +
> +       if (rc)
> +               goto err;
> +
> +       *data_size = var.datalen + sizeof(var.policy);
> +
> +       // We can be called with data = NULL to just get the object
> size.
> +       if (data) {
> +               memcpy(data, &var.policy, sizeof(var.policy));
> +               memcpy(data + sizeof(var.policy), var.data,
> var.datalen);
> +       }
> +
> +       kfree(var.data);
> +err:
> +       kfree(ucs2_name);
> +       return rc;
> +}
> +
> +static int plpks_set_variable(const char *key, uint64_t key_len,
> +                             u8 *data, uint64_t data_size)
> +{
> +       struct plpks_var var = {0};
> +       u16 ucs2_namelen;
> +       u8 *ucs2_name;
> +       int rc = 0;
> +       u64 flags;
> +
> +       // Secure variables need to be prefixed with 8 bytes of
> flags.
> +       // We only want to perform the write if we have at least one
> byte of data.
> +       if (data_size <= sizeof(flags))
> +               return -EINVAL;
> +
> +       ucs2_namelen = get_ucs2name(key, &ucs2_name);
> +       if (!ucs2_namelen)
> +               return -ENOMEM;
> +
> +       memcpy(&flags, data, sizeof(flags));
> +
> +       var.datalen = data_size - sizeof(flags);
> +       var.data = kzalloc(var.datalen, GFP_KERNEL);
> +       if (!var.data) {
> +               rc = -ENOMEM;
> +               goto err;
> +       }
> +
> +       memcpy(var.data, data + sizeof(flags), var.datalen);
> +
> +       var.name = ucs2_name;
> +       var.namelen = ucs2_namelen;
> +       var.os = PLPKS_VAR_LINUX;
> +       var.policy = get_policy(key);
> +
> +       rc = plpks_signed_update_var(var, flags);
> +
> +       kfree(var.data);
> +err:
> +       kfree(ucs2_name);
> +       return rc;
> +}
> +
> +/*
> + * get_next() in the secvar API is designed for the OPAL API.
> + * If *key is 0, it returns the first variable in the keystore.
> + * Otherwise, you pass the name of a key and it returns next in
> line.
> + *
> + * We're going to cheat here - since we have fixed keys and don't
> care about
> + * key_len, we can just use it as an index.

This is kinda gross.

> + */

Inconsistent comment style

> +static int plpks_get_next_variable(const char *key, uint64_t
> *key_len, uint64_t keybufsize)
> +{
> +       if (!key || !key_len)
> +               return -EINVAL;
> +
> +       if (*key_len >= PLPKS_SECVAR_COUNT)
> +               return -ENOENT;
> +
> +       if (strscpy((char *)key, var_names[(*key_len)++], keybufsize)
> < 0)

Not sure how I feel about the increment being buried in here

> +               return -E2BIG;
> +
> +       return 0;
> +}
> +
> +// PLPKS dynamic secure boot doesn't give us a format string in the
> same way OPAL does.
> +// Instead, report the format using the SB_VERSION variable in the
> keystore.
> +static ssize_t plpks_secvar_format(char *buf)
> +{
> +       struct plpks_var var = {0};
> +       ssize_t ret;
> +
> +       var.component = NULL;
> +       // Only the signed variables have ucs2-encoded names, this
> one doesn't
> +       var.name = "SB_VERSION";
> +       var.namelen = 10;
> +       var.datalen = 0;
> +       var.data = NULL;
> +
> +       // Unlike the other vars, SB_VERSION is owned by firmware
> instead of the OS
> +       ret = plpks_read_fw_var(&var);
> +       if (ret) {
> +               if (ret == -ENOENT)
> +                       return sysfs_emit(buf, "ibm,plpks-sb-
> unknown\n");
> +
> +               pr_err("Error %ld reading SB_VERSION from
> firmware\n", ret);
> +               return ret;
> +       }
> +
> +       // Hypervisor defines SB_VERSION as a "1 byte unsigned
> integer value"
> +       ret = sysfs_emit(buf, "ibm,plpks-sb-%hhu\n", var.data[0]);
> +
> +       kfree(var.data);
> +       return ret;
> +}
> +
> +static int plpks_max_size(uint64_t *max_size)
> +{
> +       // The max object size reported by the hypervisor is accurate
> for the
> +       // object itself, but we use the first 8 bytes of data on
> write as the
> +       // signed update flags, so the max size a user can write is
> larger.
> +       *max_size = (uint64_t)plpks_get_maxobjectsize() + 8;
> +
> +       return 0;
> +}
> +
> +
> +static const struct secvar_operations plpks_secvar_ops = {
> +       .get = plpks_get_variable,
> +       .get_next = plpks_get_next_variable,
> +       .set = plpks_set_variable,
> +       .format = plpks_secvar_format,
> +       .max_size = plpks_max_size,
> +};
> +
> +static int plpks_secvar_init(void)
> +{
> +       if (!plpks_is_available())
> +               return -ENODEV;
> +
> +       set_secvar_ops(&plpks_secvar_ops);
> +       set_secvar_config_attrs(config_attrs);
> +       return 0;
> +}
> +device_initcall(plpks_secvar_init);

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 6/7] powerpc/secvar: Extend sysfs to include config vars
  2022-12-30  4:20   ` Russell Currey
@ 2023-01-06  4:15     ` Michael Ellerman
  -1 siblings, 0 replies; 54+ messages in thread
From: Michael Ellerman @ 2023-01-06  4:15 UTC (permalink / raw)
  To: Russell Currey, linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, ajd, zohar, Russell Currey

Russell Currey <ruscur@russell.cc> writes:
> The forthcoming pseries consumer of the secvar API wants to expose a
> number of config variables.  Allowing secvar implementations to provide
> their own sysfs attributes makes it easy for consumers to expose what
> they need to.
>
> This is not being used by the OPAL secvar implementation at present, and
> the config directory will not be created if no attributes are set.

Would it be slightly cleaner if the attributes were just a member of
secvar_operations?

That would avoid the need for some of the separate handling of the ops
and the attributes.

I know "ops" implies it's only methods, but I think that's not a big
problem. The power_pmu struct does something similar, where it combines
ops and attributes.

cheers

> diff --git a/arch/powerpc/include/asm/secvar.h b/arch/powerpc/include/asm/secvar.h
> index 92d2c051918b..250e7066b6da 100644
> --- a/arch/powerpc/include/asm/secvar.h
> +++ b/arch/powerpc/include/asm/secvar.h
> @@ -10,6 +10,7 @@
>  
>  #include <linux/types.h>
>  #include <linux/errno.h>
> +#include <linux/sysfs.h>
>  
>  extern const struct secvar_operations *secvar_ops;
>  
> @@ -27,10 +28,12 @@ struct secvar_operations {
>  #ifdef CONFIG_PPC_SECURE_BOOT
>  
>  extern void set_secvar_ops(const struct secvar_operations *ops);
> +extern void set_secvar_config_attrs(const struct attribute **attrs);
>  
>  #else
>  
>  static inline void set_secvar_ops(const struct secvar_operations *ops) { }
> +static inline void set_secvar_config_attrs(const struct attribute **attrs) { }
>  
>  #endif
>  
> diff --git a/arch/powerpc/kernel/secvar-sysfs.c b/arch/powerpc/kernel/secvar-sysfs.c
> index aa1daec480e1..ad1e1d72d2ae 100644
> --- a/arch/powerpc/kernel/secvar-sysfs.c
> +++ b/arch/powerpc/kernel/secvar-sysfs.c
> @@ -15,9 +15,17 @@
>  
>  #define NAME_MAX_SIZE	   1024
>  
> +const struct attribute **secvar_config_attrs __ro_after_init = NULL;
> +
>  static struct kobject *secvar_kobj;
>  static struct kset *secvar_kset;
>  
> +void set_secvar_config_attrs(const struct attribute **attrs)
> +{
> +	WARN_ON_ONCE(secvar_config_attrs);
> +	secvar_config_attrs = attrs;
> +}
> +
>  static ssize_t format_show(struct kobject *kobj, struct kobj_attribute *attr,
>  			   char *buf)
>  {
> @@ -134,6 +142,16 @@ static int update_kobj_size(void)
>  	return 0;
>  }
>  
> +static int secvar_sysfs_config(struct kobject *kobj)
> +{
> +	struct attribute_group config_group = {
> +		.name = "config",
> +		.attrs = (struct attribute **)secvar_config_attrs,
> +	};
> +
> +	return sysfs_create_group(kobj, &config_group);
> +}
> +
>  static int secvar_sysfs_load(void)
>  {
>  	char *name;
> @@ -196,26 +214,38 @@ static int secvar_sysfs_init(void)
>  
>  	rc = sysfs_create_file(secvar_kobj, &format_attr.attr);
>  	if (rc) {
> -		kobject_put(secvar_kobj);
> -		return -ENOMEM;
> +		pr_err("secvar: Failed to create format object\n");
> +		rc = -ENOMEM;
> +		goto err;
>  	}
>  
>  	secvar_kset = kset_create_and_add("vars", NULL, secvar_kobj);
>  	if (!secvar_kset) {
>  		pr_err("secvar: sysfs kobject registration failed.\n");
> -		kobject_put(secvar_kobj);
> -		return -ENOMEM;
> +		rc = -ENOMEM;
> +		goto err;
>  	}
>  
>  	rc = update_kobj_size();
>  	if (rc) {
>  		pr_err("Cannot read the size of the attribute\n");
> -		return rc;
> +		goto err;
> +	}
> +
> +	if (secvar_config_attrs) {
> +		rc = secvar_sysfs_config(secvar_kobj);
> +		if (rc) {
> +			pr_err("secvar: Failed to create config directory\n");
> +			goto err;
> +		}
>  	}
>  
>  	secvar_sysfs_load();
>  
>  	return 0;
> +err:
> +	kobject_put(secvar_kobj);
> +	return rc;
>  }
>  
>  late_initcall(secvar_sysfs_init);
> -- 
> 2.38.1

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

* Re: [PATCH v2 6/7] powerpc/secvar: Extend sysfs to include config vars
@ 2023-01-06  4:15     ` Michael Ellerman
  0 siblings, 0 replies; 54+ messages in thread
From: Michael Ellerman @ 2023-01-06  4:15 UTC (permalink / raw)
  To: Russell Currey, linuxppc-dev
  Cc: ajd, gregkh, nayna, linux-kernel, zohar, gcwilson

Russell Currey <ruscur@russell.cc> writes:
> The forthcoming pseries consumer of the secvar API wants to expose a
> number of config variables.  Allowing secvar implementations to provide
> their own sysfs attributes makes it easy for consumers to expose what
> they need to.
>
> This is not being used by the OPAL secvar implementation at present, and
> the config directory will not be created if no attributes are set.

Would it be slightly cleaner if the attributes were just a member of
secvar_operations?

That would avoid the need for some of the separate handling of the ops
and the attributes.

I know "ops" implies it's only methods, but I think that's not a big
problem. The power_pmu struct does something similar, where it combines
ops and attributes.

cheers

> diff --git a/arch/powerpc/include/asm/secvar.h b/arch/powerpc/include/asm/secvar.h
> index 92d2c051918b..250e7066b6da 100644
> --- a/arch/powerpc/include/asm/secvar.h
> +++ b/arch/powerpc/include/asm/secvar.h
> @@ -10,6 +10,7 @@
>  
>  #include <linux/types.h>
>  #include <linux/errno.h>
> +#include <linux/sysfs.h>
>  
>  extern const struct secvar_operations *secvar_ops;
>  
> @@ -27,10 +28,12 @@ struct secvar_operations {
>  #ifdef CONFIG_PPC_SECURE_BOOT
>  
>  extern void set_secvar_ops(const struct secvar_operations *ops);
> +extern void set_secvar_config_attrs(const struct attribute **attrs);
>  
>  #else
>  
>  static inline void set_secvar_ops(const struct secvar_operations *ops) { }
> +static inline void set_secvar_config_attrs(const struct attribute **attrs) { }
>  
>  #endif
>  
> diff --git a/arch/powerpc/kernel/secvar-sysfs.c b/arch/powerpc/kernel/secvar-sysfs.c
> index aa1daec480e1..ad1e1d72d2ae 100644
> --- a/arch/powerpc/kernel/secvar-sysfs.c
> +++ b/arch/powerpc/kernel/secvar-sysfs.c
> @@ -15,9 +15,17 @@
>  
>  #define NAME_MAX_SIZE	   1024
>  
> +const struct attribute **secvar_config_attrs __ro_after_init = NULL;
> +
>  static struct kobject *secvar_kobj;
>  static struct kset *secvar_kset;
>  
> +void set_secvar_config_attrs(const struct attribute **attrs)
> +{
> +	WARN_ON_ONCE(secvar_config_attrs);
> +	secvar_config_attrs = attrs;
> +}
> +
>  static ssize_t format_show(struct kobject *kobj, struct kobj_attribute *attr,
>  			   char *buf)
>  {
> @@ -134,6 +142,16 @@ static int update_kobj_size(void)
>  	return 0;
>  }
>  
> +static int secvar_sysfs_config(struct kobject *kobj)
> +{
> +	struct attribute_group config_group = {
> +		.name = "config",
> +		.attrs = (struct attribute **)secvar_config_attrs,
> +	};
> +
> +	return sysfs_create_group(kobj, &config_group);
> +}
> +
>  static int secvar_sysfs_load(void)
>  {
>  	char *name;
> @@ -196,26 +214,38 @@ static int secvar_sysfs_init(void)
>  
>  	rc = sysfs_create_file(secvar_kobj, &format_attr.attr);
>  	if (rc) {
> -		kobject_put(secvar_kobj);
> -		return -ENOMEM;
> +		pr_err("secvar: Failed to create format object\n");
> +		rc = -ENOMEM;
> +		goto err;
>  	}
>  
>  	secvar_kset = kset_create_and_add("vars", NULL, secvar_kobj);
>  	if (!secvar_kset) {
>  		pr_err("secvar: sysfs kobject registration failed.\n");
> -		kobject_put(secvar_kobj);
> -		return -ENOMEM;
> +		rc = -ENOMEM;
> +		goto err;
>  	}
>  
>  	rc = update_kobj_size();
>  	if (rc) {
>  		pr_err("Cannot read the size of the attribute\n");
> -		return rc;
> +		goto err;
> +	}
> +
> +	if (secvar_config_attrs) {
> +		rc = secvar_sysfs_config(secvar_kobj);
> +		if (rc) {
> +			pr_err("secvar: Failed to create config directory\n");
> +			goto err;
> +		}
>  	}
>  
>  	secvar_sysfs_load();
>  
>  	return 0;
> +err:
> +	kobject_put(secvar_kobj);
> +	return rc;
>  }
>  
>  late_initcall(secvar_sysfs_init);
> -- 
> 2.38.1

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

* Re: [PATCH v2 6/7] powerpc/secvar: Extend sysfs to include config vars
  2023-01-05  7:28     ` Andrew Donnellan
@ 2023-01-06  6:33       ` Russell Currey
  -1 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2023-01-06  6:33 UTC (permalink / raw)
  To: Andrew Donnellan, linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, zohar, mpe

On Thu, 2023-01-05 at 18:28 +1100, Andrew Donnellan wrote:
> On Fri, 2022-12-30 at 15:20 +1100, Russell Currey wrote:
> > The forthcoming pseries consumer of the secvar API wants to expose
> > a
> > number of config variables.  Allowing secvar implementations to
> > provide
> > their own sysfs attributes makes it easy for consumers to expose
> > what
> > they need to.
> > 
> > This is not being used by the OPAL secvar implementation at
> > present,
> > and
> > the config directory will not be created if no attributes are set.
> > 
> > Signed-off-by: Russell Currey <ruscur@russell.cc>
> 
> Minor comments below, but regardless:
> 
> Reviewed-by: Andrew Donnellan <ajd@linux.ibm.com>
> 
> > ---
> > I played around with adding an API call to facilitate a more
> > generic
> > key/value interface for config variables and it seemed like
> > unnecessary
> > complexity.  I think this is cleaner.  If there was ever a secvar
> > interface other than sysfs we'd have to rework it, though.
> 
> I concur, this can be dealt with if/when the secvar interface is
> exposed by some other means than sysfs.
> 
> > 
> >  arch/powerpc/include/asm/secvar.h  |  3 +++
> >  arch/powerpc/kernel/secvar-sysfs.c | 40
> > ++++++++++++++++++++++++++--
> > --
> >  2 files changed, 38 insertions(+), 5 deletions(-)
> > 
> > diff --git a/arch/powerpc/include/asm/secvar.h
> > b/arch/powerpc/include/asm/secvar.h
> > index 92d2c051918b..250e7066b6da 100644
> > --- a/arch/powerpc/include/asm/secvar.h
> > +++ b/arch/powerpc/include/asm/secvar.h
> > @@ -10,6 +10,7 @@
> >  
> >  #include <linux/types.h>
> >  #include <linux/errno.h>
> > +#include <linux/sysfs.h>
> >  
> >  extern const struct secvar_operations *secvar_ops;
> >  
> > @@ -27,10 +28,12 @@ struct secvar_operations {
> >  #ifdef CONFIG_PPC_SECURE_BOOT
> >  
> >  extern void set_secvar_ops(const struct secvar_operations *ops);
> > +extern void set_secvar_config_attrs(const struct attribute
> > **attrs);
> >  
> >  #else
> >  
> >  static inline void set_secvar_ops(const struct secvar_operations
> > *ops) { }
> > +static inline void set_secvar_config_attrs(const struct attribute
> > **attrs) { }
> >  
> >  #endif
> >  
> > diff --git a/arch/powerpc/kernel/secvar-sysfs.c
> > b/arch/powerpc/kernel/secvar-sysfs.c
> > index aa1daec480e1..ad1e1d72d2ae 100644
> > --- a/arch/powerpc/kernel/secvar-sysfs.c
> > +++ b/arch/powerpc/kernel/secvar-sysfs.c
> > @@ -15,9 +15,17 @@
> >  
> >  #define NAME_MAX_SIZE     1024
> >  
> > +const struct attribute **secvar_config_attrs __ro_after_init =
> > NULL;
> > +
> >  static struct kobject *secvar_kobj;
> >  static struct kset *secvar_kset;
> >  
> > +void set_secvar_config_attrs(const struct attribute **attrs)
> > +{
> > +       WARN_ON_ONCE(secvar_config_attrs);
> > +       secvar_config_attrs = attrs;
> > +}
> > +
> >  static ssize_t format_show(struct kobject *kobj, struct
> > kobj_attribute *attr,
> >                            char *buf)
> >  {
> > @@ -134,6 +142,16 @@ static int update_kobj_size(void)
> >         return 0;
> >  }
> >  
> > +static int secvar_sysfs_config(struct kobject *kobj)
> > +{
> > +       struct attribute_group config_group = {
> > +               .name = "config",
> > +               .attrs = (struct attribute **)secvar_config_attrs,
> > +       };
> 
> I was slightly concerned that you're putting this on the stack, but
> it
> doesn't appear that sysfs_create_group() keeps any references to the
> group around after it creates all the files, so I think this is fine.
> 
> > +
> > +       return sysfs_create_group(kobj, &config_group);
> > +}
> > +
> >  static int secvar_sysfs_load(void)
> >  {
> >         char *name;
> > @@ -196,26 +214,38 @@ static int secvar_sysfs_init(void)
> >  
> >         rc = sysfs_create_file(secvar_kobj, &format_attr.attr);
> >         if (rc) {
> > -               kobject_put(secvar_kobj);
> > -               return -ENOMEM;
> > +               pr_err("secvar: Failed to create format object\n");
> 
> This file defines pr_fmt, so the secvar: prefix here can go away,
> though I notice that is the case for all the existing prints in this
> function too.

Yeah we should fix that for all of them, good catch.

> 
> > +               rc = -ENOMEM;
> > +               goto err;
> >         }
> >  
> >         secvar_kset = kset_create_and_add("vars", NULL,
> > secvar_kobj);
> >         if (!secvar_kset) {
> >                 pr_err("secvar: sysfs kobject registration
> > failed.\n");
> > -               kobject_put(secvar_kobj);
> > -               return -ENOMEM;
> > +               rc = -ENOMEM;
> > +               goto err;
> >         }
> >  
> >         rc = update_kobj_size();
> >         if (rc) {
> >                 pr_err("Cannot read the size of the attribute\n");
> > -               return rc;
> > +               goto err;
> > +       }
> > +
> > +       if (secvar_config_attrs) {
> > +               rc = secvar_sysfs_config(secvar_kobj);
> > +               if (rc) {
> > +                       pr_err("secvar: Failed to create config
> > directory\n");
> 
> Same comment as above
> 
> > +                       goto err;
> > +               }
> >         }
> >  
> >         secvar_sysfs_load();
> >  
> >         return 0;
> > +err:
> > +       kobject_put(secvar_kobj);
> > +       return rc;
> >  }
> >  
> >  late_initcall(secvar_sysfs_init);
> 


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

* Re: [PATCH v2 6/7] powerpc/secvar: Extend sysfs to include config vars
@ 2023-01-06  6:33       ` Russell Currey
  0 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2023-01-06  6:33 UTC (permalink / raw)
  To: Andrew Donnellan, linuxppc-dev
  Cc: gregkh, nayna, linux-kernel, zohar, gcwilson

On Thu, 2023-01-05 at 18:28 +1100, Andrew Donnellan wrote:
> On Fri, 2022-12-30 at 15:20 +1100, Russell Currey wrote:
> > The forthcoming pseries consumer of the secvar API wants to expose
> > a
> > number of config variables.  Allowing secvar implementations to
> > provide
> > their own sysfs attributes makes it easy for consumers to expose
> > what
> > they need to.
> > 
> > This is not being used by the OPAL secvar implementation at
> > present,
> > and
> > the config directory will not be created if no attributes are set.
> > 
> > Signed-off-by: Russell Currey <ruscur@russell.cc>
> 
> Minor comments below, but regardless:
> 
> Reviewed-by: Andrew Donnellan <ajd@linux.ibm.com>
> 
> > ---
> > I played around with adding an API call to facilitate a more
> > generic
> > key/value interface for config variables and it seemed like
> > unnecessary
> > complexity.  I think this is cleaner.  If there was ever a secvar
> > interface other than sysfs we'd have to rework it, though.
> 
> I concur, this can be dealt with if/when the secvar interface is
> exposed by some other means than sysfs.
> 
> > 
> >  arch/powerpc/include/asm/secvar.h  |  3 +++
> >  arch/powerpc/kernel/secvar-sysfs.c | 40
> > ++++++++++++++++++++++++++--
> > --
> >  2 files changed, 38 insertions(+), 5 deletions(-)
> > 
> > diff --git a/arch/powerpc/include/asm/secvar.h
> > b/arch/powerpc/include/asm/secvar.h
> > index 92d2c051918b..250e7066b6da 100644
> > --- a/arch/powerpc/include/asm/secvar.h
> > +++ b/arch/powerpc/include/asm/secvar.h
> > @@ -10,6 +10,7 @@
> >  
> >  #include <linux/types.h>
> >  #include <linux/errno.h>
> > +#include <linux/sysfs.h>
> >  
> >  extern const struct secvar_operations *secvar_ops;
> >  
> > @@ -27,10 +28,12 @@ struct secvar_operations {
> >  #ifdef CONFIG_PPC_SECURE_BOOT
> >  
> >  extern void set_secvar_ops(const struct secvar_operations *ops);
> > +extern void set_secvar_config_attrs(const struct attribute
> > **attrs);
> >  
> >  #else
> >  
> >  static inline void set_secvar_ops(const struct secvar_operations
> > *ops) { }
> > +static inline void set_secvar_config_attrs(const struct attribute
> > **attrs) { }
> >  
> >  #endif
> >  
> > diff --git a/arch/powerpc/kernel/secvar-sysfs.c
> > b/arch/powerpc/kernel/secvar-sysfs.c
> > index aa1daec480e1..ad1e1d72d2ae 100644
> > --- a/arch/powerpc/kernel/secvar-sysfs.c
> > +++ b/arch/powerpc/kernel/secvar-sysfs.c
> > @@ -15,9 +15,17 @@
> >  
> >  #define NAME_MAX_SIZE     1024
> >  
> > +const struct attribute **secvar_config_attrs __ro_after_init =
> > NULL;
> > +
> >  static struct kobject *secvar_kobj;
> >  static struct kset *secvar_kset;
> >  
> > +void set_secvar_config_attrs(const struct attribute **attrs)
> > +{
> > +       WARN_ON_ONCE(secvar_config_attrs);
> > +       secvar_config_attrs = attrs;
> > +}
> > +
> >  static ssize_t format_show(struct kobject *kobj, struct
> > kobj_attribute *attr,
> >                            char *buf)
> >  {
> > @@ -134,6 +142,16 @@ static int update_kobj_size(void)
> >         return 0;
> >  }
> >  
> > +static int secvar_sysfs_config(struct kobject *kobj)
> > +{
> > +       struct attribute_group config_group = {
> > +               .name = "config",
> > +               .attrs = (struct attribute **)secvar_config_attrs,
> > +       };
> 
> I was slightly concerned that you're putting this on the stack, but
> it
> doesn't appear that sysfs_create_group() keeps any references to the
> group around after it creates all the files, so I think this is fine.
> 
> > +
> > +       return sysfs_create_group(kobj, &config_group);
> > +}
> > +
> >  static int secvar_sysfs_load(void)
> >  {
> >         char *name;
> > @@ -196,26 +214,38 @@ static int secvar_sysfs_init(void)
> >  
> >         rc = sysfs_create_file(secvar_kobj, &format_attr.attr);
> >         if (rc) {
> > -               kobject_put(secvar_kobj);
> > -               return -ENOMEM;
> > +               pr_err("secvar: Failed to create format object\n");
> 
> This file defines pr_fmt, so the secvar: prefix here can go away,
> though I notice that is the case for all the existing prints in this
> function too.

Yeah we should fix that for all of them, good catch.

> 
> > +               rc = -ENOMEM;
> > +               goto err;
> >         }
> >  
> >         secvar_kset = kset_create_and_add("vars", NULL,
> > secvar_kobj);
> >         if (!secvar_kset) {
> >                 pr_err("secvar: sysfs kobject registration
> > failed.\n");
> > -               kobject_put(secvar_kobj);
> > -               return -ENOMEM;
> > +               rc = -ENOMEM;
> > +               goto err;
> >         }
> >  
> >         rc = update_kobj_size();
> >         if (rc) {
> >                 pr_err("Cannot read the size of the attribute\n");
> > -               return rc;
> > +               goto err;
> > +       }
> > +
> > +       if (secvar_config_attrs) {
> > +               rc = secvar_sysfs_config(secvar_kobj);
> > +               if (rc) {
> > +                       pr_err("secvar: Failed to create config
> > directory\n");
> 
> Same comment as above
> 
> > +                       goto err;
> > +               }
> >         }
> >  
> >         secvar_sysfs_load();
> >  
> >         return 0;
> > +err:
> > +       kobject_put(secvar_kobj);
> > +       return rc;
> >  }
> >  
> >  late_initcall(secvar_sysfs_init);
> 


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

* Re: [PATCH v2 6/7] powerpc/secvar: Extend sysfs to include config vars
  2023-01-06  4:15     ` Michael Ellerman
@ 2023-01-06  6:35       ` Russell Currey
  -1 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2023-01-06  6:35 UTC (permalink / raw)
  To: Michael Ellerman, linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, ajd, zohar

On Fri, 2023-01-06 at 15:15 +1100, Michael Ellerman wrote:
> Russell Currey <ruscur@russell.cc> writes:
> > The forthcoming pseries consumer of the secvar API wants to expose
> > a
> > number of config variables.  Allowing secvar implementations to
> > provide
> > their own sysfs attributes makes it easy for consumers to expose
> > what
> > they need to.
> > 
> > This is not being used by the OPAL secvar implementation at
> > present, and
> > the config directory will not be created if no attributes are set.
> 
> Would it be slightly cleaner if the attributes were just a member of
> secvar_operations?
> 
> That would avoid the need for some of the separate handling of the
> ops
> and the attributes.
> 
> I know "ops" implies it's only methods, but I think that's not a big
> problem. The power_pmu struct does something similar, where it
> combines
> ops and attributes.

Yeah that does seem easier, thanks for the suggestion.
> 
> cheers
> 
> > diff --git a/arch/powerpc/include/asm/secvar.h
> > b/arch/powerpc/include/asm/secvar.h
> > index 92d2c051918b..250e7066b6da 100644
> > --- a/arch/powerpc/include/asm/secvar.h
> > +++ b/arch/powerpc/include/asm/secvar.h
> > @@ -10,6 +10,7 @@
> >  
> >  #include <linux/types.h>
> >  #include <linux/errno.h>
> > +#include <linux/sysfs.h>
> >  
> >  extern const struct secvar_operations *secvar_ops;
> >  
> > @@ -27,10 +28,12 @@ struct secvar_operations {
> >  #ifdef CONFIG_PPC_SECURE_BOOT
> >  
> >  extern void set_secvar_ops(const struct secvar_operations *ops);
> > +extern void set_secvar_config_attrs(const struct attribute
> > **attrs);
> >  
> >  #else
> >  
> >  static inline void set_secvar_ops(const struct secvar_operations
> > *ops) { }
> > +static inline void set_secvar_config_attrs(const struct attribute
> > **attrs) { }
> >  
> >  #endif
> >  
> > diff --git a/arch/powerpc/kernel/secvar-sysfs.c
> > b/arch/powerpc/kernel/secvar-sysfs.c
> > index aa1daec480e1..ad1e1d72d2ae 100644
> > --- a/arch/powerpc/kernel/secvar-sysfs.c
> > +++ b/arch/powerpc/kernel/secvar-sysfs.c
> > @@ -15,9 +15,17 @@
> >  
> >  #define NAME_MAX_SIZE     1024
> >  
> > +const struct attribute **secvar_config_attrs __ro_after_init =
> > NULL;
> > +
> >  static struct kobject *secvar_kobj;
> >  static struct kset *secvar_kset;
> >  
> > +void set_secvar_config_attrs(const struct attribute **attrs)
> > +{
> > +       WARN_ON_ONCE(secvar_config_attrs);
> > +       secvar_config_attrs = attrs;
> > +}
> > +
> >  static ssize_t format_show(struct kobject *kobj, struct
> > kobj_attribute *attr,
> >                            char *buf)
> >  {
> > @@ -134,6 +142,16 @@ static int update_kobj_size(void)
> >         return 0;
> >  }
> >  
> > +static int secvar_sysfs_config(struct kobject *kobj)
> > +{
> > +       struct attribute_group config_group = {
> > +               .name = "config",
> > +               .attrs = (struct attribute **)secvar_config_attrs,
> > +       };
> > +
> > +       return sysfs_create_group(kobj, &config_group);
> > +}
> > +
> >  static int secvar_sysfs_load(void)
> >  {
> >         char *name;
> > @@ -196,26 +214,38 @@ static int secvar_sysfs_init(void)
> >  
> >         rc = sysfs_create_file(secvar_kobj, &format_attr.attr);
> >         if (rc) {
> > -               kobject_put(secvar_kobj);
> > -               return -ENOMEM;
> > +               pr_err("secvar: Failed to create format object\n");
> > +               rc = -ENOMEM;
> > +               goto err;
> >         }
> >  
> >         secvar_kset = kset_create_and_add("vars", NULL,
> > secvar_kobj);
> >         if (!secvar_kset) {
> >                 pr_err("secvar: sysfs kobject registration
> > failed.\n");
> > -               kobject_put(secvar_kobj);
> > -               return -ENOMEM;
> > +               rc = -ENOMEM;
> > +               goto err;
> >         }
> >  
> >         rc = update_kobj_size();
> >         if (rc) {
> >                 pr_err("Cannot read the size of the attribute\n");
> > -               return rc;
> > +               goto err;
> > +       }
> > +
> > +       if (secvar_config_attrs) {
> > +               rc = secvar_sysfs_config(secvar_kobj);
> > +               if (rc) {
> > +                       pr_err("secvar: Failed to create config
> > directory\n");
> > +                       goto err;
> > +               }
> >         }
> >  
> >         secvar_sysfs_load();
> >  
> >         return 0;
> > +err:
> > +       kobject_put(secvar_kobj);
> > +       return rc;
> >  }
> >  
> >  late_initcall(secvar_sysfs_init);
> > -- 
> > 2.38.1


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

* Re: [PATCH v2 6/7] powerpc/secvar: Extend sysfs to include config vars
@ 2023-01-06  6:35       ` Russell Currey
  0 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2023-01-06  6:35 UTC (permalink / raw)
  To: Michael Ellerman, linuxppc-dev
  Cc: ajd, gregkh, nayna, linux-kernel, zohar, gcwilson

On Fri, 2023-01-06 at 15:15 +1100, Michael Ellerman wrote:
> Russell Currey <ruscur@russell.cc> writes:
> > The forthcoming pseries consumer of the secvar API wants to expose
> > a
> > number of config variables.  Allowing secvar implementations to
> > provide
> > their own sysfs attributes makes it easy for consumers to expose
> > what
> > they need to.
> > 
> > This is not being used by the OPAL secvar implementation at
> > present, and
> > the config directory will not be created if no attributes are set.
> 
> Would it be slightly cleaner if the attributes were just a member of
> secvar_operations?
> 
> That would avoid the need for some of the separate handling of the
> ops
> and the attributes.
> 
> I know "ops" implies it's only methods, but I think that's not a big
> problem. The power_pmu struct does something similar, where it
> combines
> ops and attributes.

Yeah that does seem easier, thanks for the suggestion.
> 
> cheers
> 
> > diff --git a/arch/powerpc/include/asm/secvar.h
> > b/arch/powerpc/include/asm/secvar.h
> > index 92d2c051918b..250e7066b6da 100644
> > --- a/arch/powerpc/include/asm/secvar.h
> > +++ b/arch/powerpc/include/asm/secvar.h
> > @@ -10,6 +10,7 @@
> >  
> >  #include <linux/types.h>
> >  #include <linux/errno.h>
> > +#include <linux/sysfs.h>
> >  
> >  extern const struct secvar_operations *secvar_ops;
> >  
> > @@ -27,10 +28,12 @@ struct secvar_operations {
> >  #ifdef CONFIG_PPC_SECURE_BOOT
> >  
> >  extern void set_secvar_ops(const struct secvar_operations *ops);
> > +extern void set_secvar_config_attrs(const struct attribute
> > **attrs);
> >  
> >  #else
> >  
> >  static inline void set_secvar_ops(const struct secvar_operations
> > *ops) { }
> > +static inline void set_secvar_config_attrs(const struct attribute
> > **attrs) { }
> >  
> >  #endif
> >  
> > diff --git a/arch/powerpc/kernel/secvar-sysfs.c
> > b/arch/powerpc/kernel/secvar-sysfs.c
> > index aa1daec480e1..ad1e1d72d2ae 100644
> > --- a/arch/powerpc/kernel/secvar-sysfs.c
> > +++ b/arch/powerpc/kernel/secvar-sysfs.c
> > @@ -15,9 +15,17 @@
> >  
> >  #define NAME_MAX_SIZE     1024
> >  
> > +const struct attribute **secvar_config_attrs __ro_after_init =
> > NULL;
> > +
> >  static struct kobject *secvar_kobj;
> >  static struct kset *secvar_kset;
> >  
> > +void set_secvar_config_attrs(const struct attribute **attrs)
> > +{
> > +       WARN_ON_ONCE(secvar_config_attrs);
> > +       secvar_config_attrs = attrs;
> > +}
> > +
> >  static ssize_t format_show(struct kobject *kobj, struct
> > kobj_attribute *attr,
> >                            char *buf)
> >  {
> > @@ -134,6 +142,16 @@ static int update_kobj_size(void)
> >         return 0;
> >  }
> >  
> > +static int secvar_sysfs_config(struct kobject *kobj)
> > +{
> > +       struct attribute_group config_group = {
> > +               .name = "config",
> > +               .attrs = (struct attribute **)secvar_config_attrs,
> > +       };
> > +
> > +       return sysfs_create_group(kobj, &config_group);
> > +}
> > +
> >  static int secvar_sysfs_load(void)
> >  {
> >         char *name;
> > @@ -196,26 +214,38 @@ static int secvar_sysfs_init(void)
> >  
> >         rc = sysfs_create_file(secvar_kobj, &format_attr.attr);
> >         if (rc) {
> > -               kobject_put(secvar_kobj);
> > -               return -ENOMEM;
> > +               pr_err("secvar: Failed to create format object\n");
> > +               rc = -ENOMEM;
> > +               goto err;
> >         }
> >  
> >         secvar_kset = kset_create_and_add("vars", NULL,
> > secvar_kobj);
> >         if (!secvar_kset) {
> >                 pr_err("secvar: sysfs kobject registration
> > failed.\n");
> > -               kobject_put(secvar_kobj);
> > -               return -ENOMEM;
> > +               rc = -ENOMEM;
> > +               goto err;
> >         }
> >  
> >         rc = update_kobj_size();
> >         if (rc) {
> >                 pr_err("Cannot read the size of the attribute\n");
> > -               return rc;
> > +               goto err;
> > +       }
> > +
> > +       if (secvar_config_attrs) {
> > +               rc = secvar_sysfs_config(secvar_kobj);
> > +               if (rc) {
> > +                       pr_err("secvar: Failed to create config
> > directory\n");
> > +                       goto err;
> > +               }
> >         }
> >  
> >         secvar_sysfs_load();
> >  
> >         return 0;
> > +err:
> > +       kobject_put(secvar_kobj);
> > +       return rc;
> >  }
> >  
> >  late_initcall(secvar_sysfs_init);
> > -- 
> > 2.38.1


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

* Re: [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
  2023-01-05  8:15     ` Andrew Donnellan
@ 2023-01-06  6:49       ` Russell Currey
  -1 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2023-01-06  6:49 UTC (permalink / raw)
  To: Andrew Donnellan, linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, zohar, mpe

On Thu, 2023-01-05 at 19:15 +1100, Andrew Donnellan wrote:
> On Fri, 2022-12-30 at 15:20 +1100, Russell Currey wrote:
> > The pseries platform can support dynamic secure boot (i.e. secure
> > boot
> > using user-defined keys) using variables contained with the PowerVM
> > LPAR
> > Platform KeyStore (PLPKS).  Using the powerpc secvar API, expose
> > the
> > relevant variables for pseries dynamic secure boot through the
> > existing
> > secvar filesystem layout.
> > 
> > The relevant variables for dynamic secure boot are signed in the
> > keystore, and can only be modified using the H_PKS_SIGNED_UPDATE
> > hcall.
> > Object labels in the keystore are encoded using ucs2 format.  With
> > our
> > fixed variable names we don't have to care about encoding outside
> > of
> > the
> > necessary byte padding.
> > 
> > When a user writes to a variable, the first 8 bytes of data must
> > contain
> > the signed update flags as defined by the hypervisor.
> > 
> > When a user reads a variable, the first 4 bytes of data contain the
> > policies defined for the object.
> > 
> > Limitations exist due to the underlying implementation of sysfs
> > binary
> > attributes, as is the case for the OPAL secvar implementation -
> > partial writes are unsupported and writes cannot be larger than
> > PAGE_SIZE.
> 
> The PAGE_SIZE limit, in practice, will be a major limitation with 4K
> pages (we expect several of the variables to regularly be larger than
> 4K) but won't be much of an issue for 64K (that's all the storage
> space
> the hypervisor will give you anyway).
> 
> In a previous internal version, we printed a message when PAGE_SIZE <
> plpks_get_maxobjectsize(), might be worth still doing that?

Yeah, we should do that in the secvar code.  The same limitation
applies on the powernv side as well.

> 
> > 
> > Co-developed-by: Nayna Jain <nayna@linux.ibm.com>
> > Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
> > Co-developed-by: Andrew Donnellan <ajd@linux.ibm.com>
> > Signed-off-by: Andrew Donnellan <ajd@linux.ibm.com>
> > Signed-off-by: Russell Currey <ruscur@russell.cc>
> 
> Some minor comments for v3 on a patch which already carries my
> signoff...
> 
> > ---
> > v2: Remove unnecessary config vars from sysfs and document the
> > others,
> >     thanks to review from Greg.  If we end up needing to expose
> > more,
> > we
> >     can add them later and update the docs.
> > 
> >     Use sysfs_emit() instead of sprintf(), thanks to Greg.
> > 
> >     Change the size of the sysfs binary attributes to include the
> > 8-
> > byte
> >     flags header, preventing truncation of large writes.
> > 
> >  Documentation/ABI/testing/sysfs-secvar        |  67 ++++-
> >  arch/powerpc/platforms/pseries/Kconfig        |  13 +
> >  arch/powerpc/platforms/pseries/Makefile       |   4 +-
> >  arch/powerpc/platforms/pseries/plpks-secvar.c | 245
> > ++++++++++++++++++
> >  4 files changed, 326 insertions(+), 3 deletions(-)
> >  create mode 100644 arch/powerpc/platforms/pseries/plpks-secvar.c
> > 
> > diff --git a/Documentation/ABI/testing/sysfs-secvar
> > b/Documentation/ABI/testing/sysfs-secvar
> > index feebb8c57294..466a8cb92b92 100644
> > --- a/Documentation/ABI/testing/sysfs-secvar
> > +++ b/Documentation/ABI/testing/sysfs-secvar
> > @@ -34,7 +34,7 @@ Description:  An integer representation of the
> > size
> > of the content of the
> >  
> >  What:          /sys/firmware/secvar/vars/<variable_name>/data
> >  Date:          August 2019
> > -Contact:       Nayna Jain h<nayna@linux.ibm.com>
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> >  Description:   A read-only file containing the value of the
> > variable. The size
> >                 of the file represents the maximum size of the
> > variable data.
> >  
> > @@ -44,3 +44,68 @@ Contact:     Nayna Jain <nayna@linux.ibm.com>
> >  Description:   A write-only file that is used to submit the new
> > value for the
> >                 variable. The size of the file represents the
> > maximum
> > size of
> >                 the variable data that can be written.
> > +
> > +What:          /sys/firmware/secvar/config
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   This optional directory contains read-only config
> > attributes as
> > +               defined by the secure variable implementation.  All
> > data is in
> > +               ASCII format. The directory is only created if the
> > backing
> > +               implementation provides variables to populate it,
> > which at
> > +               present is only PLPKS on the pseries platform.
> > +
> > +What:          /sys/firmware/secvar/config/version
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is
> > PLPKS.
> > +
> > +               Contains the config version as reported by the
> > hypervisor in
> > +               ASCII decimal format.
> > +
> > +What:          /sys/firmware/secvar/config/max_object_size
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is
> > PLPKS.
> > +
> > +               Contains the maximum allowed size of objects in the
> > keystore
> > +               in bytes, represented in ASCII decimal format.
> > +
> > +               This is not necessarily the same as the max size
> > that
> > can be
> > +               written to an update file as writes can contain
> > more
> > than
> > +               object data, you should use the size of the update
> > file for
> > +               that purpose.
> > +
> > +What:          /sys/firmware/secvar/config/total_size
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is
> > PLPKS.
> > +
> > +               Contains the total size of the PLPKS in bytes,
> > represented in
> > +               ASCII decimal format.
> > +
> > +What:          /sys/firmware/secvar/config/used_space
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is
> > PLPKS.
> > +
> > +               Contains the current space consumed of the PLPKS in
> > bytes,
> > +               represented in ASCII decimal format.
> 
> Note that presently, this isn't actually updated when the user writes
> objects. I suppose we could fix this.

We should definitely fix that.

> 
> > +
> > +What:          /sys/firmware/secvar/config/supported_policies
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is
> > PLPKS.
> > +
> > +               Contains a bitmask of supported policy flags by the
> > hypervisor,
> > +               represented as an 8 byte hexadecimal ASCII string. 
> > Consult the
> > +               hypervisor documentation for what these flags are.
> > +
> > +What:          /sys/firmware/secvar/config/signed_update_algorithm
> > s
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is
> > PLPKS.
> > +
> > +               Contains a bitmask of flags indicating which
> > algorithms the
> > +               hypervisor supports objects to be signed with when
> > modifying
> > +               secvars, represented as a 16 byte hexadecimal ASCII
> > string.
> > +               Consult the hypervisor documentation for what these
> > flags mean.
> > diff --git a/arch/powerpc/platforms/pseries/Kconfig
> > b/arch/powerpc/platforms/pseries/Kconfig
> > index a3b4d99567cb..94e08c405d50 100644
> > --- a/arch/powerpc/platforms/pseries/Kconfig
> > +++ b/arch/powerpc/platforms/pseries/Kconfig
> > @@ -162,6 +162,19 @@ config PSERIES_PLPKS
> >  
> >           If unsure, select N.
> >  
> > +config PSERIES_PLPKS_SECVAR
> > +       depends on PSERIES_PLPKS
> 
> PSERIES_PLPKS has no use on its own without the secvar frontend. We
> should either just have one option, or for future-proofing purposes
> turn this depends into a select and get PSERIES_PLPKS out of
> menuconfig.

I believe I did this to enforce the dependency on PPC_SECURE_BOOT.  As
of right now they're tied together, but if I add that dependency to
PSERIES_PLPKS, then it makes things awkward if there's a more generic
PLPKS interface in future that isn't specific to secure boot.  I
suppose things can be compartmentalised in future if that happens.

> 
> > +       depends on PPC_SECURE_BOOT
> 
> FWIW, starting from a pseries_le_defconfig, turning on all the
> options
> that are required to get to PPC_SECURE_BOOT was annoying. I'd like to
> have PSERIES_PLPKS_SECVAR enabled in the defconfig but it involves
> switching on quite a lot.

Probably more of an mpe question, but we have to turn on a bunch of IMA
stuff that adds complexity and increases the build time, so I'm not
sure if we'd want it in pseries_defconfig.  We could definitely either
add things to security.config or make a new config fragment for it
though.

> 
> > +       bool "Support for the PLPKS secvar interface"
> > +       help
> > +         PowerVM can support dynamic secure boot with user-defined
> > keys
> > +         through the PLPKS. Keystore objects used in dynamic
> > secure
> > boot
> 
> We should also expand PLPKS to PowerVM LPAR Platform KeyStore.

True.

> 
> > +         can be exposed to the kernel and userspace through the
> > powerpc
> > +         secvar infrastructure. Select this to enable the PLPKS
> > backend
> > +         for secvars for use in pseries dynamic secure boot.
> > +
> > +         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 92310202bdd7..807756991f9d 100644
> > --- a/arch/powerpc/platforms/pseries/Makefile
> > +++ b/arch/powerpc/platforms/pseries/Makefile
> > @@ -27,8 +27,8 @@ obj-$(CONFIG_PAPR_SCM)                +=
> > papr_scm.o
> >  obj-$(CONFIG_PPC_SPLPAR)       += vphn.o
> >  obj-$(CONFIG_PPC_SVM)          += svm.o
> >  obj-$(CONFIG_FA_DUMP)          += rtas-fadump.o
> > -obj-$(CONFIG_PSERIES_PLPKS) += plpks.o
> > -
> > +obj-$(CONFIG_PSERIES_PLPKS)    += plpks.o
> > +obj-$(CONFIG_PSERIES_PLPKS_SECVAR)     += plpks-secvar.o
> >  obj-$(CONFIG_SUSPEND)          += suspend.o
> >  obj-$(CONFIG_PPC_VAS)          += vas.o vas-sysfs.o
> >  
> > diff --git a/arch/powerpc/platforms/pseries/plpks-secvar.c
> > b/arch/powerpc/platforms/pseries/plpks-secvar.c
> > new file mode 100644
> > index 000000000000..8298f039bef4
> > --- /dev/null
> > +++ b/arch/powerpc/platforms/pseries/plpks-secvar.c
> > @@ -0,0 +1,245 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Secure variable implementation using the PowerVM LPAR Platform
> > KeyStore (PLPKS)
> > + *
> > + * Copyright 2022, IBM Corporation
> 
> And by the time this gets merged, 2023

Optimistic!  I'll update that in v3.

> 
> > + * Authors: Russell Currey
> > + *          Andrew Donnellan
> > + *          Nayna Jain
> > + */
> > +
> > +#define pr_fmt(fmt) "secvar: "fmt
> > +
> > +#include <linux/printk.h>
> > +#include <linux/init.h>
> > +#include <linux/types.h>
> > +#include <linux/slab.h>
> > +#include <linux/string.h>
> > +#include <linux/kobject.h>
> > +#include <asm/secvar.h>
> > +#include "plpks.h"
> > +
> > +// Config attributes for sysfs
> > +#define PLPKS_CONFIG_ATTR(name, fmt, func)                     \
> > +       static ssize_t name##_show(struct kobject *kobj,        \
> > +                                  struct kobj_attribute *attr, \
> > +                                  char *buf)                   \
> > +       {                                                       \
> > +               return sysfs_emit(buf, fmt, func());            \
> > +       }                                                       \
> > +       static struct kobj_attribute attr_##name = __ATTR_RO(name)
> > +
> > +PLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version);
> > +PLPKS_CONFIG_ATTR(max_object_size, "%u\n",
> > plpks_get_maxobjectsize);
> > +PLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize);
> > +PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);
> > +PLPKS_CONFIG_ATTR(supported_policies, "%08x\n",
> > plpks_get_supportedpolicies);
> > +PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n",
> > plpks_get_signedupdatealgorithms);
> > +
> > +static const struct attribute *config_attrs[] = {
> > +       &attr_version.attr,
> > +       &attr_max_object_size.attr,
> > +       &attr_total_size.attr,
> > +       &attr_used_space.attr,
> > +       &attr_supported_policies.attr,
> > +       &attr_signed_update_algorithms.attr,
> > +       NULL,
> > +};
> > +
> > +static u16 get_ucs2name(const char *name, uint8_t **ucs2_name)
> > +{
> > +       int namelen = strlen(name) * 2;
> > +       *ucs2_name = kzalloc(namelen, GFP_KERNEL);
> > +
> > +       if (!*ucs2_name)
> > +               return 0;
> > +
> > +       for (int i = 0; name[i]; i++) {
> > +               (*ucs2_name)[i * 2] = name[i];
> > +               (*ucs2_name)[i * 2 + 1] = '\0';
> > +       }
> > +
> > +       return namelen;
> > +}
> > +
> > +static u32 get_policy(const char *name)
> > +{
> > +       if ((strcmp(name, "db") == 0) ||
> > +           (strcmp(name, "dbx") == 0) ||
> > +           (strcmp(name, "grubdb") == 0) ||
> > +           (strcmp(name, "sbat") == 0))
> > +               return (WORLDREADABLE | SIGNEDUPDATE);
> > +       else
> > +               return SIGNEDUPDATE;
> > +}
> > +
> > +#define PLPKS_SECVAR_COUNT 8
> > +static char *var_names[PLPKS_SECVAR_COUNT] = {
> > +       "PK",
> > +       "KEK",
> > +       "db",
> > +       "dbx",
> > +       "grubdb",
> > +       "sbat",
> > +       "moduledb",
> > +       "trustedcadb",
> > +};
> > +
> > +static int plpks_get_variable(const char *key, uint64_t key_len,
> > +                             u8 *data, uint64_t *data_size)
> > +{
> > +       struct plpks_var var = {0};
> > +       u16 ucs2_namelen;
> > +       u8 *ucs2_name;
> > +       int rc = 0;
> > +
> > +       ucs2_namelen = get_ucs2name(key, &ucs2_name);
> > +       if (!ucs2_namelen)
> > +               return -ENOMEM;
> > +
> > +       var.name = ucs2_name;
> > +       var.namelen = ucs2_namelen;
> > +       var.os = PLPKS_VAR_LINUX;
> > +       rc = plpks_read_os_var(&var);
> > +
> > +       if (rc)
> > +               goto err;
> > +
> > +       *data_size = var.datalen + sizeof(var.policy);
> > +
> > +       // We can be called with data = NULL to just get the object
> > size.
> > +       if (data) {
> > +               memcpy(data, &var.policy, sizeof(var.policy));
> > +               memcpy(data + sizeof(var.policy), var.data,
> > var.datalen);
> > +       }
> > +
> > +       kfree(var.data);
> > +err:
> > +       kfree(ucs2_name);
> > +       return rc;
> > +}
> > +
> > +static int plpks_set_variable(const char *key, uint64_t key_len,
> > +                             u8 *data, uint64_t data_size)
> > +{
> > +       struct plpks_var var = {0};
> > +       u16 ucs2_namelen;
> > +       u8 *ucs2_name;
> > +       int rc = 0;
> > +       u64 flags;
> > +
> > +       // Secure variables need to be prefixed with 8 bytes of
> > flags.
> > +       // We only want to perform the write if we have at least
> > one
> > byte of data.
> > +       if (data_size <= sizeof(flags))
> > +               return -EINVAL;
> > +
> > +       ucs2_namelen = get_ucs2name(key, &ucs2_name);
> > +       if (!ucs2_namelen)
> > +               return -ENOMEM;
> > +
> > +       memcpy(&flags, data, sizeof(flags));
> > +
> > +       var.datalen = data_size - sizeof(flags);
> > +       var.data = kzalloc(var.datalen, GFP_KERNEL);
> > +       if (!var.data) {
> > +               rc = -ENOMEM;
> > +               goto err;
> > +       }
> > +
> > +       memcpy(var.data, data + sizeof(flags), var.datalen);
> > +
> > +       var.name = ucs2_name;
> > +       var.namelen = ucs2_namelen;
> > +       var.os = PLPKS_VAR_LINUX;
> > +       var.policy = get_policy(key);
> > +
> > +       rc = plpks_signed_update_var(var, flags);
> > +
> > +       kfree(var.data);
> > +err:
> > +       kfree(ucs2_name);
> > +       return rc;
> > +}
> > +
> > +/*
> > + * get_next() in the secvar API is designed for the OPAL API.
> > + * If *key is 0, it returns the first variable in the keystore.
> > + * Otherwise, you pass the name of a key and it returns next in
> > line.
> > + *
> > + * We're going to cheat here - since we have fixed keys and don't
> > care about
> > + * key_len, we can just use it as an index.
> 
> This is kinda gross.

It's gross, but it's really simple and means we don't either have to
have two ops for the same thing (getting all the variables) or rework
powernv to better fit pseries.  Open to suggestions on other ways to do
it, this is just the best I came up with.

> 
> > + */
> 
> Inconsistent comment style

True, I'm using // for multi-line comments in other places.  I think my
brain decided that this one was too long for that, but I'll make the
other multi-line comments similarly old-fashioned for consistency.

> 
> > +static int plpks_get_next_variable(const char *key, uint64_t
> > *key_len, uint64_t keybufsize)
> > +{
> > +       if (!key || !key_len)
> > +               return -EINVAL;
> > +
> > +       if (*key_len >= PLPKS_SECVAR_COUNT)
> > +               return -ENOENT;
> > +
> > +       if (strscpy((char *)key, var_names[(*key_len)++],
> > keybufsize)
> > < 0)
> 
> Not sure how I feel about the increment being buried in here

Yeah this is definitely more clever than clear, I'll separate the
increment.

Cheers for the review (and for your contributions obviously)

> 
> > +               return -E2BIG;
> > +
> > +       return 0;
> > +}
> > +
> > +// PLPKS dynamic secure boot doesn't give us a format string in
> > the
> > same way OPAL does.
> > +// Instead, report the format using the SB_VERSION variable in the
> > keystore.
> > +static ssize_t plpks_secvar_format(char *buf)
> > +{
> > +       struct plpks_var var = {0};
> > +       ssize_t ret;
> > +
> > +       var.component = NULL;
> > +       // Only the signed variables have ucs2-encoded names, this
> > one doesn't
> > +       var.name = "SB_VERSION";
> > +       var.namelen = 10;
> > +       var.datalen = 0;
> > +       var.data = NULL;
> > +
> > +       // Unlike the other vars, SB_VERSION is owned by firmware
> > instead of the OS
> > +       ret = plpks_read_fw_var(&var);
> > +       if (ret) {
> > +               if (ret == -ENOENT)
> > +                       return sysfs_emit(buf, "ibm,plpks-sb-
> > unknown\n");
> > +
> > +               pr_err("Error %ld reading SB_VERSION from
> > firmware\n", ret);
> > +               return ret;
> > +       }
> > +
> > +       // Hypervisor defines SB_VERSION as a "1 byte unsigned
> > integer value"
> > +       ret = sysfs_emit(buf, "ibm,plpks-sb-%hhu\n", var.data[0]);
> > +
> > +       kfree(var.data);
> > +       return ret;
> > +}
> > +
> > +static int plpks_max_size(uint64_t *max_size)
> > +{
> > +       // The max object size reported by the hypervisor is
> > accurate
> > for the
> > +       // object itself, but we use the first 8 bytes of data on
> > write as the
> > +       // signed update flags, so the max size a user can write is
> > larger.
> > +       *max_size = (uint64_t)plpks_get_maxobjectsize() + 8;
> > +
> > +       return 0;
> > +}
> > +
> > +
> > +static const struct secvar_operations plpks_secvar_ops = {
> > +       .get = plpks_get_variable,
> > +       .get_next = plpks_get_next_variable,
> > +       .set = plpks_set_variable,
> > +       .format = plpks_secvar_format,
> > +       .max_size = plpks_max_size,
> > +};
> > +
> > +static int plpks_secvar_init(void)
> > +{
> > +       if (!plpks_is_available())
> > +               return -ENODEV;
> > +
> > +       set_secvar_ops(&plpks_secvar_ops);
> > +       set_secvar_config_attrs(config_attrs);
> > +       return 0;
> > +}
> > +device_initcall(plpks_secvar_init);
> 


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

* Re: [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
@ 2023-01-06  6:49       ` Russell Currey
  0 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2023-01-06  6:49 UTC (permalink / raw)
  To: Andrew Donnellan, linuxppc-dev
  Cc: gregkh, nayna, linux-kernel, zohar, gcwilson

On Thu, 2023-01-05 at 19:15 +1100, Andrew Donnellan wrote:
> On Fri, 2022-12-30 at 15:20 +1100, Russell Currey wrote:
> > The pseries platform can support dynamic secure boot (i.e. secure
> > boot
> > using user-defined keys) using variables contained with the PowerVM
> > LPAR
> > Platform KeyStore (PLPKS).  Using the powerpc secvar API, expose
> > the
> > relevant variables for pseries dynamic secure boot through the
> > existing
> > secvar filesystem layout.
> > 
> > The relevant variables for dynamic secure boot are signed in the
> > keystore, and can only be modified using the H_PKS_SIGNED_UPDATE
> > hcall.
> > Object labels in the keystore are encoded using ucs2 format.  With
> > our
> > fixed variable names we don't have to care about encoding outside
> > of
> > the
> > necessary byte padding.
> > 
> > When a user writes to a variable, the first 8 bytes of data must
> > contain
> > the signed update flags as defined by the hypervisor.
> > 
> > When a user reads a variable, the first 4 bytes of data contain the
> > policies defined for the object.
> > 
> > Limitations exist due to the underlying implementation of sysfs
> > binary
> > attributes, as is the case for the OPAL secvar implementation -
> > partial writes are unsupported and writes cannot be larger than
> > PAGE_SIZE.
> 
> The PAGE_SIZE limit, in practice, will be a major limitation with 4K
> pages (we expect several of the variables to regularly be larger than
> 4K) but won't be much of an issue for 64K (that's all the storage
> space
> the hypervisor will give you anyway).
> 
> In a previous internal version, we printed a message when PAGE_SIZE <
> plpks_get_maxobjectsize(), might be worth still doing that?

Yeah, we should do that in the secvar code.  The same limitation
applies on the powernv side as well.

> 
> > 
> > Co-developed-by: Nayna Jain <nayna@linux.ibm.com>
> > Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
> > Co-developed-by: Andrew Donnellan <ajd@linux.ibm.com>
> > Signed-off-by: Andrew Donnellan <ajd@linux.ibm.com>
> > Signed-off-by: Russell Currey <ruscur@russell.cc>
> 
> Some minor comments for v3 on a patch which already carries my
> signoff...
> 
> > ---
> > v2: Remove unnecessary config vars from sysfs and document the
> > others,
> >     thanks to review from Greg.  If we end up needing to expose
> > more,
> > we
> >     can add them later and update the docs.
> > 
> >     Use sysfs_emit() instead of sprintf(), thanks to Greg.
> > 
> >     Change the size of the sysfs binary attributes to include the
> > 8-
> > byte
> >     flags header, preventing truncation of large writes.
> > 
> >  Documentation/ABI/testing/sysfs-secvar        |  67 ++++-
> >  arch/powerpc/platforms/pseries/Kconfig        |  13 +
> >  arch/powerpc/platforms/pseries/Makefile       |   4 +-
> >  arch/powerpc/platforms/pseries/plpks-secvar.c | 245
> > ++++++++++++++++++
> >  4 files changed, 326 insertions(+), 3 deletions(-)
> >  create mode 100644 arch/powerpc/platforms/pseries/plpks-secvar.c
> > 
> > diff --git a/Documentation/ABI/testing/sysfs-secvar
> > b/Documentation/ABI/testing/sysfs-secvar
> > index feebb8c57294..466a8cb92b92 100644
> > --- a/Documentation/ABI/testing/sysfs-secvar
> > +++ b/Documentation/ABI/testing/sysfs-secvar
> > @@ -34,7 +34,7 @@ Description:  An integer representation of the
> > size
> > of the content of the
> >  
> >  What:          /sys/firmware/secvar/vars/<variable_name>/data
> >  Date:          August 2019
> > -Contact:       Nayna Jain h<nayna@linux.ibm.com>
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> >  Description:   A read-only file containing the value of the
> > variable. The size
> >                 of the file represents the maximum size of the
> > variable data.
> >  
> > @@ -44,3 +44,68 @@ Contact:     Nayna Jain <nayna@linux.ibm.com>
> >  Description:   A write-only file that is used to submit the new
> > value for the
> >                 variable. The size of the file represents the
> > maximum
> > size of
> >                 the variable data that can be written.
> > +
> > +What:          /sys/firmware/secvar/config
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   This optional directory contains read-only config
> > attributes as
> > +               defined by the secure variable implementation.  All
> > data is in
> > +               ASCII format. The directory is only created if the
> > backing
> > +               implementation provides variables to populate it,
> > which at
> > +               present is only PLPKS on the pseries platform.
> > +
> > +What:          /sys/firmware/secvar/config/version
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is
> > PLPKS.
> > +
> > +               Contains the config version as reported by the
> > hypervisor in
> > +               ASCII decimal format.
> > +
> > +What:          /sys/firmware/secvar/config/max_object_size
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is
> > PLPKS.
> > +
> > +               Contains the maximum allowed size of objects in the
> > keystore
> > +               in bytes, represented in ASCII decimal format.
> > +
> > +               This is not necessarily the same as the max size
> > that
> > can be
> > +               written to an update file as writes can contain
> > more
> > than
> > +               object data, you should use the size of the update
> > file for
> > +               that purpose.
> > +
> > +What:          /sys/firmware/secvar/config/total_size
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is
> > PLPKS.
> > +
> > +               Contains the total size of the PLPKS in bytes,
> > represented in
> > +               ASCII decimal format.
> > +
> > +What:          /sys/firmware/secvar/config/used_space
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is
> > PLPKS.
> > +
> > +               Contains the current space consumed of the PLPKS in
> > bytes,
> > +               represented in ASCII decimal format.
> 
> Note that presently, this isn't actually updated when the user writes
> objects. I suppose we could fix this.

We should definitely fix that.

> 
> > +
> > +What:          /sys/firmware/secvar/config/supported_policies
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is
> > PLPKS.
> > +
> > +               Contains a bitmask of supported policy flags by the
> > hypervisor,
> > +               represented as an 8 byte hexadecimal ASCII string. 
> > Consult the
> > +               hypervisor documentation for what these flags are.
> > +
> > +What:          /sys/firmware/secvar/config/signed_update_algorithm
> > s
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is
> > PLPKS.
> > +
> > +               Contains a bitmask of flags indicating which
> > algorithms the
> > +               hypervisor supports objects to be signed with when
> > modifying
> > +               secvars, represented as a 16 byte hexadecimal ASCII
> > string.
> > +               Consult the hypervisor documentation for what these
> > flags mean.
> > diff --git a/arch/powerpc/platforms/pseries/Kconfig
> > b/arch/powerpc/platforms/pseries/Kconfig
> > index a3b4d99567cb..94e08c405d50 100644
> > --- a/arch/powerpc/platforms/pseries/Kconfig
> > +++ b/arch/powerpc/platforms/pseries/Kconfig
> > @@ -162,6 +162,19 @@ config PSERIES_PLPKS
> >  
> >           If unsure, select N.
> >  
> > +config PSERIES_PLPKS_SECVAR
> > +       depends on PSERIES_PLPKS
> 
> PSERIES_PLPKS has no use on its own without the secvar frontend. We
> should either just have one option, or for future-proofing purposes
> turn this depends into a select and get PSERIES_PLPKS out of
> menuconfig.

I believe I did this to enforce the dependency on PPC_SECURE_BOOT.  As
of right now they're tied together, but if I add that dependency to
PSERIES_PLPKS, then it makes things awkward if there's a more generic
PLPKS interface in future that isn't specific to secure boot.  I
suppose things can be compartmentalised in future if that happens.

> 
> > +       depends on PPC_SECURE_BOOT
> 
> FWIW, starting from a pseries_le_defconfig, turning on all the
> options
> that are required to get to PPC_SECURE_BOOT was annoying. I'd like to
> have PSERIES_PLPKS_SECVAR enabled in the defconfig but it involves
> switching on quite a lot.

Probably more of an mpe question, but we have to turn on a bunch of IMA
stuff that adds complexity and increases the build time, so I'm not
sure if we'd want it in pseries_defconfig.  We could definitely either
add things to security.config or make a new config fragment for it
though.

> 
> > +       bool "Support for the PLPKS secvar interface"
> > +       help
> > +         PowerVM can support dynamic secure boot with user-defined
> > keys
> > +         through the PLPKS. Keystore objects used in dynamic
> > secure
> > boot
> 
> We should also expand PLPKS to PowerVM LPAR Platform KeyStore.

True.

> 
> > +         can be exposed to the kernel and userspace through the
> > powerpc
> > +         secvar infrastructure. Select this to enable the PLPKS
> > backend
> > +         for secvars for use in pseries dynamic secure boot.
> > +
> > +         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 92310202bdd7..807756991f9d 100644
> > --- a/arch/powerpc/platforms/pseries/Makefile
> > +++ b/arch/powerpc/platforms/pseries/Makefile
> > @@ -27,8 +27,8 @@ obj-$(CONFIG_PAPR_SCM)                +=
> > papr_scm.o
> >  obj-$(CONFIG_PPC_SPLPAR)       += vphn.o
> >  obj-$(CONFIG_PPC_SVM)          += svm.o
> >  obj-$(CONFIG_FA_DUMP)          += rtas-fadump.o
> > -obj-$(CONFIG_PSERIES_PLPKS) += plpks.o
> > -
> > +obj-$(CONFIG_PSERIES_PLPKS)    += plpks.o
> > +obj-$(CONFIG_PSERIES_PLPKS_SECVAR)     += plpks-secvar.o
> >  obj-$(CONFIG_SUSPEND)          += suspend.o
> >  obj-$(CONFIG_PPC_VAS)          += vas.o vas-sysfs.o
> >  
> > diff --git a/arch/powerpc/platforms/pseries/plpks-secvar.c
> > b/arch/powerpc/platforms/pseries/plpks-secvar.c
> > new file mode 100644
> > index 000000000000..8298f039bef4
> > --- /dev/null
> > +++ b/arch/powerpc/platforms/pseries/plpks-secvar.c
> > @@ -0,0 +1,245 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Secure variable implementation using the PowerVM LPAR Platform
> > KeyStore (PLPKS)
> > + *
> > + * Copyright 2022, IBM Corporation
> 
> And by the time this gets merged, 2023

Optimistic!  I'll update that in v3.

> 
> > + * Authors: Russell Currey
> > + *          Andrew Donnellan
> > + *          Nayna Jain
> > + */
> > +
> > +#define pr_fmt(fmt) "secvar: "fmt
> > +
> > +#include <linux/printk.h>
> > +#include <linux/init.h>
> > +#include <linux/types.h>
> > +#include <linux/slab.h>
> > +#include <linux/string.h>
> > +#include <linux/kobject.h>
> > +#include <asm/secvar.h>
> > +#include "plpks.h"
> > +
> > +// Config attributes for sysfs
> > +#define PLPKS_CONFIG_ATTR(name, fmt, func)                     \
> > +       static ssize_t name##_show(struct kobject *kobj,        \
> > +                                  struct kobj_attribute *attr, \
> > +                                  char *buf)                   \
> > +       {                                                       \
> > +               return sysfs_emit(buf, fmt, func());            \
> > +       }                                                       \
> > +       static struct kobj_attribute attr_##name = __ATTR_RO(name)
> > +
> > +PLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version);
> > +PLPKS_CONFIG_ATTR(max_object_size, "%u\n",
> > plpks_get_maxobjectsize);
> > +PLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize);
> > +PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);
> > +PLPKS_CONFIG_ATTR(supported_policies, "%08x\n",
> > plpks_get_supportedpolicies);
> > +PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n",
> > plpks_get_signedupdatealgorithms);
> > +
> > +static const struct attribute *config_attrs[] = {
> > +       &attr_version.attr,
> > +       &attr_max_object_size.attr,
> > +       &attr_total_size.attr,
> > +       &attr_used_space.attr,
> > +       &attr_supported_policies.attr,
> > +       &attr_signed_update_algorithms.attr,
> > +       NULL,
> > +};
> > +
> > +static u16 get_ucs2name(const char *name, uint8_t **ucs2_name)
> > +{
> > +       int namelen = strlen(name) * 2;
> > +       *ucs2_name = kzalloc(namelen, GFP_KERNEL);
> > +
> > +       if (!*ucs2_name)
> > +               return 0;
> > +
> > +       for (int i = 0; name[i]; i++) {
> > +               (*ucs2_name)[i * 2] = name[i];
> > +               (*ucs2_name)[i * 2 + 1] = '\0';
> > +       }
> > +
> > +       return namelen;
> > +}
> > +
> > +static u32 get_policy(const char *name)
> > +{
> > +       if ((strcmp(name, "db") == 0) ||
> > +           (strcmp(name, "dbx") == 0) ||
> > +           (strcmp(name, "grubdb") == 0) ||
> > +           (strcmp(name, "sbat") == 0))
> > +               return (WORLDREADABLE | SIGNEDUPDATE);
> > +       else
> > +               return SIGNEDUPDATE;
> > +}
> > +
> > +#define PLPKS_SECVAR_COUNT 8
> > +static char *var_names[PLPKS_SECVAR_COUNT] = {
> > +       "PK",
> > +       "KEK",
> > +       "db",
> > +       "dbx",
> > +       "grubdb",
> > +       "sbat",
> > +       "moduledb",
> > +       "trustedcadb",
> > +};
> > +
> > +static int plpks_get_variable(const char *key, uint64_t key_len,
> > +                             u8 *data, uint64_t *data_size)
> > +{
> > +       struct plpks_var var = {0};
> > +       u16 ucs2_namelen;
> > +       u8 *ucs2_name;
> > +       int rc = 0;
> > +
> > +       ucs2_namelen = get_ucs2name(key, &ucs2_name);
> > +       if (!ucs2_namelen)
> > +               return -ENOMEM;
> > +
> > +       var.name = ucs2_name;
> > +       var.namelen = ucs2_namelen;
> > +       var.os = PLPKS_VAR_LINUX;
> > +       rc = plpks_read_os_var(&var);
> > +
> > +       if (rc)
> > +               goto err;
> > +
> > +       *data_size = var.datalen + sizeof(var.policy);
> > +
> > +       // We can be called with data = NULL to just get the object
> > size.
> > +       if (data) {
> > +               memcpy(data, &var.policy, sizeof(var.policy));
> > +               memcpy(data + sizeof(var.policy), var.data,
> > var.datalen);
> > +       }
> > +
> > +       kfree(var.data);
> > +err:
> > +       kfree(ucs2_name);
> > +       return rc;
> > +}
> > +
> > +static int plpks_set_variable(const char *key, uint64_t key_len,
> > +                             u8 *data, uint64_t data_size)
> > +{
> > +       struct plpks_var var = {0};
> > +       u16 ucs2_namelen;
> > +       u8 *ucs2_name;
> > +       int rc = 0;
> > +       u64 flags;
> > +
> > +       // Secure variables need to be prefixed with 8 bytes of
> > flags.
> > +       // We only want to perform the write if we have at least
> > one
> > byte of data.
> > +       if (data_size <= sizeof(flags))
> > +               return -EINVAL;
> > +
> > +       ucs2_namelen = get_ucs2name(key, &ucs2_name);
> > +       if (!ucs2_namelen)
> > +               return -ENOMEM;
> > +
> > +       memcpy(&flags, data, sizeof(flags));
> > +
> > +       var.datalen = data_size - sizeof(flags);
> > +       var.data = kzalloc(var.datalen, GFP_KERNEL);
> > +       if (!var.data) {
> > +               rc = -ENOMEM;
> > +               goto err;
> > +       }
> > +
> > +       memcpy(var.data, data + sizeof(flags), var.datalen);
> > +
> > +       var.name = ucs2_name;
> > +       var.namelen = ucs2_namelen;
> > +       var.os = PLPKS_VAR_LINUX;
> > +       var.policy = get_policy(key);
> > +
> > +       rc = plpks_signed_update_var(var, flags);
> > +
> > +       kfree(var.data);
> > +err:
> > +       kfree(ucs2_name);
> > +       return rc;
> > +}
> > +
> > +/*
> > + * get_next() in the secvar API is designed for the OPAL API.
> > + * If *key is 0, it returns the first variable in the keystore.
> > + * Otherwise, you pass the name of a key and it returns next in
> > line.
> > + *
> > + * We're going to cheat here - since we have fixed keys and don't
> > care about
> > + * key_len, we can just use it as an index.
> 
> This is kinda gross.

It's gross, but it's really simple and means we don't either have to
have two ops for the same thing (getting all the variables) or rework
powernv to better fit pseries.  Open to suggestions on other ways to do
it, this is just the best I came up with.

> 
> > + */
> 
> Inconsistent comment style

True, I'm using // for multi-line comments in other places.  I think my
brain decided that this one was too long for that, but I'll make the
other multi-line comments similarly old-fashioned for consistency.

> 
> > +static int plpks_get_next_variable(const char *key, uint64_t
> > *key_len, uint64_t keybufsize)
> > +{
> > +       if (!key || !key_len)
> > +               return -EINVAL;
> > +
> > +       if (*key_len >= PLPKS_SECVAR_COUNT)
> > +               return -ENOENT;
> > +
> > +       if (strscpy((char *)key, var_names[(*key_len)++],
> > keybufsize)
> > < 0)
> 
> Not sure how I feel about the increment being buried in here

Yeah this is definitely more clever than clear, I'll separate the
increment.

Cheers for the review (and for your contributions obviously)

> 
> > +               return -E2BIG;
> > +
> > +       return 0;
> > +}
> > +
> > +// PLPKS dynamic secure boot doesn't give us a format string in
> > the
> > same way OPAL does.
> > +// Instead, report the format using the SB_VERSION variable in the
> > keystore.
> > +static ssize_t plpks_secvar_format(char *buf)
> > +{
> > +       struct plpks_var var = {0};
> > +       ssize_t ret;
> > +
> > +       var.component = NULL;
> > +       // Only the signed variables have ucs2-encoded names, this
> > one doesn't
> > +       var.name = "SB_VERSION";
> > +       var.namelen = 10;
> > +       var.datalen = 0;
> > +       var.data = NULL;
> > +
> > +       // Unlike the other vars, SB_VERSION is owned by firmware
> > instead of the OS
> > +       ret = plpks_read_fw_var(&var);
> > +       if (ret) {
> > +               if (ret == -ENOENT)
> > +                       return sysfs_emit(buf, "ibm,plpks-sb-
> > unknown\n");
> > +
> > +               pr_err("Error %ld reading SB_VERSION from
> > firmware\n", ret);
> > +               return ret;
> > +       }
> > +
> > +       // Hypervisor defines SB_VERSION as a "1 byte unsigned
> > integer value"
> > +       ret = sysfs_emit(buf, "ibm,plpks-sb-%hhu\n", var.data[0]);
> > +
> > +       kfree(var.data);
> > +       return ret;
> > +}
> > +
> > +static int plpks_max_size(uint64_t *max_size)
> > +{
> > +       // The max object size reported by the hypervisor is
> > accurate
> > for the
> > +       // object itself, but we use the first 8 bytes of data on
> > write as the
> > +       // signed update flags, so the max size a user can write is
> > larger.
> > +       *max_size = (uint64_t)plpks_get_maxobjectsize() + 8;
> > +
> > +       return 0;
> > +}
> > +
> > +
> > +static const struct secvar_operations plpks_secvar_ops = {
> > +       .get = plpks_get_variable,
> > +       .get_next = plpks_get_next_variable,
> > +       .set = plpks_set_variable,
> > +       .format = plpks_secvar_format,
> > +       .max_size = plpks_max_size,
> > +};
> > +
> > +static int plpks_secvar_init(void)
> > +{
> > +       if (!plpks_is_available())
> > +               return -ENODEV;
> > +
> > +       set_secvar_ops(&plpks_secvar_ops);
> > +       set_secvar_config_attrs(config_attrs);
> > +       return 0;
> > +}
> > +device_initcall(plpks_secvar_init);
> 


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

* Re: [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
  2022-12-30  4:20   ` Russell Currey
@ 2023-01-06 10:49     ` Michael Ellerman
  -1 siblings, 0 replies; 54+ messages in thread
From: Michael Ellerman @ 2023-01-06 10:49 UTC (permalink / raw)
  To: Russell Currey, linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, ajd, zohar, Russell Currey

Russell Currey <ruscur@russell.cc> writes:
> The pseries platform can support dynamic secure boot (i.e. secure boot
> using user-defined keys) using variables contained with the PowerVM LPAR
> Platform KeyStore (PLPKS).  Using the powerpc secvar API, expose the
> relevant variables for pseries dynamic secure boot through the existing
> secvar filesystem layout.
>
> The relevant variables for dynamic secure boot are signed in the
> keystore, and can only be modified using the H_PKS_SIGNED_UPDATE hcall.
> Object labels in the keystore are encoded using ucs2 format.  With our
> fixed variable names we don't have to care about encoding outside of the
> necessary byte padding.
>
> When a user writes to a variable, the first 8 bytes of data must contain
> the signed update flags as defined by the hypervisor.
>
> When a user reads a variable, the first 4 bytes of data contain the
> policies defined for the object.
>
> Limitations exist due to the underlying implementation of sysfs binary
> attributes, as is the case for the OPAL secvar implementation -
> partial writes are unsupported and writes cannot be larger than PAGE_SIZE.
>
> Co-developed-by: Nayna Jain <nayna@linux.ibm.com>
> Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
> Co-developed-by: Andrew Donnellan <ajd@linux.ibm.com>
> Signed-off-by: Andrew Donnellan <ajd@linux.ibm.com>
> Signed-off-by: Russell Currey <ruscur@russell.cc>
> ---
> v2: Remove unnecessary config vars from sysfs and document the others,
>     thanks to review from Greg.  If we end up needing to expose more, we
>     can add them later and update the docs.
>
>     Use sysfs_emit() instead of sprintf(), thanks to Greg.
>
>     Change the size of the sysfs binary attributes to include the 8-byte
>     flags header, preventing truncation of large writes.
>
>  Documentation/ABI/testing/sysfs-secvar        |  67 ++++-
>  arch/powerpc/platforms/pseries/Kconfig        |  13 +
>  arch/powerpc/platforms/pseries/Makefile       |   4 +-
>  arch/powerpc/platforms/pseries/plpks-secvar.c | 245 ++++++++++++++++++
>  4 files changed, 326 insertions(+), 3 deletions(-)
>  create mode 100644 arch/powerpc/platforms/pseries/plpks-secvar.c
>
> diff --git a/Documentation/ABI/testing/sysfs-secvar b/Documentation/ABI/testing/sysfs-secvar
> index feebb8c57294..466a8cb92b92 100644
> --- a/Documentation/ABI/testing/sysfs-secvar
> +++ b/Documentation/ABI/testing/sysfs-secvar
> @@ -34,7 +34,7 @@ Description:	An integer representation of the size of the content of the
>  
>  What:		/sys/firmware/secvar/vars/<variable_name>/data
>  Date:		August 2019
> -Contact:	Nayna Jain h<nayna@linux.ibm.com>
> +Contact:	Nayna Jain <nayna@linux.ibm.com>
>  Description:	A read-only file containing the value of the variable. The size
>  		of the file represents the maximum size of the variable data.
>  
> @@ -44,3 +44,68 @@ Contact:	Nayna Jain <nayna@linux.ibm.com>
>  Description:	A write-only file that is used to submit the new value for the
>  		variable. The size of the file represents the maximum size of
>  		the variable data that can be written.
> +
> +What:		/sys/firmware/secvar/config
> +Date:		December 2022
> +Contact:	Nayna Jain <nayna@linux.ibm.com>
> +Description:	This optional directory contains read-only config attributes as
> +		defined by the secure variable implementation.  All data is in
> +		ASCII format. The directory is only created if the backing
> +		implementation provides variables to populate it, which at
> +		present is only PLPKS on the pseries platform.

I think it's OK to mention that currently this only exists for PLPKS ...

> +What:		/sys/firmware/secvar/config/version
> +Date:		December 2022
> +Contact:	Nayna Jain <nayna@linux.ibm.com>
> +Description:	RO file, only present if the secvar implementation is PLPKS.

... but I don't think we want to specify that files are only present for PLPKS. 

Because if another backend wanted to create them in future, that would
technically be an ABI change.

> +		Contains the config version as reported by the hypervisor in
> +		ASCII decimal format.
> +
> +What:		/sys/firmware/secvar/config/max_object_size
> +Date:		December 2022
> +Contact:	Nayna Jain <nayna@linux.ibm.com>
> +Description:	RO file, only present if the secvar implementation is PLPKS.
> +
> +		Contains the maximum allowed size of objects in the keystore
> +		in bytes, represented in ASCII decimal format.
> +
> +		This is not necessarily the same as the max size that can be
> +		written to an update file as writes can contain more than
> +		object data, you should use the size of the update file for
> +		that purpose.
> +
> +What:		/sys/firmware/secvar/config/total_size
> +Date:		December 2022
> +Contact:	Nayna Jain <nayna@linux.ibm.com>
> +Description:	RO file, only present if the secvar implementation is PLPKS.
> +
> +		Contains the total size of the PLPKS in bytes, represented in
> +		ASCII decimal format.

Similarly here I think the description should be written in a way that
is agnostic about the backend. So eg. "Contains the total size of the
key store in bytes".


> +What:		/sys/firmware/secvar/config/used_space
> +Date:		December 2022
> +Contact:	Nayna Jain <nayna@linux.ibm.com>
> +Description:	RO file, only present if the secvar implementation is PLPKS.
> +
> +		Contains the current space consumed of the PLPKS in bytes,
> +		represented in ASCII decimal format.
> +
> +What:		/sys/firmware/secvar/config/supported_policies
> +Date:		December 2022
> +Contact:	Nayna Jain <nayna@linux.ibm.com>
> +Description:	RO file, only present if the secvar implementation is PLPKS.
> +
> +		Contains a bitmask of supported policy flags by the hypervisor,
> +		represented as an 8 byte hexadecimal ASCII string.  Consult the
> +		hypervisor documentation for what these flags are.
> +
> +What:		/sys/firmware/secvar/config/signed_update_algorithms
> +Date:		December 2022
> +Contact:	Nayna Jain <nayna@linux.ibm.com>
> +Description:	RO file, only present if the secvar implementation is PLPKS.
> +
> +		Contains a bitmask of flags indicating which algorithms the
> +		hypervisor supports objects to be signed with when modifying
> +		secvars, represented as a 16 byte hexadecimal ASCII string.
> +		Consult the hypervisor documentation for what these flags mean.
 
Can this at least say "as defined in PAPR version X section Y"?

> diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
> index a3b4d99567cb..94e08c405d50 100644
> --- a/arch/powerpc/platforms/pseries/Kconfig
> +++ b/arch/powerpc/platforms/pseries/Kconfig
> @@ -162,6 +162,19 @@ config PSERIES_PLPKS
>  
>  	  If unsure, select N.
>  
> +config PSERIES_PLPKS_SECVAR
> +	depends on PSERIES_PLPKS
> +	depends on PPC_SECURE_BOOT
> +	bool "Support for the PLPKS secvar interface"
> +	help
> +	  PowerVM can support dynamic secure boot with user-defined keys
> +	  through the PLPKS. Keystore objects used in dynamic secure boot
> +	  can be exposed to the kernel and userspace through the powerpc
> +	  secvar infrastructure. Select this to enable the PLPKS backend
> +	  for secvars for use in pseries dynamic secure boot.
> +
> +	  If unsure, select N.

I don't think we need that config option at all, or if we do it should
not be user selectable and just enabled automatically by PSERIES_PLPKS.

> diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
> index 92310202bdd7..807756991f9d 100644
> --- a/arch/powerpc/platforms/pseries/Makefile
> +++ b/arch/powerpc/platforms/pseries/Makefile
> @@ -27,8 +27,8 @@ obj-$(CONFIG_PAPR_SCM)		+= papr_scm.o
>  obj-$(CONFIG_PPC_SPLPAR)	+= vphn.o
>  obj-$(CONFIG_PPC_SVM)		+= svm.o
>  obj-$(CONFIG_FA_DUMP)		+= rtas-fadump.o
> -obj-$(CONFIG_PSERIES_PLPKS) += plpks.o
> -
> +obj-$(CONFIG_PSERIES_PLPKS)	+= plpks.o
> +obj-$(CONFIG_PSERIES_PLPKS_SECVAR)	+= plpks-secvar.o

I'm not convinced the secvar parts need to be in a separate C file.

If it was all in plpks.c we could avoid all/most of plpks.h and a bunch
of accessors and so on.

But I don't feel that strongly about it if you think it's better separate.

> diff --git a/arch/powerpc/platforms/pseries/plpks-secvar.c b/arch/powerpc/platforms/pseries/plpks-secvar.c
> new file mode 100644
> index 000000000000..8298f039bef4
> --- /dev/null
> +++ b/arch/powerpc/platforms/pseries/plpks-secvar.c
> @@ -0,0 +1,245 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Secure variable implementation using the PowerVM LPAR Platform KeyStore (PLPKS)
> + *
> + * Copyright 2022, IBM Corporation
> + * Authors: Russell Currey
> + *          Andrew Donnellan
> + *          Nayna Jain
> + */
> +
> +#define pr_fmt(fmt) "secvar: "fmt
> +
> +#include <linux/printk.h>
> +#include <linux/init.h>
> +#include <linux/types.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +#include <linux/kobject.h>
> +#include <asm/secvar.h>
> +#include "plpks.h"
> +
> +// Config attributes for sysfs
> +#define PLPKS_CONFIG_ATTR(name, fmt, func)			\
> +	static ssize_t name##_show(struct kobject *kobj,	\
> +				   struct kobj_attribute *attr,	\
> +				   char *buf)			\
> +	{							\
> +		return sysfs_emit(buf, fmt, func());		\
> +	}							\
> +	static struct kobj_attribute attr_##name = __ATTR_RO(name)
> +
> +PLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version);
> +PLPKS_CONFIG_ATTR(max_object_size, "%u\n", plpks_get_maxobjectsize);
> +PLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize);
> +PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);
> +PLPKS_CONFIG_ATTR(supported_policies, "%08x\n", plpks_get_supportedpolicies);
> +PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n", plpks_get_signedupdatealgorithms);

For those last two I wonder if we should be decoding the integer values
into a comma separated list of named flags?

Just blatting out the integer values is a bit gross. It's not helpful
for shell scripts, and a consumer written in C has to strtoull() the
value back into an integer before it can decode it.

> +static const struct attribute *config_attrs[] = {
> +	&attr_version.attr,
> +	&attr_max_object_size.attr,
> +	&attr_total_size.attr,
> +	&attr_used_space.attr,
> +	&attr_supported_policies.attr,
> +	&attr_signed_update_algorithms.attr,
> +	NULL,
> +};
> +
> +static u16 get_ucs2name(const char *name, uint8_t **ucs2_name)
> +{
> +	int namelen = strlen(name) * 2;
> +	*ucs2_name = kzalloc(namelen, GFP_KERNEL);
> +
> +	if (!*ucs2_name)
> +		return 0;
> +
> +	for (int i = 0; name[i]; i++) {
> +		(*ucs2_name)[i * 2] = name[i];
> +		(*ucs2_name)[i * 2 + 1] = '\0';
> +	}
> +
> +	return namelen;
> +}

There are some ucs2 routines in lib/ucs2_string.c, can we use any of
them?

> +static u32 get_policy(const char *name)
> +{
> +	if ((strcmp(name, "db") == 0) ||
> +	    (strcmp(name, "dbx") == 0) ||
> +	    (strcmp(name, "grubdb") == 0) ||
> +	    (strcmp(name, "sbat") == 0))
> +		return (WORLDREADABLE | SIGNEDUPDATE);
> +	else
> +		return SIGNEDUPDATE;
> +}
> +
> +#define PLPKS_SECVAR_COUNT 8

I don't think we need that. Just declare the array as unsized and then
use ARRAY_SIZE(var_names) in plpks_get_next_variable().

> +static char *var_names[PLPKS_SECVAR_COUNT] = {
> +	"PK",
> +	"KEK",
> +	"db",
> +	"dbx",
> +	"grubdb",
> +	"sbat",
> +	"moduledb",
> +	"trustedcadb",
> +};
> +
> +static int plpks_get_variable(const char *key, uint64_t key_len,
> +			      u8 *data, uint64_t *data_size)
> +{
> +	struct plpks_var var = {0};
> +	u16 ucs2_namelen;
> +	u8 *ucs2_name;
> +	int rc = 0;
> +
> +	ucs2_namelen = get_ucs2name(key, &ucs2_name);
> +	if (!ucs2_namelen)
> +		return -ENOMEM;
> +
> +	var.name = ucs2_name;
> +	var.namelen = ucs2_namelen;
> +	var.os = PLPKS_VAR_LINUX;
> +	rc = plpks_read_os_var(&var);
> +
> +	if (rc)
> +		goto err;
> +
> +	*data_size = var.datalen + sizeof(var.policy);
> +
> +	// We can be called with data = NULL to just get the object size.
> +	if (data) {
> +		memcpy(data, &var.policy, sizeof(var.policy));
> +		memcpy(data + sizeof(var.policy), var.data, var.datalen);
> +	}

There's a lot of allocation and copying going on. The secvar-sysfs.c
data_read() has kzalloc'ed data, but only after already doing the hcall
to get the size. Then plpks_read_os_var() does an allocation for the
hcall and then another allocation of the exact data size. Then data_read()
does another copy into the sysfs supplied buffer.

So to read a single variable we do the hcall twice, and allocate/copy
the content of the variable 4 times?

 - Hypervisor into "output" in plpks_read_var().
 - "output" into "var->data" in plpks_read_var().
 - "var.data" into "data" in plpks_get_variable().
 - "data" into "buf" in data_read().

As long as maxobjsize is < PAGE_SIZE I think we could do the hcall
directly into "buf". Maybe we want to avoid writing into "buf" directly
in case the hcall fails or something, but the other 3 copies seem
unnecessary.

> +	kfree(var.data);
> +err:
> +	kfree(ucs2_name);
> +	return rc;
> +}
> +
> +static int plpks_set_variable(const char *key, uint64_t key_len,
> +			      u8 *data, uint64_t data_size)
> +{
> +	struct plpks_var var = {0};
> +	u16 ucs2_namelen;
> +	u8 *ucs2_name;
> +	int rc = 0;
> +	u64 flags;
> +
> +	// Secure variables need to be prefixed with 8 bytes of flags.
> +	// We only want to perform the write if we have at least one byte of data.
> +	if (data_size <= sizeof(flags))
> +		return -EINVAL;
> +
> +	ucs2_namelen = get_ucs2name(key, &ucs2_name);
> +	if (!ucs2_namelen)
> +		return -ENOMEM;
> +
> +	memcpy(&flags, data, sizeof(flags));
> +
> +	var.datalen = data_size - sizeof(flags);
> +	var.data = kzalloc(var.datalen, GFP_KERNEL);
> +	if (!var.data) {
> +		rc = -ENOMEM;
> +		goto err;
> +	}
> +
> +	memcpy(var.data, data + sizeof(flags), var.datalen);
> +
> +	var.name = ucs2_name;
> +	var.namelen = ucs2_namelen;
> +	var.os = PLPKS_VAR_LINUX;
> +	var.policy = get_policy(key);
> +
> +	rc = plpks_signed_update_var(var, flags);
> +
> +	kfree(var.data);
> +err:
> +	kfree(ucs2_name);
> +	return rc;
> +}
> +
> +/*
> + * get_next() in the secvar API is designed for the OPAL API.
> + * If *key is 0, it returns the first variable in the keystore.
> + * Otherwise, you pass the name of a key and it returns next in line.
> + *
> + * We're going to cheat here - since we have fixed keys and don't care about
> + * key_len, we can just use it as an index.
> + */

That's kinda gross. Just change the ops API to do what we need? Either
add a separate get-by-index routine or change the existing one and
update the only other implementation.

> +static int plpks_get_next_variable(const char *key, uint64_t *key_len, uint64_t keybufsize)
> +{
> +	if (!key || !key_len)
> +		return -EINVAL;
> +
> +	if (*key_len >= PLPKS_SECVAR_COUNT)
> +		return -ENOENT;
> +
> +	if (strscpy((char *)key, var_names[(*key_len)++], keybufsize) < 0)
> +		return -E2BIG;
> +
> +	return 0;
> +}
> +
> +// PLPKS dynamic secure boot doesn't give us a format string in the same way OPAL does.
> +// Instead, report the format using the SB_VERSION variable in the keystore.
> +static ssize_t plpks_secvar_format(char *buf)
> +{
> +	struct plpks_var var = {0};
> +	ssize_t ret;
> +
> +	var.component = NULL;
> +	// Only the signed variables have ucs2-encoded names, this one doesn't
> +	var.name = "SB_VERSION";

Is that specified somewhere?

> +	var.namelen = 10;
> +	var.datalen = 0;
> +	var.data = NULL;
> +
> +	// Unlike the other vars, SB_VERSION is owned by firmware instead of the OS
> +	ret = plpks_read_fw_var(&var);
> +	if (ret) {
> +		if (ret == -ENOENT)
> +			return sysfs_emit(buf, "ibm,plpks-sb-unknown\n");
> +
> +		pr_err("Error %ld reading SB_VERSION from firmware\n", ret);
> +		return ret;

I'm not sure you should pass that raw error back to sysfs. Some of the
values could be confusing, eg. if you return -EINVAL it looks like a
parameter to the read() syscall was invalid. Might be better to just
return -EIO.

> +	}
> +
> +	// Hypervisor defines SB_VERSION as a "1 byte unsigned integer value"
> +	ret = sysfs_emit(buf, "ibm,plpks-sb-%hhu\n", var.data[0]);

The rest of the name string is just made up by us?

> +	kfree(var.data);
> +	return ret;
> +}
> +
> +static int plpks_max_size(uint64_t *max_size)
> +{
> +	// The max object size reported by the hypervisor is accurate for the
> +	// object itself, but we use the first 8 bytes of data on write as the
> +	// signed update flags, so the max size a user can write is larger.
> +	*max_size = (uint64_t)plpks_get_maxobjectsize() + 8;
> +
> +	return 0;
> +}
> +
> +
> +static const struct secvar_operations plpks_secvar_ops = {
> +	.get = plpks_get_variable,
> +	.get_next = plpks_get_next_variable,
> +	.set = plpks_set_variable,
> +	.format = plpks_secvar_format,
> +	.max_size = plpks_max_size,
> +};
> +
> +static int plpks_secvar_init(void)
> +{
> +	if (!plpks_is_available())
> +		return -ENODEV;
> +
> +	set_secvar_ops(&plpks_secvar_ops);
> +	set_secvar_config_attrs(config_attrs);
> +	return 0;
> +}
> +device_initcall(plpks_secvar_init);

That must be a machine_device_initcall(pseries, ...), otherwise we will
blow up doing a hcall on powernv in plpks_is_available().

cheers

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

* Re: [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
@ 2023-01-06 10:49     ` Michael Ellerman
  0 siblings, 0 replies; 54+ messages in thread
From: Michael Ellerman @ 2023-01-06 10:49 UTC (permalink / raw)
  To: Russell Currey, linuxppc-dev
  Cc: ajd, gregkh, nayna, linux-kernel, zohar, gcwilson

Russell Currey <ruscur@russell.cc> writes:
> The pseries platform can support dynamic secure boot (i.e. secure boot
> using user-defined keys) using variables contained with the PowerVM LPAR
> Platform KeyStore (PLPKS).  Using the powerpc secvar API, expose the
> relevant variables for pseries dynamic secure boot through the existing
> secvar filesystem layout.
>
> The relevant variables for dynamic secure boot are signed in the
> keystore, and can only be modified using the H_PKS_SIGNED_UPDATE hcall.
> Object labels in the keystore are encoded using ucs2 format.  With our
> fixed variable names we don't have to care about encoding outside of the
> necessary byte padding.
>
> When a user writes to a variable, the first 8 bytes of data must contain
> the signed update flags as defined by the hypervisor.
>
> When a user reads a variable, the first 4 bytes of data contain the
> policies defined for the object.
>
> Limitations exist due to the underlying implementation of sysfs binary
> attributes, as is the case for the OPAL secvar implementation -
> partial writes are unsupported and writes cannot be larger than PAGE_SIZE.
>
> Co-developed-by: Nayna Jain <nayna@linux.ibm.com>
> Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
> Co-developed-by: Andrew Donnellan <ajd@linux.ibm.com>
> Signed-off-by: Andrew Donnellan <ajd@linux.ibm.com>
> Signed-off-by: Russell Currey <ruscur@russell.cc>
> ---
> v2: Remove unnecessary config vars from sysfs and document the others,
>     thanks to review from Greg.  If we end up needing to expose more, we
>     can add them later and update the docs.
>
>     Use sysfs_emit() instead of sprintf(), thanks to Greg.
>
>     Change the size of the sysfs binary attributes to include the 8-byte
>     flags header, preventing truncation of large writes.
>
>  Documentation/ABI/testing/sysfs-secvar        |  67 ++++-
>  arch/powerpc/platforms/pseries/Kconfig        |  13 +
>  arch/powerpc/platforms/pseries/Makefile       |   4 +-
>  arch/powerpc/platforms/pseries/plpks-secvar.c | 245 ++++++++++++++++++
>  4 files changed, 326 insertions(+), 3 deletions(-)
>  create mode 100644 arch/powerpc/platforms/pseries/plpks-secvar.c
>
> diff --git a/Documentation/ABI/testing/sysfs-secvar b/Documentation/ABI/testing/sysfs-secvar
> index feebb8c57294..466a8cb92b92 100644
> --- a/Documentation/ABI/testing/sysfs-secvar
> +++ b/Documentation/ABI/testing/sysfs-secvar
> @@ -34,7 +34,7 @@ Description:	An integer representation of the size of the content of the
>  
>  What:		/sys/firmware/secvar/vars/<variable_name>/data
>  Date:		August 2019
> -Contact:	Nayna Jain h<nayna@linux.ibm.com>
> +Contact:	Nayna Jain <nayna@linux.ibm.com>
>  Description:	A read-only file containing the value of the variable. The size
>  		of the file represents the maximum size of the variable data.
>  
> @@ -44,3 +44,68 @@ Contact:	Nayna Jain <nayna@linux.ibm.com>
>  Description:	A write-only file that is used to submit the new value for the
>  		variable. The size of the file represents the maximum size of
>  		the variable data that can be written.
> +
> +What:		/sys/firmware/secvar/config
> +Date:		December 2022
> +Contact:	Nayna Jain <nayna@linux.ibm.com>
> +Description:	This optional directory contains read-only config attributes as
> +		defined by the secure variable implementation.  All data is in
> +		ASCII format. The directory is only created if the backing
> +		implementation provides variables to populate it, which at
> +		present is only PLPKS on the pseries platform.

I think it's OK to mention that currently this only exists for PLPKS ...

> +What:		/sys/firmware/secvar/config/version
> +Date:		December 2022
> +Contact:	Nayna Jain <nayna@linux.ibm.com>
> +Description:	RO file, only present if the secvar implementation is PLPKS.

... but I don't think we want to specify that files are only present for PLPKS. 

Because if another backend wanted to create them in future, that would
technically be an ABI change.

> +		Contains the config version as reported by the hypervisor in
> +		ASCII decimal format.
> +
> +What:		/sys/firmware/secvar/config/max_object_size
> +Date:		December 2022
> +Contact:	Nayna Jain <nayna@linux.ibm.com>
> +Description:	RO file, only present if the secvar implementation is PLPKS.
> +
> +		Contains the maximum allowed size of objects in the keystore
> +		in bytes, represented in ASCII decimal format.
> +
> +		This is not necessarily the same as the max size that can be
> +		written to an update file as writes can contain more than
> +		object data, you should use the size of the update file for
> +		that purpose.
> +
> +What:		/sys/firmware/secvar/config/total_size
> +Date:		December 2022
> +Contact:	Nayna Jain <nayna@linux.ibm.com>
> +Description:	RO file, only present if the secvar implementation is PLPKS.
> +
> +		Contains the total size of the PLPKS in bytes, represented in
> +		ASCII decimal format.

Similarly here I think the description should be written in a way that
is agnostic about the backend. So eg. "Contains the total size of the
key store in bytes".


> +What:		/sys/firmware/secvar/config/used_space
> +Date:		December 2022
> +Contact:	Nayna Jain <nayna@linux.ibm.com>
> +Description:	RO file, only present if the secvar implementation is PLPKS.
> +
> +		Contains the current space consumed of the PLPKS in bytes,
> +		represented in ASCII decimal format.
> +
> +What:		/sys/firmware/secvar/config/supported_policies
> +Date:		December 2022
> +Contact:	Nayna Jain <nayna@linux.ibm.com>
> +Description:	RO file, only present if the secvar implementation is PLPKS.
> +
> +		Contains a bitmask of supported policy flags by the hypervisor,
> +		represented as an 8 byte hexadecimal ASCII string.  Consult the
> +		hypervisor documentation for what these flags are.
> +
> +What:		/sys/firmware/secvar/config/signed_update_algorithms
> +Date:		December 2022
> +Contact:	Nayna Jain <nayna@linux.ibm.com>
> +Description:	RO file, only present if the secvar implementation is PLPKS.
> +
> +		Contains a bitmask of flags indicating which algorithms the
> +		hypervisor supports objects to be signed with when modifying
> +		secvars, represented as a 16 byte hexadecimal ASCII string.
> +		Consult the hypervisor documentation for what these flags mean.
 
Can this at least say "as defined in PAPR version X section Y"?

> diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
> index a3b4d99567cb..94e08c405d50 100644
> --- a/arch/powerpc/platforms/pseries/Kconfig
> +++ b/arch/powerpc/platforms/pseries/Kconfig
> @@ -162,6 +162,19 @@ config PSERIES_PLPKS
>  
>  	  If unsure, select N.
>  
> +config PSERIES_PLPKS_SECVAR
> +	depends on PSERIES_PLPKS
> +	depends on PPC_SECURE_BOOT
> +	bool "Support for the PLPKS secvar interface"
> +	help
> +	  PowerVM can support dynamic secure boot with user-defined keys
> +	  through the PLPKS. Keystore objects used in dynamic secure boot
> +	  can be exposed to the kernel and userspace through the powerpc
> +	  secvar infrastructure. Select this to enable the PLPKS backend
> +	  for secvars for use in pseries dynamic secure boot.
> +
> +	  If unsure, select N.

I don't think we need that config option at all, or if we do it should
not be user selectable and just enabled automatically by PSERIES_PLPKS.

> diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
> index 92310202bdd7..807756991f9d 100644
> --- a/arch/powerpc/platforms/pseries/Makefile
> +++ b/arch/powerpc/platforms/pseries/Makefile
> @@ -27,8 +27,8 @@ obj-$(CONFIG_PAPR_SCM)		+= papr_scm.o
>  obj-$(CONFIG_PPC_SPLPAR)	+= vphn.o
>  obj-$(CONFIG_PPC_SVM)		+= svm.o
>  obj-$(CONFIG_FA_DUMP)		+= rtas-fadump.o
> -obj-$(CONFIG_PSERIES_PLPKS) += plpks.o
> -
> +obj-$(CONFIG_PSERIES_PLPKS)	+= plpks.o
> +obj-$(CONFIG_PSERIES_PLPKS_SECVAR)	+= plpks-secvar.o

I'm not convinced the secvar parts need to be in a separate C file.

If it was all in plpks.c we could avoid all/most of plpks.h and a bunch
of accessors and so on.

But I don't feel that strongly about it if you think it's better separate.

> diff --git a/arch/powerpc/platforms/pseries/plpks-secvar.c b/arch/powerpc/platforms/pseries/plpks-secvar.c
> new file mode 100644
> index 000000000000..8298f039bef4
> --- /dev/null
> +++ b/arch/powerpc/platforms/pseries/plpks-secvar.c
> @@ -0,0 +1,245 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Secure variable implementation using the PowerVM LPAR Platform KeyStore (PLPKS)
> + *
> + * Copyright 2022, IBM Corporation
> + * Authors: Russell Currey
> + *          Andrew Donnellan
> + *          Nayna Jain
> + */
> +
> +#define pr_fmt(fmt) "secvar: "fmt
> +
> +#include <linux/printk.h>
> +#include <linux/init.h>
> +#include <linux/types.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +#include <linux/kobject.h>
> +#include <asm/secvar.h>
> +#include "plpks.h"
> +
> +// Config attributes for sysfs
> +#define PLPKS_CONFIG_ATTR(name, fmt, func)			\
> +	static ssize_t name##_show(struct kobject *kobj,	\
> +				   struct kobj_attribute *attr,	\
> +				   char *buf)			\
> +	{							\
> +		return sysfs_emit(buf, fmt, func());		\
> +	}							\
> +	static struct kobj_attribute attr_##name = __ATTR_RO(name)
> +
> +PLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version);
> +PLPKS_CONFIG_ATTR(max_object_size, "%u\n", plpks_get_maxobjectsize);
> +PLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize);
> +PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);
> +PLPKS_CONFIG_ATTR(supported_policies, "%08x\n", plpks_get_supportedpolicies);
> +PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n", plpks_get_signedupdatealgorithms);

For those last two I wonder if we should be decoding the integer values
into a comma separated list of named flags?

Just blatting out the integer values is a bit gross. It's not helpful
for shell scripts, and a consumer written in C has to strtoull() the
value back into an integer before it can decode it.

> +static const struct attribute *config_attrs[] = {
> +	&attr_version.attr,
> +	&attr_max_object_size.attr,
> +	&attr_total_size.attr,
> +	&attr_used_space.attr,
> +	&attr_supported_policies.attr,
> +	&attr_signed_update_algorithms.attr,
> +	NULL,
> +};
> +
> +static u16 get_ucs2name(const char *name, uint8_t **ucs2_name)
> +{
> +	int namelen = strlen(name) * 2;
> +	*ucs2_name = kzalloc(namelen, GFP_KERNEL);
> +
> +	if (!*ucs2_name)
> +		return 0;
> +
> +	for (int i = 0; name[i]; i++) {
> +		(*ucs2_name)[i * 2] = name[i];
> +		(*ucs2_name)[i * 2 + 1] = '\0';
> +	}
> +
> +	return namelen;
> +}

There are some ucs2 routines in lib/ucs2_string.c, can we use any of
them?

> +static u32 get_policy(const char *name)
> +{
> +	if ((strcmp(name, "db") == 0) ||
> +	    (strcmp(name, "dbx") == 0) ||
> +	    (strcmp(name, "grubdb") == 0) ||
> +	    (strcmp(name, "sbat") == 0))
> +		return (WORLDREADABLE | SIGNEDUPDATE);
> +	else
> +		return SIGNEDUPDATE;
> +}
> +
> +#define PLPKS_SECVAR_COUNT 8

I don't think we need that. Just declare the array as unsized and then
use ARRAY_SIZE(var_names) in plpks_get_next_variable().

> +static char *var_names[PLPKS_SECVAR_COUNT] = {
> +	"PK",
> +	"KEK",
> +	"db",
> +	"dbx",
> +	"grubdb",
> +	"sbat",
> +	"moduledb",
> +	"trustedcadb",
> +};
> +
> +static int plpks_get_variable(const char *key, uint64_t key_len,
> +			      u8 *data, uint64_t *data_size)
> +{
> +	struct plpks_var var = {0};
> +	u16 ucs2_namelen;
> +	u8 *ucs2_name;
> +	int rc = 0;
> +
> +	ucs2_namelen = get_ucs2name(key, &ucs2_name);
> +	if (!ucs2_namelen)
> +		return -ENOMEM;
> +
> +	var.name = ucs2_name;
> +	var.namelen = ucs2_namelen;
> +	var.os = PLPKS_VAR_LINUX;
> +	rc = plpks_read_os_var(&var);
> +
> +	if (rc)
> +		goto err;
> +
> +	*data_size = var.datalen + sizeof(var.policy);
> +
> +	// We can be called with data = NULL to just get the object size.
> +	if (data) {
> +		memcpy(data, &var.policy, sizeof(var.policy));
> +		memcpy(data + sizeof(var.policy), var.data, var.datalen);
> +	}

There's a lot of allocation and copying going on. The secvar-sysfs.c
data_read() has kzalloc'ed data, but only after already doing the hcall
to get the size. Then plpks_read_os_var() does an allocation for the
hcall and then another allocation of the exact data size. Then data_read()
does another copy into the sysfs supplied buffer.

So to read a single variable we do the hcall twice, and allocate/copy
the content of the variable 4 times?

 - Hypervisor into "output" in plpks_read_var().
 - "output" into "var->data" in plpks_read_var().
 - "var.data" into "data" in plpks_get_variable().
 - "data" into "buf" in data_read().

As long as maxobjsize is < PAGE_SIZE I think we could do the hcall
directly into "buf". Maybe we want to avoid writing into "buf" directly
in case the hcall fails or something, but the other 3 copies seem
unnecessary.

> +	kfree(var.data);
> +err:
> +	kfree(ucs2_name);
> +	return rc;
> +}
> +
> +static int plpks_set_variable(const char *key, uint64_t key_len,
> +			      u8 *data, uint64_t data_size)
> +{
> +	struct plpks_var var = {0};
> +	u16 ucs2_namelen;
> +	u8 *ucs2_name;
> +	int rc = 0;
> +	u64 flags;
> +
> +	// Secure variables need to be prefixed with 8 bytes of flags.
> +	// We only want to perform the write if we have at least one byte of data.
> +	if (data_size <= sizeof(flags))
> +		return -EINVAL;
> +
> +	ucs2_namelen = get_ucs2name(key, &ucs2_name);
> +	if (!ucs2_namelen)
> +		return -ENOMEM;
> +
> +	memcpy(&flags, data, sizeof(flags));
> +
> +	var.datalen = data_size - sizeof(flags);
> +	var.data = kzalloc(var.datalen, GFP_KERNEL);
> +	if (!var.data) {
> +		rc = -ENOMEM;
> +		goto err;
> +	}
> +
> +	memcpy(var.data, data + sizeof(flags), var.datalen);
> +
> +	var.name = ucs2_name;
> +	var.namelen = ucs2_namelen;
> +	var.os = PLPKS_VAR_LINUX;
> +	var.policy = get_policy(key);
> +
> +	rc = plpks_signed_update_var(var, flags);
> +
> +	kfree(var.data);
> +err:
> +	kfree(ucs2_name);
> +	return rc;
> +}
> +
> +/*
> + * get_next() in the secvar API is designed for the OPAL API.
> + * If *key is 0, it returns the first variable in the keystore.
> + * Otherwise, you pass the name of a key and it returns next in line.
> + *
> + * We're going to cheat here - since we have fixed keys and don't care about
> + * key_len, we can just use it as an index.
> + */

That's kinda gross. Just change the ops API to do what we need? Either
add a separate get-by-index routine or change the existing one and
update the only other implementation.

> +static int plpks_get_next_variable(const char *key, uint64_t *key_len, uint64_t keybufsize)
> +{
> +	if (!key || !key_len)
> +		return -EINVAL;
> +
> +	if (*key_len >= PLPKS_SECVAR_COUNT)
> +		return -ENOENT;
> +
> +	if (strscpy((char *)key, var_names[(*key_len)++], keybufsize) < 0)
> +		return -E2BIG;
> +
> +	return 0;
> +}
> +
> +// PLPKS dynamic secure boot doesn't give us a format string in the same way OPAL does.
> +// Instead, report the format using the SB_VERSION variable in the keystore.
> +static ssize_t plpks_secvar_format(char *buf)
> +{
> +	struct plpks_var var = {0};
> +	ssize_t ret;
> +
> +	var.component = NULL;
> +	// Only the signed variables have ucs2-encoded names, this one doesn't
> +	var.name = "SB_VERSION";

Is that specified somewhere?

> +	var.namelen = 10;
> +	var.datalen = 0;
> +	var.data = NULL;
> +
> +	// Unlike the other vars, SB_VERSION is owned by firmware instead of the OS
> +	ret = plpks_read_fw_var(&var);
> +	if (ret) {
> +		if (ret == -ENOENT)
> +			return sysfs_emit(buf, "ibm,plpks-sb-unknown\n");
> +
> +		pr_err("Error %ld reading SB_VERSION from firmware\n", ret);
> +		return ret;

I'm not sure you should pass that raw error back to sysfs. Some of the
values could be confusing, eg. if you return -EINVAL it looks like a
parameter to the read() syscall was invalid. Might be better to just
return -EIO.

> +	}
> +
> +	// Hypervisor defines SB_VERSION as a "1 byte unsigned integer value"
> +	ret = sysfs_emit(buf, "ibm,plpks-sb-%hhu\n", var.data[0]);

The rest of the name string is just made up by us?

> +	kfree(var.data);
> +	return ret;
> +}
> +
> +static int plpks_max_size(uint64_t *max_size)
> +{
> +	// The max object size reported by the hypervisor is accurate for the
> +	// object itself, but we use the first 8 bytes of data on write as the
> +	// signed update flags, so the max size a user can write is larger.
> +	*max_size = (uint64_t)plpks_get_maxobjectsize() + 8;
> +
> +	return 0;
> +}
> +
> +
> +static const struct secvar_operations plpks_secvar_ops = {
> +	.get = plpks_get_variable,
> +	.get_next = plpks_get_next_variable,
> +	.set = plpks_set_variable,
> +	.format = plpks_secvar_format,
> +	.max_size = plpks_max_size,
> +};
> +
> +static int plpks_secvar_init(void)
> +{
> +	if (!plpks_is_available())
> +		return -ENODEV;
> +
> +	set_secvar_ops(&plpks_secvar_ops);
> +	set_secvar_config_attrs(config_attrs);
> +	return 0;
> +}
> +device_initcall(plpks_secvar_init);

That must be a machine_device_initcall(pseries, ...), otherwise we will
blow up doing a hcall on powernv in plpks_is_available().

cheers

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

* Re: [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
  2023-01-06 10:49     ` Michael Ellerman
@ 2023-01-09  3:33       ` Andrew Donnellan
  -1 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-09  3:33 UTC (permalink / raw)
  To: Michael Ellerman, Russell Currey, linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, zohar

On Fri, 2023-01-06 at 21:49 +1100, Michael Ellerman wrote:
> > +What:          /sys/firmware/secvar/config/supported_policies
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is PLPKS.
> > +
> > +               Contains a bitmask of supported policy flags by the
> > hypervisor,
> > +               represented as an 8 byte hexadecimal ASCII string. 
> > Consult the
> > +               hypervisor documentation for what these flags are.
> > +
> > +What:          /sys/firmware/secvar/config/signed_update_algorithm
> > s
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is PLPKS.
> > +
> > +               Contains a bitmask of flags indicating which
> > algorithms the
> > +               hypervisor supports objects to be signed with when
> > modifying
> > +               secvars, represented as a 16 byte hexadecimal ASCII
> > string.
> > +               Consult the hypervisor documentation for what these
> > flags mean.
>  
> Can this at least say "as defined in PAPR version X section Y"?

We don't currently have a PAPR reference for this - that will come
eventually.

> 
> > diff --git a/arch/powerpc/platforms/pseries/Kconfig
> > b/arch/powerpc/platforms/pseries/Kconfig
> > index a3b4d99567cb..94e08c405d50 100644
> > --- a/arch/powerpc/platforms/pseries/Kconfig
> > +++ b/arch/powerpc/platforms/pseries/Kconfig
> > @@ -162,6 +162,19 @@ config PSERIES_PLPKS
> >  
> >           If unsure, select N.
> >  
> > +config PSERIES_PLPKS_SECVAR
> > +       depends on PSERIES_PLPKS
> > +       depends on PPC_SECURE_BOOT
> > +       bool "Support for the PLPKS secvar interface"
> > +       help
> > +         PowerVM can support dynamic secure boot with user-defined
> > keys
> > +         through the PLPKS. Keystore objects used in dynamic
> > secure boot
> > +         can be exposed to the kernel and userspace through the
> > powerpc
> > +         secvar infrastructure. Select this to enable the PLPKS
> > backend
> > +         for secvars for use in pseries dynamic secure boot.
> > +
> > +         If unsure, select N.
> 
> I don't think we need that config option at all, or if we do it
> should
> not be user selectable and just enabled automatically by
> PSERIES_PLPKS.
> 
> > diff --git a/arch/powerpc/platforms/pseries/Makefile
> > b/arch/powerpc/platforms/pseries/Makefile
> > index 92310202bdd7..807756991f9d 100644
> > --- a/arch/powerpc/platforms/pseries/Makefile
> > +++ b/arch/powerpc/platforms/pseries/Makefile
> > @@ -27,8 +27,8 @@ obj-$(CONFIG_PAPR_SCM)                +=
> > papr_scm.o
> >  obj-$(CONFIG_PPC_SPLPAR)       += vphn.o
> >  obj-$(CONFIG_PPC_SVM)          += svm.o
> >  obj-$(CONFIG_FA_DUMP)          += rtas-fadump.o
> > -obj-$(CONFIG_PSERIES_PLPKS) += plpks.o
> > -
> > +obj-$(CONFIG_PSERIES_PLPKS)    += plpks.o
> > +obj-$(CONFIG_PSERIES_PLPKS_SECVAR)     += plpks-secvar.o
> 
> I'm not convinced the secvar parts need to be in a separate C file.
> 
> If it was all in plpks.c we could avoid all/most of plpks.h and a
> bunch
> of accessors and so on.
> 
> But I don't feel that strongly about it if you think it's better
> separate.

I feel pretty strongly that we should keep it separate. We're going to
need a header file anyway because in future patches to come shortly we
need to wire plpks up with the integrity subsystem to load keys into
kernel keyrings.

> 
> > diff --git a/arch/powerpc/platforms/pseries/plpks-secvar.c
> > b/arch/powerpc/platforms/pseries/plpks-secvar.c
> > new file mode 100644
> > index 000000000000..8298f039bef4
> > --- /dev/null
> > +++ b/arch/powerpc/platforms/pseries/plpks-secvar.c
> > @@ -0,0 +1,245 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Secure variable implementation using the PowerVM LPAR Platform
> > KeyStore (PLPKS)
> > + *
> > + * Copyright 2022, IBM Corporation
> > + * Authors: Russell Currey
> > + *          Andrew Donnellan
> > + *          Nayna Jain
> > + */
> > +
> > +#define pr_fmt(fmt) "secvar: "fmt
> > +
> > +#include <linux/printk.h>
> > +#include <linux/init.h>
> > +#include <linux/types.h>
> > +#include <linux/slab.h>
> > +#include <linux/string.h>
> > +#include <linux/kobject.h>
> > +#include <asm/secvar.h>
> > +#include "plpks.h"
> > +
> > +// Config attributes for sysfs
> > +#define PLPKS_CONFIG_ATTR(name, fmt, func)                     \
> > +       static ssize_t name##_show(struct kobject *kobj,        \
> > +                                  struct kobj_attribute *attr, \
> > +                                  char *buf)                   \
> > +       {                                                       \
> > +               return sysfs_emit(buf, fmt, func());            \
> > +       }                                                       \
> > +       static struct kobj_attribute attr_##name = __ATTR_RO(name)
> > +
> > +PLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version);
> > +PLPKS_CONFIG_ATTR(max_object_size, "%u\n",
> > plpks_get_maxobjectsize);
> > +PLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize);les
> > +PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);
> > +PLPKS_CONFIG_ATTR(supported_policies, "%08x\n",
> > plpks_get_supportedpolicies);
> > +PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n",
> > plpks_get_signedupdatealgorithms);
> 
> For those last two I wonder if we should be decoding the integer
> values
> into a comma separated list of named flags?
> 
> Just blatting out the integer values is a bit gross. It's not helpful
> for shell scripts, and a consumer written in C has to strtoull() the
> value back into an integer before it can decode it.

How would you propose dealing with currently-reserved bits that might
be used by a future version of the hypervisor?

> 
> > +static const struct attribute *config_attrs[] = {
> > +       &attr_version.attr,
> > +       &attr_max_object_size.attr,
> > +       &attr_total_size.attr,
> > +       &attr_used_space.attr,
> > +       &attr_supported_policies.attr,
> > +       &attr_signed_update_algorithms.attr,
> > +       NULL,
> > +};
> > +
> > +static u16 get_ucs2name(const char *name, uint8_t **ucs2_name)
> > +{
> > +       int namelen = strlen(name) * 2;
> > +       *ucs2_name = kzalloc(namelen, GFP_KERNEL);
> > +
> > +       if (!*ucs2_name)
> > +               return 0;
> > +
> > +       for (int i = 0; name[i]; i++) {
> > +               (*ucs2_name)[i * 2] = name[i];
> > +               (*ucs2_name)[i * 2 + 1] = '\0';
> > +       }
> > +
> > +       return namelen;
> > +}
> 
> There are some ucs2 routines in lib/ucs2_string.c, can we use any of
> them?

ucs2_string.c doesn't do this.


-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
@ 2023-01-09  3:33       ` Andrew Donnellan
  0 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-09  3:33 UTC (permalink / raw)
  To: Michael Ellerman, Russell Currey, linuxppc-dev
  Cc: nayna, gregkh, gcwilson, linux-kernel, zohar

On Fri, 2023-01-06 at 21:49 +1100, Michael Ellerman wrote:
> > +What:          /sys/firmware/secvar/config/supported_policies
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is PLPKS.
> > +
> > +               Contains a bitmask of supported policy flags by the
> > hypervisor,
> > +               represented as an 8 byte hexadecimal ASCII string. 
> > Consult the
> > +               hypervisor documentation for what these flags are.
> > +
> > +What:          /sys/firmware/secvar/config/signed_update_algorithm
> > s
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is PLPKS.
> > +
> > +               Contains a bitmask of flags indicating which
> > algorithms the
> > +               hypervisor supports objects to be signed with when
> > modifying
> > +               secvars, represented as a 16 byte hexadecimal ASCII
> > string.
> > +               Consult the hypervisor documentation for what these
> > flags mean.
>  
> Can this at least say "as defined in PAPR version X section Y"?

We don't currently have a PAPR reference for this - that will come
eventually.

> 
> > diff --git a/arch/powerpc/platforms/pseries/Kconfig
> > b/arch/powerpc/platforms/pseries/Kconfig
> > index a3b4d99567cb..94e08c405d50 100644
> > --- a/arch/powerpc/platforms/pseries/Kconfig
> > +++ b/arch/powerpc/platforms/pseries/Kconfig
> > @@ -162,6 +162,19 @@ config PSERIES_PLPKS
> >  
> >           If unsure, select N.
> >  
> > +config PSERIES_PLPKS_SECVAR
> > +       depends on PSERIES_PLPKS
> > +       depends on PPC_SECURE_BOOT
> > +       bool "Support for the PLPKS secvar interface"
> > +       help
> > +         PowerVM can support dynamic secure boot with user-defined
> > keys
> > +         through the PLPKS. Keystore objects used in dynamic
> > secure boot
> > +         can be exposed to the kernel and userspace through the
> > powerpc
> > +         secvar infrastructure. Select this to enable the PLPKS
> > backend
> > +         for secvars for use in pseries dynamic secure boot.
> > +
> > +         If unsure, select N.
> 
> I don't think we need that config option at all, or if we do it
> should
> not be user selectable and just enabled automatically by
> PSERIES_PLPKS.
> 
> > diff --git a/arch/powerpc/platforms/pseries/Makefile
> > b/arch/powerpc/platforms/pseries/Makefile
> > index 92310202bdd7..807756991f9d 100644
> > --- a/arch/powerpc/platforms/pseries/Makefile
> > +++ b/arch/powerpc/platforms/pseries/Makefile
> > @@ -27,8 +27,8 @@ obj-$(CONFIG_PAPR_SCM)                +=
> > papr_scm.o
> >  obj-$(CONFIG_PPC_SPLPAR)       += vphn.o
> >  obj-$(CONFIG_PPC_SVM)          += svm.o
> >  obj-$(CONFIG_FA_DUMP)          += rtas-fadump.o
> > -obj-$(CONFIG_PSERIES_PLPKS) += plpks.o
> > -
> > +obj-$(CONFIG_PSERIES_PLPKS)    += plpks.o
> > +obj-$(CONFIG_PSERIES_PLPKS_SECVAR)     += plpks-secvar.o
> 
> I'm not convinced the secvar parts need to be in a separate C file.
> 
> If it was all in plpks.c we could avoid all/most of plpks.h and a
> bunch
> of accessors and so on.
> 
> But I don't feel that strongly about it if you think it's better
> separate.

I feel pretty strongly that we should keep it separate. We're going to
need a header file anyway because in future patches to come shortly we
need to wire plpks up with the integrity subsystem to load keys into
kernel keyrings.

> 
> > diff --git a/arch/powerpc/platforms/pseries/plpks-secvar.c
> > b/arch/powerpc/platforms/pseries/plpks-secvar.c
> > new file mode 100644
> > index 000000000000..8298f039bef4
> > --- /dev/null
> > +++ b/arch/powerpc/platforms/pseries/plpks-secvar.c
> > @@ -0,0 +1,245 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Secure variable implementation using the PowerVM LPAR Platform
> > KeyStore (PLPKS)
> > + *
> > + * Copyright 2022, IBM Corporation
> > + * Authors: Russell Currey
> > + *          Andrew Donnellan
> > + *          Nayna Jain
> > + */
> > +
> > +#define pr_fmt(fmt) "secvar: "fmt
> > +
> > +#include <linux/printk.h>
> > +#include <linux/init.h>
> > +#include <linux/types.h>
> > +#include <linux/slab.h>
> > +#include <linux/string.h>
> > +#include <linux/kobject.h>
> > +#include <asm/secvar.h>
> > +#include "plpks.h"
> > +
> > +// Config attributes for sysfs
> > +#define PLPKS_CONFIG_ATTR(name, fmt, func)                     \
> > +       static ssize_t name##_show(struct kobject *kobj,        \
> > +                                  struct kobj_attribute *attr, \
> > +                                  char *buf)                   \
> > +       {                                                       \
> > +               return sysfs_emit(buf, fmt, func());            \
> > +       }                                                       \
> > +       static struct kobj_attribute attr_##name = __ATTR_RO(name)
> > +
> > +PLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version);
> > +PLPKS_CONFIG_ATTR(max_object_size, "%u\n",
> > plpks_get_maxobjectsize);
> > +PLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize);les
> > +PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);
> > +PLPKS_CONFIG_ATTR(supported_policies, "%08x\n",
> > plpks_get_supportedpolicies);
> > +PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n",
> > plpks_get_signedupdatealgorithms);
> 
> For those last two I wonder if we should be decoding the integer
> values
> into a comma separated list of named flags?
> 
> Just blatting out the integer values is a bit gross. It's not helpful
> for shell scripts, and a consumer written in C has to strtoull() the
> value back into an integer before it can decode it.

How would you propose dealing with currently-reserved bits that might
be used by a future version of the hypervisor?

> 
> > +static const struct attribute *config_attrs[] = {
> > +       &attr_version.attr,
> > +       &attr_max_object_size.attr,
> > +       &attr_total_size.attr,
> > +       &attr_used_space.attr,
> > +       &attr_supported_policies.attr,
> > +       &attr_signed_update_algorithms.attr,
> > +       NULL,
> > +};
> > +
> > +static u16 get_ucs2name(const char *name, uint8_t **ucs2_name)
> > +{
> > +       int namelen = strlen(name) * 2;
> > +       *ucs2_name = kzalloc(namelen, GFP_KERNEL);
> > +
> > +       if (!*ucs2_name)
> > +               return 0;
> > +
> > +       for (int i = 0; name[i]; i++) {
> > +               (*ucs2_name)[i * 2] = name[i];
> > +               (*ucs2_name)[i * 2 + 1] = '\0';
> > +       }
> > +
> > +       return namelen;
> > +}
> 
> There are some ucs2 routines in lib/ucs2_string.c, can we use any of
> them?

ucs2_string.c doesn't do this.


-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
  2023-01-06 10:49     ` Michael Ellerman
@ 2023-01-09  3:34       ` Russell Currey
  -1 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2023-01-09  3:34 UTC (permalink / raw)
  To: Michael Ellerman, linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, ajd, zohar

On Fri, 2023-01-06 at 21:49 +1100, Michael Ellerman wrote:
> Russell Currey <ruscur@russell.cc> writes:
> > The pseries platform can support dynamic secure boot (i.e. secure
> > boot
> > using user-defined keys) using variables contained with the PowerVM
> > LPAR
> > Platform KeyStore (PLPKS).  Using the powerpc secvar API, expose
> > the
> > relevant variables for pseries dynamic secure boot through the
> > existing
> > secvar filesystem layout.
> > 
> > The relevant variables for dynamic secure boot are signed in the
> > keystore, and can only be modified using the H_PKS_SIGNED_UPDATE
> > hcall.
> > Object labels in the keystore are encoded using ucs2 format.  With
> > our
> > fixed variable names we don't have to care about encoding outside
> > of the
> > necessary byte padding.
> > 
> > When a user writes to a variable, the first 8 bytes of data must
> > contain
> > the signed update flags as defined by the hypervisor.
> > 
> > When a user reads a variable, the first 4 bytes of data contain the
> > policies defined for the object.
> > 
> > Limitations exist due to the underlying implementation of sysfs
> > binary
> > attributes, as is the case for the OPAL secvar implementation -
> > partial writes are unsupported and writes cannot be larger than
> > PAGE_SIZE.
> > 
> > Co-developed-by: Nayna Jain <nayna@linux.ibm.com>
> > Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
> > Co-developed-by: Andrew Donnellan <ajd@linux.ibm.com>
> > Signed-off-by: Andrew Donnellan <ajd@linux.ibm.com>
> > Signed-off-by: Russell Currey <ruscur@russell.cc>
> > ---
> > v2: Remove unnecessary config vars from sysfs and document the
> > others,
> >     thanks to review from Greg.  If we end up needing to expose
> > more, we
> >     can add them later and update the docs.
> > 
> >     Use sysfs_emit() instead of sprintf(), thanks to Greg.
> > 
> >     Change the size of the sysfs binary attributes to include the
> > 8-byte
> >     flags header, preventing truncation of large writes.
> > 
> >  Documentation/ABI/testing/sysfs-secvar        |  67 ++++-
> >  arch/powerpc/platforms/pseries/Kconfig        |  13 +
> >  arch/powerpc/platforms/pseries/Makefile       |   4 +-
> >  arch/powerpc/platforms/pseries/plpks-secvar.c | 245
> > ++++++++++++++++++
> >  4 files changed, 326 insertions(+), 3 deletions(-)
> >  create mode 100644 arch/powerpc/platforms/pseries/plpks-secvar.c
> > 
> > diff --git a/Documentation/ABI/testing/sysfs-secvar
> > b/Documentation/ABI/testing/sysfs-secvar
> > index feebb8c57294..466a8cb92b92 100644
> > --- a/Documentation/ABI/testing/sysfs-secvar
> > +++ b/Documentation/ABI/testing/sysfs-secvar
> > @@ -34,7 +34,7 @@ Description:  An integer representation of the
> > size of the content of the
> >  
> >  What:          /sys/firmware/secvar/vars/<variable_name>/data
> >  Date:          August 2019
> > -Contact:       Nayna Jain h<nayna@linux.ibm.com>
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> >  Description:   A read-only file containing the value of the
> > variable. The size
> >                 of the file represents the maximum size of the
> > variable data.
> >  
> > @@ -44,3 +44,68 @@ Contact:     Nayna Jain <nayna@linux.ibm.com>
> >  Description:   A write-only file that is used to submit the new
> > value for the
> >                 variable. The size of the file represents the
> > maximum size of
> >                 the variable data that can be written.
> > +
> > +What:          /sys/firmware/secvar/config
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   This optional directory contains read-only config
> > attributes as
> > +               defined by the secure variable implementation.  All
> > data is in
> > +               ASCII format. The directory is only created if the
> > backing
> > +               implementation provides variables to populate it,
> > which at
> > +               present is only PLPKS on the pseries platform.
> 
> I think it's OK to mention that currently this only exists for PLPKS
> ...
> 
> > +What:          /sys/firmware/secvar/config/version
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is PLPKS.
> 
> ... but I don't think we want to specify that files are only present
> for PLPKS. 
> 
> Because if another backend wanted to create them in future, that
> would
> technically be an ABI change.

Some are going to be PLPKS-specific, but for generic stuff like this I
can change the description.

> 
> > +               Contains the config version as reported by the
> > hypervisor in
> > +               ASCII decimal format.
> > +
> > +What:          /sys/firmware/secvar/config/max_object_size
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is PLPKS.
> > +
> > +               Contains the maximum allowed size of objects in the
> > keystore
> > +               in bytes, represented in ASCII decimal format.
> > +
> > +               This is not necessarily the same as the max size
> > that can be
> > +               written to an update file as writes can contain
> > more than
> > +               object data, you should use the size of the update
> > file for
> > +               that purpose.
> > +
> > +What:          /sys/firmware/secvar/config/total_size
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is PLPKS.
> > +
> > +               Contains the total size of the PLPKS in bytes,
> > represented in
> > +               ASCII decimal format.
> 
> Similarly here I think the description should be written in a way
> that
> is agnostic about the backend. So eg. "Contains the total size of the
> key store in bytes".
> 
> 
> > +What:          /sys/firmware/secvar/config/used_space
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is PLPKS.
> > +
> > +               Contains the current space consumed of the PLPKS in
> > bytes,
> > +               represented in ASCII decimal format.
> > +
> > +What:          /sys/firmware/secvar/config/supported_policies
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is PLPKS.
> > +
> > +               Contains a bitmask of supported policy flags by the
> > hypervisor,
> > +               represented as an 8 byte hexadecimal ASCII string. 
> > Consult the
> > +               hypervisor documentation for what these flags are.
> > +
> > +What:          /sys/firmware/secvar/config/signed_update_algorithm
> > s
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is PLPKS.
> > +
> > +               Contains a bitmask of flags indicating which
> > algorithms the
> > +               hypervisor supports objects to be signed with when
> > modifying
> > +               secvars, represented as a 16 byte hexadecimal ASCII
> > string.
> > +               Consult the hypervisor documentation for what these
> > flags mean.
>  
> Can this at least say "as defined in PAPR version X section Y"?

I don't think there is currently a published PAPR version with this
stuff in it, but once there is we should update the docs to reference
it.

> 
> > diff --git a/arch/powerpc/platforms/pseries/Kconfig
> > b/arch/powerpc/platforms/pseries/Kconfig
> > index a3b4d99567cb..94e08c405d50 100644
> > --- a/arch/powerpc/platforms/pseries/Kconfig
> > +++ b/arch/powerpc/platforms/pseries/Kconfig
> > @@ -162,6 +162,19 @@ config PSERIES_PLPKS
> >  
> >           If unsure, select N.
> >  
> > +config PSERIES_PLPKS_SECVAR
> > +       depends on PSERIES_PLPKS
> > +       depends on PPC_SECURE_BOOT
> > +       bool "Support for the PLPKS secvar interface"
> > +       help
> > +         PowerVM can support dynamic secure boot with user-defined
> > keys
> > +         through the PLPKS. Keystore objects used in dynamic
> > secure boot
> > +         can be exposed to the kernel and userspace through the
> > powerpc
> > +         secvar infrastructure. Select this to enable the PLPKS
> > backend
> > +         for secvars for use in pseries dynamic secure boot.
> > +
> > +         If unsure, select N.
> 
> I don't think we need that config option at all, or if we do it
> should
> not be user selectable and just enabled automatically by
> PSERIES_PLPKS.

This code needs secvar (which is why PPC_SECURE_BOOT is there).  We
could add a PPC_SECURE_BOOT dependency to PSERIES_PLPKS, but that's not
necessary to just use the keystore, i.e. what [0] is doing.  While
there's no other PLPKS consumers upstream right now, there will be [0],
so that's why I added a new config option for the secure boot case.

I can make it selected automatically if you have both PSERIES_PLPKS and
PPC_SECURE_BOOT.  Not exactly sure what convention is when it comes to
nested dependencies.

[0]:
https://lore.kernel.org/linuxppc-dev/20221130202358.18034-3-gjoyce@linux.vnet.ibm.com/

> 
> > diff --git a/arch/powerpc/platforms/pseries/Makefile
> > b/arch/powerpc/platforms/pseries/Makefile
> > index 92310202bdd7..807756991f9d 100644
> > --- a/arch/powerpc/platforms/pseries/Makefile
> > +++ b/arch/powerpc/platforms/pseries/Makefile
> > @@ -27,8 +27,8 @@ obj-$(CONFIG_PAPR_SCM)                +=
> > papr_scm.o
> >  obj-$(CONFIG_PPC_SPLPAR)       += vphn.o
> >  obj-$(CONFIG_PPC_SVM)          += svm.o
> >  obj-$(CONFIG_FA_DUMP)          += rtas-fadump.o
> > -obj-$(CONFIG_PSERIES_PLPKS) += plpks.o
> > -
> > +obj-$(CONFIG_PSERIES_PLPKS)    += plpks.o
> > +obj-$(CONFIG_PSERIES_PLPKS_SECVAR)     += plpks-secvar.o
> 
> I'm not convinced the secvar parts need to be in a separate C file.
> 
> If it was all in plpks.c we could avoid all/most of plpks.h and a
> bunch
> of accessors and so on.
> 
> But I don't feel that strongly about it if you think it's better
> separate.

I think it makes sense for the reasons above - PPC_SECURE_BOOT (which
is needed for secvar) has a ton of dependencies and any other consumers
of the keystore outside of secure boot would have to build in a lot
more stuff than they need.

Some of plpks.h is going to need to move into include/ to solve some
kexec issues I found too, so I don't think we can hope to entirely get
rid of it.

> 
> > diff --git a/arch/powerpc/platforms/pseries/plpks-secvar.c
> > b/arch/powerpc/platforms/pseries/plpks-secvar.c
> > new file mode 100644
> > index 000000000000..8298f039bef4
> > --- /dev/null
> > +++ b/arch/powerpc/platforms/pseries/plpks-secvar.c
> > @@ -0,0 +1,245 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Secure variable implementation using the PowerVM LPAR Platform
> > KeyStore (PLPKS)
> > + *
> > + * Copyright 2022, IBM Corporation
> > + * Authors: Russell Currey
> > + *          Andrew Donnellan
> > + *          Nayna Jain
> > + */
> > +
> > +#define pr_fmt(fmt) "secvar: "fmt
> > +
> > +#include <linux/printk.h>
> > +#include <linux/init.h>
> > +#include <linux/types.h>
> > +#include <linux/slab.h>
> > +#include <linux/string.h>
> > +#include <linux/kobject.h>
> > +#include <asm/secvar.h>
> > +#include "plpks.h"
> > +
> > +// Config attributes for sysfs
> > +#define PLPKS_CONFIG_ATTR(name, fmt, func)                     \
> > +       static ssize_t name##_show(struct kobject *kobj,        \
> > +                                  struct kobj_attribute *attr, \
> > +                                  char *buf)                   \
> > +       {                                                       \
> > +               return sysfs_emit(buf, fmt, func());            \
> > +       }                                                       \
> > +       static struct kobj_attribute attr_##name = __ATTR_RO(name)
> > +
> > +PLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version);
> > +PLPKS_CONFIG_ATTR(max_object_size, "%u\n",
> > plpks_get_maxobjectsize);
> > +PLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize);
> > +PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);
> > +PLPKS_CONFIG_ATTR(supported_policies, "%08x\n",
> > plpks_get_supportedpolicies);
> > +PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n",
> > plpks_get_signedupdatealgorithms);
> 
> For those last two I wonder if we should be decoding the integer
> values
> into a comma separated list of named flags?
> 
> Just blatting out the integer values is a bit gross. It's not helpful
> for shell scripts, and a consumer written in C has to strtoull() the
> value back into an integer before it can decode it.

We can do that.  We should still blat the integer value in case the
hypervisor adds something the kernel doesn't know about yet, though.

> 
> > +static const struct attribute *config_attrs[] = {
> > +       &attr_version.attr,
> > +       &attr_max_object_size.attr,
> > +       &attr_total_size.attr,
> > +       &attr_used_space.attr,
> > +       &attr_supported_policies.attr,
> > +       &attr_signed_update_algorithms.attr,
> > +       NULL,
> > +};
> > +
> > +static u16 get_ucs2name(const char *name, uint8_t **ucs2_name)
> > +{
> > +       int namelen = strlen(name) * 2;
> > +       *ucs2_name = kzalloc(namelen, GFP_KERNEL);
> > +
> > +       if (!*ucs2_name)
> > +               return 0;
> > +
> > +       for (int i = 0; name[i]; i++) {
> > +               (*ucs2_name)[i * 2] = name[i];
> > +               (*ucs2_name)[i * 2 + 1] = '\0';
> > +       }
> > +
> > +       return namelen;
> > +}
> 
> There are some ucs2 routines in lib/ucs2_string.c, can we use any of
> them?

We didn't think so.  There's routines for dealing with ucs2 strings,
but we don't ever actually do that - all we do is pad C strings and
double the length.  There's ucs2_to_utf8() there, but not the opposite.

We could drop this function and instead hardcode "P\0K\0", "K\0E\0K\0"
etc but that seemed like a lot of duplication.

> 
> > +static u32 get_policy(const char *name)
> > +{
> > +       if ((strcmp(name, "db") == 0) ||
> > +           (strcmp(name, "dbx") == 0) ||
> > +           (strcmp(name, "grubdb") == 0) ||
> > +           (strcmp(name, "sbat") == 0))
> > +               return (WORLDREADABLE | SIGNEDUPDATE);
> > +       else
> > +               return SIGNEDUPDATE;
> > +}
> > +
> > +#define PLPKS_SECVAR_COUNT 8
> 
> I don't think we need that. Just declare the array as unsized and
> then
> use ARRAY_SIZE(var_names) in plpks_get_next_variable().

True, that's better.

> 
> > +static char *var_names[PLPKS_SECVAR_COUNT] = {
> > +       "PK",
> > +       "KEK",
> > +       "db",
> > +       "dbx",
> > +       "grubdb",
> > +       "sbat",
> > +       "moduledb",
> > +       "trustedcadb",
> > +};
> > +
> > +static int plpks_get_variable(const char *key, uint64_t key_len,
> > +                             u8 *data, uint64_t *data_size)
> > +{
> > +       struct plpks_var var = {0};
> > +       u16 ucs2_namelen;
> > +       u8 *ucs2_name;
> > +       int rc = 0;
> > +
> > +       ucs2_namelen = get_ucs2name(key, &ucs2_name);
> > +       if (!ucs2_namelen)
> > +               return -ENOMEM;
> > +
> > +       var.name = ucs2_name;
> > +       var.namelen = ucs2_namelen;
> > +       var.os = PLPKS_VAR_LINUX;
> > +       rc = plpks_read_os_var(&var);
> > +
> > +       if (rc)
> > +               goto err;
> > +
> > +       *data_size = var.datalen + sizeof(var.policy);
> > +
> > +       // We can be called with data = NULL to just get the object
> > size.
> > +       if (data) {
> > +               memcpy(data, &var.policy, sizeof(var.policy));
> > +               memcpy(data + sizeof(var.policy), var.data,
> > var.datalen);
> > +       }
> 
> There's a lot of allocation and copying going on. The secvar-sysfs.c
> data_read() has kzalloc'ed data, but only after already doing the
> hcall
> to get the size. Then plpks_read_os_var() does an allocation for the
> hcall and then another allocation of the exact data size. Then
> data_read()
> does another copy into the sysfs supplied buffer.
> 
> So to read a single variable we do the hcall twice, and allocate/copy
> the content of the variable 4 times?

We don't need to do the hcall twice for PLPKS.  I can add a flag to
secvar_ops to skip object size discovery and just allocate max_size()
in data_read() instead.  I can't see a reason why OPAL can't just do
that too, but I don't know the details and I don't want to break it.

We would also have to change get_cert_list() in load_powerpc.c, which
does the same thing.

> 
>  - Hypervisor into "output" in plpks_read_var().
>  - "output" into "var->data" in plpks_read_var().
>  - "var.data" into "data" in plpks_get_variable().
>  - "data" into "buf" in data_read().
> 
> As long as maxobjsize is < PAGE_SIZE I think we could do the hcall
> directly into "buf". Maybe we want to avoid writing into "buf"
> directly
> in case the hcall fails or something, but the other 3 copies seem
> unnecessary.

The plpks.c code is pretty heavily abstracted, though maybe we could do
something like not allocate a new buffer if we call plpks_read_var()
and var.data != NULL.  So if plpks_get_variable() calls
plpks_read_var() where var.data = data + 4 (gotta make room for the
policy), I think those changes combine to go from 2 hcalls to 1, and 4
copies to 2.

That said, while we should make it faster, this isn't a particularly
hot code path.

> 
> > +       kfree(var.data);
> > +err:
> > +       kfree(ucs2_name);
> > +       return rc;
> > +}
> > +
> > +static int plpks_set_variable(const char *key, uint64_t key_len,
> > +                             u8 *data, uint64_t data_size)
> > +{
> > +       struct plpks_var var = {0};
> > +       u16 ucs2_namelen;
> > +       u8 *ucs2_name;
> > +       int rc = 0;
> > +       u64 flags;
> > +
> > +       // Secure variables need to be prefixed with 8 bytes of
> > flags.
> > +       // We only want to perform the write if we have at least
> > one byte of data.
> > +       if (data_size <= sizeof(flags))
> > +               return -EINVAL;
> > +
> > +       ucs2_namelen = get_ucs2name(key, &ucs2_name);
> > +       if (!ucs2_namelen)
> > +               return -ENOMEM;
> > +
> > +       memcpy(&flags, data, sizeof(flags));
> > +
> > +       var.datalen = data_size - sizeof(flags);
> > +       var.data = kzalloc(var.datalen, GFP_KERNEL);
> > +       if (!var.data) {
> > +               rc = -ENOMEM;
> > +               goto err;
> > +       }
> > +
> > +       memcpy(var.data, data + sizeof(flags), var.datalen);
> > +
> > +       var.name = ucs2_name;
> > +       var.namelen = ucs2_namelen;
> > +       var.os = PLPKS_VAR_LINUX;
> > +       var.policy = get_policy(key);
> > +
> > +       rc = plpks_signed_update_var(var, flags);
> > +
> > +       kfree(var.data);
> > +err:
> > +       kfree(ucs2_name);
> > +       return rc;
> > +}
> > +
> > +/*
> > + * get_next() in the secvar API is designed for the OPAL API.
> > + * If *key is 0, it returns the first variable in the keystore.
> > + * Otherwise, you pass the name of a key and it returns next in
> > line.
> > + *
> > + * We're going to cheat here - since we have fixed keys and don't
> > care about
> > + * key_len, we can just use it as an index.
> > + */
> 
> That's kinda gross. Just change the ops API to do what we need?
> Either
> add a separate get-by-index routine or change the existing one and
> update the only other implementation.

I tried the latter and it was substantially more complex, I'll add a
new op and secvar can use whichever it's provided with.

> 
> > +static int plpks_get_next_variable(const char *key, uint64_t
> > *key_len, uint64_t keybufsize)
> > +{
> > +       if (!key || !key_len)
> > +               return -EINVAL;
> > +
> > +       if (*key_len >= PLPKS_SECVAR_COUNT)
> > +               return -ENOENT;
> > +
> > +       if (strscpy((char *)key, var_names[(*key_len)++],
> > keybufsize) < 0)
> > +               return -E2BIG;
> > +
> > +       return 0;
> > +}
> > +
> > +// PLPKS dynamic secure boot doesn't give us a format string in
> > the same way OPAL does.
> > +// Instead, report the format using the SB_VERSION variable in the
> > keystore.
> > +static ssize_t plpks_secvar_format(char *buf)
> > +{
> > +       struct plpks_var var = {0};
> > +       ssize_t ret;
> > +
> > +       var.component = NULL;
> > +       // Only the signed variables have ucs2-encoded names, this
> > one doesn't
> > +       var.name = "SB_VERSION";
> 
> Is that specified somewhere?

Not publicly, at least not yet.  PAPR will document everything about
the hcalls but I'm not sure if it will document specific pre-created
objects used for dynamic secure boot.

> 
> > +       var.namelen = 10;
> > +       var.datalen = 0;
> > +       var.data = NULL;
> > +
> > +       // Unlike the other vars, SB_VERSION is owned by firmware
> > instead of the OS
> > +       ret = plpks_read_fw_var(&var);
> > +       if (ret) {
> > +               if (ret == -ENOENT)
> > +                       return sysfs_emit(buf, "ibm,plpks-sb-
> > unknown\n");
> > +
> > +               pr_err("Error %ld reading SB_VERSION from
> > firmware\n", ret);
> > +               return ret;
> 
> I'm not sure you should pass that raw error back to sysfs. Some of
> the
> values could be confusing, eg. if you return -EINVAL it looks like a
> parameter to the read() syscall was invalid. Might be better to just
> return -EIO.

OK, is it sane to print a different error code than the one we return?
I assume it's fine in this context, just wouldn't want to lose
information.

> 
> > +       }
> > +
> > +       // Hypervisor defines SB_VERSION as a "1 byte unsigned
> > integer value"
> > +       ret = sysfs_emit(buf, "ibm,plpks-sb-%hhu\n", var.data[0]);
> 
> The rest of the name string is just made up by us?

The format string is entirely made up by me.

OPAL secvar has a "real" format string (i.e. it's provided by the
device tree).  We have a format string in sysfs which is ABI, and
security/integrity/platform_certs/load_powerpc.c looks for it (by
looking in the device tree rather than using secvar_ops->format(), I
need to fix that too).  I figured pseries should use it too, and that
it should be different from OPAL.  There is nothing specified on the
hypervisor end that we could use, all we have is the magic SB_VERSION
value.

I figured it was a reasonable way to do things.  Open to other ideas
(including better names).

> 
> > +       kfree(var.data);
> > +       return ret;
> > +}
> > +
> > +static int plpks_max_size(uint64_t *max_size)
> > +{
> > +       // The max object size reported by the hypervisor is
> > accurate for the
> > +       // object itself, but we use the first 8 bytes of data on
> > write as the
> > +       // signed update flags, so the max size a user can write is
> > larger.
> > +       *max_size = (uint64_t)plpks_get_maxobjectsize() + 8;
> > +
> > +       return 0;
> > +}
> > +
> > +
> > +static const struct secvar_operations plpks_secvar_ops = {
> > +       .get = plpks_get_variable,
> > +       .get_next = plpks_get_next_variable,
> > +       .set = plpks_set_variable,
> > +       .format = plpks_secvar_format,
> > +       .max_size = plpks_max_size,
> > +};
> > +
> > +static int plpks_secvar_init(void)
> > +{
> > +       if (!plpks_is_available())
> > +               return -ENODEV;
> > +
> > +       set_secvar_ops(&plpks_secvar_ops);
> > +       set_secvar_config_attrs(config_attrs);
> > +       return 0;
> > +}
> > +device_initcall(plpks_secvar_init);
> 
> That must be a machine_device_initcall(pseries, ...), otherwise we
> will
> blow up doing a hcall on powernv in plpks_is_available().

OK, can do.  I don't understand your case of how powernv could hit
this, but I think I to have to move plpks_is_available() into include/,
so it's going to be even more possible anyway.

> 
> cheers


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

* Re: [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
@ 2023-01-09  3:34       ` Russell Currey
  0 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2023-01-09  3:34 UTC (permalink / raw)
  To: Michael Ellerman, linuxppc-dev
  Cc: ajd, gregkh, nayna, linux-kernel, zohar, gcwilson

On Fri, 2023-01-06 at 21:49 +1100, Michael Ellerman wrote:
> Russell Currey <ruscur@russell.cc> writes:
> > The pseries platform can support dynamic secure boot (i.e. secure
> > boot
> > using user-defined keys) using variables contained with the PowerVM
> > LPAR
> > Platform KeyStore (PLPKS).  Using the powerpc secvar API, expose
> > the
> > relevant variables for pseries dynamic secure boot through the
> > existing
> > secvar filesystem layout.
> > 
> > The relevant variables for dynamic secure boot are signed in the
> > keystore, and can only be modified using the H_PKS_SIGNED_UPDATE
> > hcall.
> > Object labels in the keystore are encoded using ucs2 format.  With
> > our
> > fixed variable names we don't have to care about encoding outside
> > of the
> > necessary byte padding.
> > 
> > When a user writes to a variable, the first 8 bytes of data must
> > contain
> > the signed update flags as defined by the hypervisor.
> > 
> > When a user reads a variable, the first 4 bytes of data contain the
> > policies defined for the object.
> > 
> > Limitations exist due to the underlying implementation of sysfs
> > binary
> > attributes, as is the case for the OPAL secvar implementation -
> > partial writes are unsupported and writes cannot be larger than
> > PAGE_SIZE.
> > 
> > Co-developed-by: Nayna Jain <nayna@linux.ibm.com>
> > Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
> > Co-developed-by: Andrew Donnellan <ajd@linux.ibm.com>
> > Signed-off-by: Andrew Donnellan <ajd@linux.ibm.com>
> > Signed-off-by: Russell Currey <ruscur@russell.cc>
> > ---
> > v2: Remove unnecessary config vars from sysfs and document the
> > others,
> >     thanks to review from Greg.  If we end up needing to expose
> > more, we
> >     can add them later and update the docs.
> > 
> >     Use sysfs_emit() instead of sprintf(), thanks to Greg.
> > 
> >     Change the size of the sysfs binary attributes to include the
> > 8-byte
> >     flags header, preventing truncation of large writes.
> > 
> >  Documentation/ABI/testing/sysfs-secvar        |  67 ++++-
> >  arch/powerpc/platforms/pseries/Kconfig        |  13 +
> >  arch/powerpc/platforms/pseries/Makefile       |   4 +-
> >  arch/powerpc/platforms/pseries/plpks-secvar.c | 245
> > ++++++++++++++++++
> >  4 files changed, 326 insertions(+), 3 deletions(-)
> >  create mode 100644 arch/powerpc/platforms/pseries/plpks-secvar.c
> > 
> > diff --git a/Documentation/ABI/testing/sysfs-secvar
> > b/Documentation/ABI/testing/sysfs-secvar
> > index feebb8c57294..466a8cb92b92 100644
> > --- a/Documentation/ABI/testing/sysfs-secvar
> > +++ b/Documentation/ABI/testing/sysfs-secvar
> > @@ -34,7 +34,7 @@ Description:  An integer representation of the
> > size of the content of the
> >  
> >  What:          /sys/firmware/secvar/vars/<variable_name>/data
> >  Date:          August 2019
> > -Contact:       Nayna Jain h<nayna@linux.ibm.com>
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> >  Description:   A read-only file containing the value of the
> > variable. The size
> >                 of the file represents the maximum size of the
> > variable data.
> >  
> > @@ -44,3 +44,68 @@ Contact:     Nayna Jain <nayna@linux.ibm.com>
> >  Description:   A write-only file that is used to submit the new
> > value for the
> >                 variable. The size of the file represents the
> > maximum size of
> >                 the variable data that can be written.
> > +
> > +What:          /sys/firmware/secvar/config
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   This optional directory contains read-only config
> > attributes as
> > +               defined by the secure variable implementation.  All
> > data is in
> > +               ASCII format. The directory is only created if the
> > backing
> > +               implementation provides variables to populate it,
> > which at
> > +               present is only PLPKS on the pseries platform.
> 
> I think it's OK to mention that currently this only exists for PLPKS
> ...
> 
> > +What:          /sys/firmware/secvar/config/version
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is PLPKS.
> 
> ... but I don't think we want to specify that files are only present
> for PLPKS. 
> 
> Because if another backend wanted to create them in future, that
> would
> technically be an ABI change.

Some are going to be PLPKS-specific, but for generic stuff like this I
can change the description.

> 
> > +               Contains the config version as reported by the
> > hypervisor in
> > +               ASCII decimal format.
> > +
> > +What:          /sys/firmware/secvar/config/max_object_size
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is PLPKS.
> > +
> > +               Contains the maximum allowed size of objects in the
> > keystore
> > +               in bytes, represented in ASCII decimal format.
> > +
> > +               This is not necessarily the same as the max size
> > that can be
> > +               written to an update file as writes can contain
> > more than
> > +               object data, you should use the size of the update
> > file for
> > +               that purpose.
> > +
> > +What:          /sys/firmware/secvar/config/total_size
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is PLPKS.
> > +
> > +               Contains the total size of the PLPKS in bytes,
> > represented in
> > +               ASCII decimal format.
> 
> Similarly here I think the description should be written in a way
> that
> is agnostic about the backend. So eg. "Contains the total size of the
> key store in bytes".
> 
> 
> > +What:          /sys/firmware/secvar/config/used_space
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is PLPKS.
> > +
> > +               Contains the current space consumed of the PLPKS in
> > bytes,
> > +               represented in ASCII decimal format.
> > +
> > +What:          /sys/firmware/secvar/config/supported_policies
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is PLPKS.
> > +
> > +               Contains a bitmask of supported policy flags by the
> > hypervisor,
> > +               represented as an 8 byte hexadecimal ASCII string. 
> > Consult the
> > +               hypervisor documentation for what these flags are.
> > +
> > +What:          /sys/firmware/secvar/config/signed_update_algorithm
> > s
> > +Date:          December 2022
> > +Contact:       Nayna Jain <nayna@linux.ibm.com>
> > +Description:   RO file, only present if the secvar implementation
> > is PLPKS.
> > +
> > +               Contains a bitmask of flags indicating which
> > algorithms the
> > +               hypervisor supports objects to be signed with when
> > modifying
> > +               secvars, represented as a 16 byte hexadecimal ASCII
> > string.
> > +               Consult the hypervisor documentation for what these
> > flags mean.
>  
> Can this at least say "as defined in PAPR version X section Y"?

I don't think there is currently a published PAPR version with this
stuff in it, but once there is we should update the docs to reference
it.

> 
> > diff --git a/arch/powerpc/platforms/pseries/Kconfig
> > b/arch/powerpc/platforms/pseries/Kconfig
> > index a3b4d99567cb..94e08c405d50 100644
> > --- a/arch/powerpc/platforms/pseries/Kconfig
> > +++ b/arch/powerpc/platforms/pseries/Kconfig
> > @@ -162,6 +162,19 @@ config PSERIES_PLPKS
> >  
> >           If unsure, select N.
> >  
> > +config PSERIES_PLPKS_SECVAR
> > +       depends on PSERIES_PLPKS
> > +       depends on PPC_SECURE_BOOT
> > +       bool "Support for the PLPKS secvar interface"
> > +       help
> > +         PowerVM can support dynamic secure boot with user-defined
> > keys
> > +         through the PLPKS. Keystore objects used in dynamic
> > secure boot
> > +         can be exposed to the kernel and userspace through the
> > powerpc
> > +         secvar infrastructure. Select this to enable the PLPKS
> > backend
> > +         for secvars for use in pseries dynamic secure boot.
> > +
> > +         If unsure, select N.
> 
> I don't think we need that config option at all, or if we do it
> should
> not be user selectable and just enabled automatically by
> PSERIES_PLPKS.

This code needs secvar (which is why PPC_SECURE_BOOT is there).  We
could add a PPC_SECURE_BOOT dependency to PSERIES_PLPKS, but that's not
necessary to just use the keystore, i.e. what [0] is doing.  While
there's no other PLPKS consumers upstream right now, there will be [0],
so that's why I added a new config option for the secure boot case.

I can make it selected automatically if you have both PSERIES_PLPKS and
PPC_SECURE_BOOT.  Not exactly sure what convention is when it comes to
nested dependencies.

[0]:
https://lore.kernel.org/linuxppc-dev/20221130202358.18034-3-gjoyce@linux.vnet.ibm.com/

> 
> > diff --git a/arch/powerpc/platforms/pseries/Makefile
> > b/arch/powerpc/platforms/pseries/Makefile
> > index 92310202bdd7..807756991f9d 100644
> > --- a/arch/powerpc/platforms/pseries/Makefile
> > +++ b/arch/powerpc/platforms/pseries/Makefile
> > @@ -27,8 +27,8 @@ obj-$(CONFIG_PAPR_SCM)                +=
> > papr_scm.o
> >  obj-$(CONFIG_PPC_SPLPAR)       += vphn.o
> >  obj-$(CONFIG_PPC_SVM)          += svm.o
> >  obj-$(CONFIG_FA_DUMP)          += rtas-fadump.o
> > -obj-$(CONFIG_PSERIES_PLPKS) += plpks.o
> > -
> > +obj-$(CONFIG_PSERIES_PLPKS)    += plpks.o
> > +obj-$(CONFIG_PSERIES_PLPKS_SECVAR)     += plpks-secvar.o
> 
> I'm not convinced the secvar parts need to be in a separate C file.
> 
> If it was all in plpks.c we could avoid all/most of plpks.h and a
> bunch
> of accessors and so on.
> 
> But I don't feel that strongly about it if you think it's better
> separate.

I think it makes sense for the reasons above - PPC_SECURE_BOOT (which
is needed for secvar) has a ton of dependencies and any other consumers
of the keystore outside of secure boot would have to build in a lot
more stuff than they need.

Some of plpks.h is going to need to move into include/ to solve some
kexec issues I found too, so I don't think we can hope to entirely get
rid of it.

> 
> > diff --git a/arch/powerpc/platforms/pseries/plpks-secvar.c
> > b/arch/powerpc/platforms/pseries/plpks-secvar.c
> > new file mode 100644
> > index 000000000000..8298f039bef4
> > --- /dev/null
> > +++ b/arch/powerpc/platforms/pseries/plpks-secvar.c
> > @@ -0,0 +1,245 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Secure variable implementation using the PowerVM LPAR Platform
> > KeyStore (PLPKS)
> > + *
> > + * Copyright 2022, IBM Corporation
> > + * Authors: Russell Currey
> > + *          Andrew Donnellan
> > + *          Nayna Jain
> > + */
> > +
> > +#define pr_fmt(fmt) "secvar: "fmt
> > +
> > +#include <linux/printk.h>
> > +#include <linux/init.h>
> > +#include <linux/types.h>
> > +#include <linux/slab.h>
> > +#include <linux/string.h>
> > +#include <linux/kobject.h>
> > +#include <asm/secvar.h>
> > +#include "plpks.h"
> > +
> > +// Config attributes for sysfs
> > +#define PLPKS_CONFIG_ATTR(name, fmt, func)                     \
> > +       static ssize_t name##_show(struct kobject *kobj,        \
> > +                                  struct kobj_attribute *attr, \
> > +                                  char *buf)                   \
> > +       {                                                       \
> > +               return sysfs_emit(buf, fmt, func());            \
> > +       }                                                       \
> > +       static struct kobj_attribute attr_##name = __ATTR_RO(name)
> > +
> > +PLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version);
> > +PLPKS_CONFIG_ATTR(max_object_size, "%u\n",
> > plpks_get_maxobjectsize);
> > +PLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize);
> > +PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);
> > +PLPKS_CONFIG_ATTR(supported_policies, "%08x\n",
> > plpks_get_supportedpolicies);
> > +PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n",
> > plpks_get_signedupdatealgorithms);
> 
> For those last two I wonder if we should be decoding the integer
> values
> into a comma separated list of named flags?
> 
> Just blatting out the integer values is a bit gross. It's not helpful
> for shell scripts, and a consumer written in C has to strtoull() the
> value back into an integer before it can decode it.

We can do that.  We should still blat the integer value in case the
hypervisor adds something the kernel doesn't know about yet, though.

> 
> > +static const struct attribute *config_attrs[] = {
> > +       &attr_version.attr,
> > +       &attr_max_object_size.attr,
> > +       &attr_total_size.attr,
> > +       &attr_used_space.attr,
> > +       &attr_supported_policies.attr,
> > +       &attr_signed_update_algorithms.attr,
> > +       NULL,
> > +};
> > +
> > +static u16 get_ucs2name(const char *name, uint8_t **ucs2_name)
> > +{
> > +       int namelen = strlen(name) * 2;
> > +       *ucs2_name = kzalloc(namelen, GFP_KERNEL);
> > +
> > +       if (!*ucs2_name)
> > +               return 0;
> > +
> > +       for (int i = 0; name[i]; i++) {
> > +               (*ucs2_name)[i * 2] = name[i];
> > +               (*ucs2_name)[i * 2 + 1] = '\0';
> > +       }
> > +
> > +       return namelen;
> > +}
> 
> There are some ucs2 routines in lib/ucs2_string.c, can we use any of
> them?

We didn't think so.  There's routines for dealing with ucs2 strings,
but we don't ever actually do that - all we do is pad C strings and
double the length.  There's ucs2_to_utf8() there, but not the opposite.

We could drop this function and instead hardcode "P\0K\0", "K\0E\0K\0"
etc but that seemed like a lot of duplication.

> 
> > +static u32 get_policy(const char *name)
> > +{
> > +       if ((strcmp(name, "db") == 0) ||
> > +           (strcmp(name, "dbx") == 0) ||
> > +           (strcmp(name, "grubdb") == 0) ||
> > +           (strcmp(name, "sbat") == 0))
> > +               return (WORLDREADABLE | SIGNEDUPDATE);
> > +       else
> > +               return SIGNEDUPDATE;
> > +}
> > +
> > +#define PLPKS_SECVAR_COUNT 8
> 
> I don't think we need that. Just declare the array as unsized and
> then
> use ARRAY_SIZE(var_names) in plpks_get_next_variable().

True, that's better.

> 
> > +static char *var_names[PLPKS_SECVAR_COUNT] = {
> > +       "PK",
> > +       "KEK",
> > +       "db",
> > +       "dbx",
> > +       "grubdb",
> > +       "sbat",
> > +       "moduledb",
> > +       "trustedcadb",
> > +};
> > +
> > +static int plpks_get_variable(const char *key, uint64_t key_len,
> > +                             u8 *data, uint64_t *data_size)
> > +{
> > +       struct plpks_var var = {0};
> > +       u16 ucs2_namelen;
> > +       u8 *ucs2_name;
> > +       int rc = 0;
> > +
> > +       ucs2_namelen = get_ucs2name(key, &ucs2_name);
> > +       if (!ucs2_namelen)
> > +               return -ENOMEM;
> > +
> > +       var.name = ucs2_name;
> > +       var.namelen = ucs2_namelen;
> > +       var.os = PLPKS_VAR_LINUX;
> > +       rc = plpks_read_os_var(&var);
> > +
> > +       if (rc)
> > +               goto err;
> > +
> > +       *data_size = var.datalen + sizeof(var.policy);
> > +
> > +       // We can be called with data = NULL to just get the object
> > size.
> > +       if (data) {
> > +               memcpy(data, &var.policy, sizeof(var.policy));
> > +               memcpy(data + sizeof(var.policy), var.data,
> > var.datalen);
> > +       }
> 
> There's a lot of allocation and copying going on. The secvar-sysfs.c
> data_read() has kzalloc'ed data, but only after already doing the
> hcall
> to get the size. Then plpks_read_os_var() does an allocation for the
> hcall and then another allocation of the exact data size. Then
> data_read()
> does another copy into the sysfs supplied buffer.
> 
> So to read a single variable we do the hcall twice, and allocate/copy
> the content of the variable 4 times?

We don't need to do the hcall twice for PLPKS.  I can add a flag to
secvar_ops to skip object size discovery and just allocate max_size()
in data_read() instead.  I can't see a reason why OPAL can't just do
that too, but I don't know the details and I don't want to break it.

We would also have to change get_cert_list() in load_powerpc.c, which
does the same thing.

> 
>  - Hypervisor into "output" in plpks_read_var().
>  - "output" into "var->data" in plpks_read_var().
>  - "var.data" into "data" in plpks_get_variable().
>  - "data" into "buf" in data_read().
> 
> As long as maxobjsize is < PAGE_SIZE I think we could do the hcall
> directly into "buf". Maybe we want to avoid writing into "buf"
> directly
> in case the hcall fails or something, but the other 3 copies seem
> unnecessary.

The plpks.c code is pretty heavily abstracted, though maybe we could do
something like not allocate a new buffer if we call plpks_read_var()
and var.data != NULL.  So if plpks_get_variable() calls
plpks_read_var() where var.data = data + 4 (gotta make room for the
policy), I think those changes combine to go from 2 hcalls to 1, and 4
copies to 2.

That said, while we should make it faster, this isn't a particularly
hot code path.

> 
> > +       kfree(var.data);
> > +err:
> > +       kfree(ucs2_name);
> > +       return rc;
> > +}
> > +
> > +static int plpks_set_variable(const char *key, uint64_t key_len,
> > +                             u8 *data, uint64_t data_size)
> > +{
> > +       struct plpks_var var = {0};
> > +       u16 ucs2_namelen;
> > +       u8 *ucs2_name;
> > +       int rc = 0;
> > +       u64 flags;
> > +
> > +       // Secure variables need to be prefixed with 8 bytes of
> > flags.
> > +       // We only want to perform the write if we have at least
> > one byte of data.
> > +       if (data_size <= sizeof(flags))
> > +               return -EINVAL;
> > +
> > +       ucs2_namelen = get_ucs2name(key, &ucs2_name);
> > +       if (!ucs2_namelen)
> > +               return -ENOMEM;
> > +
> > +       memcpy(&flags, data, sizeof(flags));
> > +
> > +       var.datalen = data_size - sizeof(flags);
> > +       var.data = kzalloc(var.datalen, GFP_KERNEL);
> > +       if (!var.data) {
> > +               rc = -ENOMEM;
> > +               goto err;
> > +       }
> > +
> > +       memcpy(var.data, data + sizeof(flags), var.datalen);
> > +
> > +       var.name = ucs2_name;
> > +       var.namelen = ucs2_namelen;
> > +       var.os = PLPKS_VAR_LINUX;
> > +       var.policy = get_policy(key);
> > +
> > +       rc = plpks_signed_update_var(var, flags);
> > +
> > +       kfree(var.data);
> > +err:
> > +       kfree(ucs2_name);
> > +       return rc;
> > +}
> > +
> > +/*
> > + * get_next() in the secvar API is designed for the OPAL API.
> > + * If *key is 0, it returns the first variable in the keystore.
> > + * Otherwise, you pass the name of a key and it returns next in
> > line.
> > + *
> > + * We're going to cheat here - since we have fixed keys and don't
> > care about
> > + * key_len, we can just use it as an index.
> > + */
> 
> That's kinda gross. Just change the ops API to do what we need?
> Either
> add a separate get-by-index routine or change the existing one and
> update the only other implementation.

I tried the latter and it was substantially more complex, I'll add a
new op and secvar can use whichever it's provided with.

> 
> > +static int plpks_get_next_variable(const char *key, uint64_t
> > *key_len, uint64_t keybufsize)
> > +{
> > +       if (!key || !key_len)
> > +               return -EINVAL;
> > +
> > +       if (*key_len >= PLPKS_SECVAR_COUNT)
> > +               return -ENOENT;
> > +
> > +       if (strscpy((char *)key, var_names[(*key_len)++],
> > keybufsize) < 0)
> > +               return -E2BIG;
> > +
> > +       return 0;
> > +}
> > +
> > +// PLPKS dynamic secure boot doesn't give us a format string in
> > the same way OPAL does.
> > +// Instead, report the format using the SB_VERSION variable in the
> > keystore.
> > +static ssize_t plpks_secvar_format(char *buf)
> > +{
> > +       struct plpks_var var = {0};
> > +       ssize_t ret;
> > +
> > +       var.component = NULL;
> > +       // Only the signed variables have ucs2-encoded names, this
> > one doesn't
> > +       var.name = "SB_VERSION";
> 
> Is that specified somewhere?

Not publicly, at least not yet.  PAPR will document everything about
the hcalls but I'm not sure if it will document specific pre-created
objects used for dynamic secure boot.

> 
> > +       var.namelen = 10;
> > +       var.datalen = 0;
> > +       var.data = NULL;
> > +
> > +       // Unlike the other vars, SB_VERSION is owned by firmware
> > instead of the OS
> > +       ret = plpks_read_fw_var(&var);
> > +       if (ret) {
> > +               if (ret == -ENOENT)
> > +                       return sysfs_emit(buf, "ibm,plpks-sb-
> > unknown\n");
> > +
> > +               pr_err("Error %ld reading SB_VERSION from
> > firmware\n", ret);
> > +               return ret;
> 
> I'm not sure you should pass that raw error back to sysfs. Some of
> the
> values could be confusing, eg. if you return -EINVAL it looks like a
> parameter to the read() syscall was invalid. Might be better to just
> return -EIO.

OK, is it sane to print a different error code than the one we return?
I assume it's fine in this context, just wouldn't want to lose
information.

> 
> > +       }
> > +
> > +       // Hypervisor defines SB_VERSION as a "1 byte unsigned
> > integer value"
> > +       ret = sysfs_emit(buf, "ibm,plpks-sb-%hhu\n", var.data[0]);
> 
> The rest of the name string is just made up by us?

The format string is entirely made up by me.

OPAL secvar has a "real" format string (i.e. it's provided by the
device tree).  We have a format string in sysfs which is ABI, and
security/integrity/platform_certs/load_powerpc.c looks for it (by
looking in the device tree rather than using secvar_ops->format(), I
need to fix that too).  I figured pseries should use it too, and that
it should be different from OPAL.  There is nothing specified on the
hypervisor end that we could use, all we have is the magic SB_VERSION
value.

I figured it was a reasonable way to do things.  Open to other ideas
(including better names).

> 
> > +       kfree(var.data);
> > +       return ret;
> > +}
> > +
> > +static int plpks_max_size(uint64_t *max_size)
> > +{
> > +       // The max object size reported by the hypervisor is
> > accurate for the
> > +       // object itself, but we use the first 8 bytes of data on
> > write as the
> > +       // signed update flags, so the max size a user can write is
> > larger.
> > +       *max_size = (uint64_t)plpks_get_maxobjectsize() + 8;
> > +
> > +       return 0;
> > +}
> > +
> > +
> > +static const struct secvar_operations plpks_secvar_ops = {
> > +       .get = plpks_get_variable,
> > +       .get_next = plpks_get_next_variable,
> > +       .set = plpks_set_variable,
> > +       .format = plpks_secvar_format,
> > +       .max_size = plpks_max_size,
> > +};
> > +
> > +static int plpks_secvar_init(void)
> > +{
> > +       if (!plpks_is_available())
> > +               return -ENODEV;
> > +
> > +       set_secvar_ops(&plpks_secvar_ops);
> > +       set_secvar_config_attrs(config_attrs);
> > +       return 0;
> > +}
> > +device_initcall(plpks_secvar_init);
> 
> That must be a machine_device_initcall(pseries, ...), otherwise we
> will
> blow up doing a hcall on powernv in plpks_is_available().

OK, can do.  I don't understand your case of how powernv could hit
this, but I think I to have to move plpks_is_available() into include/,
so it's going to be even more possible anyway.

> 
> cheers


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

* Re: [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
  2023-01-06  6:49       ` Russell Currey
@ 2023-01-09  4:42         ` Andrew Donnellan
  -1 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-09  4:42 UTC (permalink / raw)
  To: Russell Currey, linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, zohar, mpe

On Fri, 2023-01-06 at 17:49 +1100, Russell Currey wrote:
> > 
> > > + */
> > 
> > Inconsistent comment style
> 
> True, I'm using // for multi-line comments in other places.  I think
> my
> brain decided that this one was too long for that, but I'll make the
> other multi-line comments similarly old-fashioned for consistency.

Sigh, I was trying to encourage you to move into the future...

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
@ 2023-01-09  4:42         ` Andrew Donnellan
  0 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-09  4:42 UTC (permalink / raw)
  To: Russell Currey, linuxppc-dev; +Cc: gregkh, nayna, linux-kernel, zohar, gcwilson

On Fri, 2023-01-06 at 17:49 +1100, Russell Currey wrote:
> > 
> > > + */
> > 
> > Inconsistent comment style
> 
> True, I'm using // for multi-line comments in other places.  I think
> my
> brain decided that this one was too long for that, but I'll make the
> other multi-line comments similarly old-fashioned for consistency.

Sigh, I was trying to encourage you to move into the future...

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
  2023-01-09  3:34       ` Russell Currey
@ 2023-01-09  5:20         ` Andrew Donnellan
  -1 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-09  5:20 UTC (permalink / raw)
  To: Russell Currey, Michael Ellerman, linuxppc-dev
  Cc: nayna, gregkh, gcwilson, linux-kernel, zohar

On Mon, 2023-01-09 at 14:34 +1100, Russell Currey wrote:
> 
> > > +static int plpks_secvar_init(void)
> > > +{
> > > +       if (!plpks_is_available())
> > > +               return -ENODEV;
> > > +
> > > +       set_secvar_ops(&plpks_secvar_ops);
> > > +       set_secvar_config_attrs(config_attrs);
> > > +       return 0;
> > > +}
> > > +device_initcall(plpks_secvar_init);
> > 
> > That must be a machine_device_initcall(pseries, ...), otherwise we
> > will
> > blow up doing a hcall on powernv in plpks_is_available().
> 
> OK, can do.  I don't understand your case of how powernv could hit
> this, but I think I to have to move plpks_is_available() into
> include/,
> so it's going to be even more possible anyway.

Kernels can be compiled with both pseries and powernv support, in which
case plpks_secvar_init() will be called unconditionally even when
booting on a powernv machine.

I can confirm that as it is, booting this on powernv qemu causes a
panic.

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
@ 2023-01-09  5:20         ` Andrew Donnellan
  0 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-09  5:20 UTC (permalink / raw)
  To: Russell Currey, Michael Ellerman, linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, zohar

On Mon, 2023-01-09 at 14:34 +1100, Russell Currey wrote:
> 
> > > +static int plpks_secvar_init(void)
> > > +{
> > > +       if (!plpks_is_available())
> > > +               return -ENODEV;
> > > +
> > > +       set_secvar_ops(&plpks_secvar_ops);
> > > +       set_secvar_config_attrs(config_attrs);
> > > +       return 0;
> > > +}
> > > +device_initcall(plpks_secvar_init);
> > 
> > That must be a machine_device_initcall(pseries, ...), otherwise we
> > will
> > blow up doing a hcall on powernv in plpks_is_available().
> 
> OK, can do.  I don't understand your case of how powernv could hit
> this, but I think I to have to move plpks_is_available() into
> include/,
> so it's going to be even more possible anyway.

Kernels can be compiled with both pseries and powernv support, in which
case plpks_secvar_init() will be called unconditionally even when
booting on a powernv machine.

I can confirm that as it is, booting this on powernv qemu causes a
panic.

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
  2023-01-09  5:20         ` Andrew Donnellan
@ 2023-01-10  1:27           ` Russell Currey
  -1 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2023-01-10  1:27 UTC (permalink / raw)
  To: Andrew Donnellan, Michael Ellerman, linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, zohar

On Mon, 2023-01-09 at 16:20 +1100, Andrew Donnellan wrote:
> On Mon, 2023-01-09 at 14:34 +1100, Russell Currey wrote:
> > 
> > > > +static int plpks_secvar_init(void)
> > > > +{
> > > > +       if (!plpks_is_available())
> > > > +               return -ENODEV;
> > > > +
> > > > +       set_secvar_ops(&plpks_secvar_ops);
> > > > +       set_secvar_config_attrs(config_attrs);
> > > > +       return 0;
> > > > +}
> > > > +device_initcall(plpks_secvar_init);
> > > 
> > > That must be a machine_device_initcall(pseries, ...), otherwise
> > > we
> > > will
> > > blow up doing a hcall on powernv in plpks_is_available().
> > 
> > OK, can do.  I don't understand your case of how powernv could hit
> > this, but I think I to have to move plpks_is_available() into
> > include/,
> > so it's going to be even more possible anyway.
> 
> Kernels can be compiled with both pseries and powernv support, in
> which
> case plpks_secvar_init() will be called unconditionally even when
> booting on a powernv machine.
> 
> I can confirm that as it is, booting this on powernv qemu causes a
> panic.

Of course, I'm not sure why I thought an initcall in a platform that
wasn't active would magically not run on other platforms.

> 


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

* Re: [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
@ 2023-01-10  1:27           ` Russell Currey
  0 siblings, 0 replies; 54+ messages in thread
From: Russell Currey @ 2023-01-10  1:27 UTC (permalink / raw)
  To: Andrew Donnellan, Michael Ellerman, linuxppc-dev
  Cc: nayna, gregkh, gcwilson, linux-kernel, zohar

On Mon, 2023-01-09 at 16:20 +1100, Andrew Donnellan wrote:
> On Mon, 2023-01-09 at 14:34 +1100, Russell Currey wrote:
> > 
> > > > +static int plpks_secvar_init(void)
> > > > +{
> > > > +       if (!plpks_is_available())
> > > > +               return -ENODEV;
> > > > +
> > > > +       set_secvar_ops(&plpks_secvar_ops);
> > > > +       set_secvar_config_attrs(config_attrs);
> > > > +       return 0;
> > > > +}
> > > > +device_initcall(plpks_secvar_init);
> > > 
> > > That must be a machine_device_initcall(pseries, ...), otherwise
> > > we
> > > will
> > > blow up doing a hcall on powernv in plpks_is_available().
> > 
> > OK, can do.  I don't understand your case of how powernv could hit
> > this, but I think I to have to move plpks_is_available() into
> > include/,
> > so it's going to be even more possible anyway.
> 
> Kernels can be compiled with both pseries and powernv support, in
> which
> case plpks_secvar_init() will be called unconditionally even when
> booting on a powernv machine.
> 
> I can confirm that as it is, booting this on powernv qemu causes a
> panic.

Of course, I'm not sure why I thought an initcall in a platform that
wasn't active would magically not run on other platforms.

> 


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

* Re: [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
  2023-01-06 10:49     ` Michael Ellerman
@ 2023-01-10  3:59       ` Andrew Donnellan
  -1 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-10  3:59 UTC (permalink / raw)
  To: Michael Ellerman, Russell Currey, linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, zohar

On Fri, 2023-01-06 at 21:49 +1100, Michael Ellerman wrote:
> 
> > +static int plpks_get_variable(const char *key, uint64_t key_len,
> > +                             u8 *data, uint64_t *data_size)
> > +{
> > +       struct plpks_var var = {0};
> > +       u16 ucs2_namelen;
> > +       u8 *ucs2_name;
> > +       int rc = 0;
> > +
> > +       ucs2_namelen = get_ucs2name(key, &ucs2_name);
> > +       if (!ucs2_namelen)
> > +               return -ENOMEM;
> > +
> > +       var.name = ucs2_name;
> > +       var.namelen = ucs2_namelen;
> > +       var.os = PLPKS_VAR_LINUX;
> > +       rc = plpks_read_os_var(&var);
> > +
> > +       if (rc)
> > +               goto err;
> > +
> > +       *data_size = var.datalen + sizeof(var.policy);
> > +
> > +       // We can be called with data = NULL to just get the object
> > size.
> > +       if (data) {
> > +               memcpy(data, &var.policy, sizeof(var.policy));
> > +               memcpy(data + sizeof(var.policy), var.data,
> > var.datalen);
> > +       }
> 
> There's a lot of allocation and copying going on. The secvar-sysfs.c
> data_read() has kzalloc'ed data, but only after already doing the
> hcall
> to get the size. Then plpks_read_os_var() does an allocation for the
> hcall and then another allocation of the exact data size. Then
> data_read()
> does another copy into the sysfs supplied buffer.
> 
> So to read a single variable we do the hcall twice, and allocate/copy
> the content of the variable 4 times?
> 
>  - Hypervisor into "output" in plpks_read_var().
>  - "output" into "var->data" in plpks_read_var().
>  - "var.data" into "data" in plpks_get_variable().
>  - "data" into "buf" in data_read().
> 
> As long as maxobjsize is < PAGE_SIZE I think we could do the hcall
> directly into "buf". Maybe we want to avoid writing into "buf"
> directly
> in case the hcall fails or something, but the other 3 copies seem
> unnecessary.

In the general case, I don't like passing buffer pointers straight from
parameters into hcalls, since the address has to be in the linear map,
and that's a detail I'd rather hide from callers. But otherwise, yes I
think we can probably shift to having the caller allocate the buffers.

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
@ 2023-01-10  3:59       ` Andrew Donnellan
  0 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-10  3:59 UTC (permalink / raw)
  To: Michael Ellerman, Russell Currey, linuxppc-dev
  Cc: nayna, gregkh, gcwilson, linux-kernel, zohar

On Fri, 2023-01-06 at 21:49 +1100, Michael Ellerman wrote:
> 
> > +static int plpks_get_variable(const char *key, uint64_t key_len,
> > +                             u8 *data, uint64_t *data_size)
> > +{
> > +       struct plpks_var var = {0};
> > +       u16 ucs2_namelen;
> > +       u8 *ucs2_name;
> > +       int rc = 0;
> > +
> > +       ucs2_namelen = get_ucs2name(key, &ucs2_name);
> > +       if (!ucs2_namelen)
> > +               return -ENOMEM;
> > +
> > +       var.name = ucs2_name;
> > +       var.namelen = ucs2_namelen;
> > +       var.os = PLPKS_VAR_LINUX;
> > +       rc = plpks_read_os_var(&var);
> > +
> > +       if (rc)
> > +               goto err;
> > +
> > +       *data_size = var.datalen + sizeof(var.policy);
> > +
> > +       // We can be called with data = NULL to just get the object
> > size.
> > +       if (data) {
> > +               memcpy(data, &var.policy, sizeof(var.policy));
> > +               memcpy(data + sizeof(var.policy), var.data,
> > var.datalen);
> > +       }
> 
> There's a lot of allocation and copying going on. The secvar-sysfs.c
> data_read() has kzalloc'ed data, but only after already doing the
> hcall
> to get the size. Then plpks_read_os_var() does an allocation for the
> hcall and then another allocation of the exact data size. Then
> data_read()
> does another copy into the sysfs supplied buffer.
> 
> So to read a single variable we do the hcall twice, and allocate/copy
> the content of the variable 4 times?
> 
>  - Hypervisor into "output" in plpks_read_var().
>  - "output" into "var->data" in plpks_read_var().
>  - "var.data" into "data" in plpks_get_variable().
>  - "data" into "buf" in data_read().
> 
> As long as maxobjsize is < PAGE_SIZE I think we could do the hcall
> directly into "buf". Maybe we want to avoid writing into "buf"
> directly
> in case the hcall fails or something, but the other 3 copies seem
> unnecessary.

In the general case, I don't like passing buffer pointers straight from
parameters into hcalls, since the address has to be in the linear map,
and that's a detail I'd rather hide from callers. But otherwise, yes I
think we can probably shift to having the caller allocate the buffers.

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
  2023-01-06 10:49     ` Michael Ellerman
@ 2023-01-11  3:57       ` Andrew Donnellan
  -1 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-11  3:57 UTC (permalink / raw)
  To: Michael Ellerman, Russell Currey, linuxppc-dev
  Cc: gregkh, gcwilson, linux-kernel, nayna, zohar

On Fri, 2023-01-06 at 21:49 +1100, Michael Ellerman wrote:
> 
> > diff --git a/arch/powerpc/platforms/pseries/Kconfig
> > b/arch/powerpc/platforms/pseries/Kconfig
> > index a3b4d99567cb..94e08c405d50 100644
> > --- a/arch/powerpc/platforms/pseries/Kconfig
> > +++ b/arch/powerpc/platforms/pseries/Kconfig
> > @@ -162,6 +162,19 @@ config PSERIES_PLPKS
> >  
> >           If unsure, select N.
> >  
> > +config PSERIES_PLPKS_SECVAR
> > +       depends on PSERIES_PLPKS
> > +       depends on PPC_SECURE_BOOT
> > +       bool "Support for the PLPKS secvar interface"
> > +       help
> > +         PowerVM can support dynamic secure boot with user-defined
> > keys
> > +         through the PLPKS. Keystore objects used in dynamic
> > secure boot
> > +         can be exposed to the kernel and userspace through the
> > powerpc
> > +         secvar infrastructure. Select this to enable the PLPKS
> > backend
> > +         for secvars for use in pseries dynamic secure boot.
> > +
> > +         If unsure, select N.
> 
> I don't think we need that config option at all, or if we do it
> should
> not be user selectable and just enabled automatically by
> PSERIES_PLPKS.

I actually think we should get rid of both PSERIES_PLPKS_SECVAR and
PSERIES_PLPKS, and just use PPC_SECURE_BOOT / PPC_SECVAR_SYSFS.

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

* Re: [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot
@ 2023-01-11  3:57       ` Andrew Donnellan
  0 siblings, 0 replies; 54+ messages in thread
From: Andrew Donnellan @ 2023-01-11  3:57 UTC (permalink / raw)
  To: Michael Ellerman, Russell Currey, linuxppc-dev
  Cc: nayna, gregkh, gcwilson, linux-kernel, zohar

On Fri, 2023-01-06 at 21:49 +1100, Michael Ellerman wrote:
> 
> > diff --git a/arch/powerpc/platforms/pseries/Kconfig
> > b/arch/powerpc/platforms/pseries/Kconfig
> > index a3b4d99567cb..94e08c405d50 100644
> > --- a/arch/powerpc/platforms/pseries/Kconfig
> > +++ b/arch/powerpc/platforms/pseries/Kconfig
> > @@ -162,6 +162,19 @@ config PSERIES_PLPKS
> >  
> >           If unsure, select N.
> >  
> > +config PSERIES_PLPKS_SECVAR
> > +       depends on PSERIES_PLPKS
> > +       depends on PPC_SECURE_BOOT
> > +       bool "Support for the PLPKS secvar interface"
> > +       help
> > +         PowerVM can support dynamic secure boot with user-defined
> > keys
> > +         through the PLPKS. Keystore objects used in dynamic
> > secure boot
> > +         can be exposed to the kernel and userspace through the
> > powerpc
> > +         secvar infrastructure. Select this to enable the PLPKS
> > backend
> > +         for secvars for use in pseries dynamic secure boot.
> > +
> > +         If unsure, select N.
> 
> I don't think we need that config option at all, or if we do it
> should
> not be user selectable and just enabled automatically by
> PSERIES_PLPKS.

I actually think we should get rid of both PSERIES_PLPKS_SECVAR and
PSERIES_PLPKS, and just use PPC_SECURE_BOOT / PPC_SECVAR_SYSFS.

-- 
Andrew Donnellan    OzLabs, ADL Canberra
ajd@linux.ibm.com   IBM Australia Limited

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

end of thread, other threads:[~2023-01-11  3:58 UTC | newest]

Thread overview: 54+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-12-30  4:20 [PATCH v2 0/7] pseries dynamic secure boot interface using secvar Russell Currey
2022-12-30  4:20 ` Russell Currey
2022-12-30  4:20 ` [PATCH v2 1/7] powerpc/pseries: Log hcall return codes for PLPKS debug Russell Currey
2022-12-30  4:20   ` Russell Currey
2023-01-04  4:45   ` Andrew Donnellan
2023-01-04  4:45     ` Andrew Donnellan
2022-12-30  4:20 ` [PATCH v2 2/7] powerpc/secvar: WARN_ON_ONCE() if multiple secvar ops are set Russell Currey
2022-12-30  4:20   ` Russell Currey
2023-01-04  7:10   ` Andrew Donnellan
2023-01-04  7:10     ` Andrew Donnellan
2022-12-30  4:20 ` [PATCH v2 3/7] powerpc/secvar: Use sysfs_emit() instead of sprintf() Russell Currey
2022-12-30  4:20   ` Russell Currey
2023-01-04  7:12   ` Andrew Donnellan
2023-01-04  7:12     ` Andrew Donnellan
2022-12-30  4:20 ` [PATCH v2 4/7] powerpc/secvar: Handle format string in the consumer Russell Currey
2022-12-30  4:20   ` Russell Currey
2023-01-04  7:31   ` Andrew Donnellan
2023-01-04  7:31     ` Andrew Donnellan
2022-12-30  4:20 ` [PATCH v2 5/7] powerpc/secvar: Handle max object size " Russell Currey
2022-12-30  4:20   ` Russell Currey
2023-01-04  7:50   ` Andrew Donnellan
2023-01-04  7:50     ` Andrew Donnellan
2022-12-30  4:20 ` [PATCH v2 6/7] powerpc/secvar: Extend sysfs to include config vars Russell Currey
2022-12-30  4:20   ` Russell Currey
2023-01-05  7:28   ` Andrew Donnellan
2023-01-05  7:28     ` Andrew Donnellan
2023-01-06  6:33     ` Russell Currey
2023-01-06  6:33       ` Russell Currey
2023-01-06  4:15   ` Michael Ellerman
2023-01-06  4:15     ` Michael Ellerman
2023-01-06  6:35     ` Russell Currey
2023-01-06  6:35       ` Russell Currey
2022-12-30  4:20 ` [PATCH v2 7/7] powerpc/pseries: Implement secvars for dynamic secure boot Russell Currey
2022-12-30  4:20   ` Russell Currey
2023-01-05  8:15   ` Andrew Donnellan
2023-01-05  8:15     ` Andrew Donnellan
2023-01-06  6:49     ` Russell Currey
2023-01-06  6:49       ` Russell Currey
2023-01-09  4:42       ` Andrew Donnellan
2023-01-09  4:42         ` Andrew Donnellan
2023-01-06 10:49   ` Michael Ellerman
2023-01-06 10:49     ` Michael Ellerman
2023-01-09  3:33     ` Andrew Donnellan
2023-01-09  3:33       ` Andrew Donnellan
2023-01-09  3:34     ` Russell Currey
2023-01-09  3:34       ` Russell Currey
2023-01-09  5:20       ` Andrew Donnellan
2023-01-09  5:20         ` Andrew Donnellan
2023-01-10  1:27         ` Russell Currey
2023-01-10  1:27           ` Russell Currey
2023-01-10  3:59     ` Andrew Donnellan
2023-01-10  3:59       ` Andrew Donnellan
2023-01-11  3:57     ` Andrew Donnellan
2023-01-11  3:57       ` Andrew Donnellan

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.