162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci// Secure variable implementation using the PowerVM LPAR Platform KeyStore (PLPKS)
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright 2022, 2023 IBM Corporation
662306a36Sopenharmony_ci// Authors: Russell Currey
762306a36Sopenharmony_ci//          Andrew Donnellan
862306a36Sopenharmony_ci//          Nayna Jain
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define pr_fmt(fmt) "secvar: "fmt
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/printk.h>
1362306a36Sopenharmony_ci#include <linux/init.h>
1462306a36Sopenharmony_ci#include <linux/types.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/string.h>
1762306a36Sopenharmony_ci#include <linux/kobject.h>
1862306a36Sopenharmony_ci#include <linux/nls.h>
1962306a36Sopenharmony_ci#include <asm/machdep.h>
2062306a36Sopenharmony_ci#include <asm/secvar.h>
2162306a36Sopenharmony_ci#include <asm/plpks.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci// Config attributes for sysfs
2462306a36Sopenharmony_ci#define PLPKS_CONFIG_ATTR(name, fmt, func)			\
2562306a36Sopenharmony_ci	static ssize_t name##_show(struct kobject *kobj,	\
2662306a36Sopenharmony_ci				   struct kobj_attribute *attr,	\
2762306a36Sopenharmony_ci				   char *buf)			\
2862306a36Sopenharmony_ci	{							\
2962306a36Sopenharmony_ci		return sysfs_emit(buf, fmt, func());		\
3062306a36Sopenharmony_ci	}							\
3162306a36Sopenharmony_ci	static struct kobj_attribute attr_##name = __ATTR_RO(name)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ciPLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version);
3462306a36Sopenharmony_ciPLPKS_CONFIG_ATTR(max_object_size, "%u\n", plpks_get_maxobjectsize);
3562306a36Sopenharmony_ciPLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize);
3662306a36Sopenharmony_ciPLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);
3762306a36Sopenharmony_ciPLPKS_CONFIG_ATTR(supported_policies, "%08x\n", plpks_get_supportedpolicies);
3862306a36Sopenharmony_ciPLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n", plpks_get_signedupdatealgorithms);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic const struct attribute *config_attrs[] = {
4162306a36Sopenharmony_ci	&attr_version.attr,
4262306a36Sopenharmony_ci	&attr_max_object_size.attr,
4362306a36Sopenharmony_ci	&attr_total_size.attr,
4462306a36Sopenharmony_ci	&attr_used_space.attr,
4562306a36Sopenharmony_ci	&attr_supported_policies.attr,
4662306a36Sopenharmony_ci	&attr_signed_update_algorithms.attr,
4762306a36Sopenharmony_ci	NULL,
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic u32 get_policy(const char *name)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	if ((strcmp(name, "db") == 0) ||
5362306a36Sopenharmony_ci	    (strcmp(name, "dbx") == 0) ||
5462306a36Sopenharmony_ci	    (strcmp(name, "grubdb") == 0) ||
5562306a36Sopenharmony_ci	    (strcmp(name, "grubdbx") == 0) ||
5662306a36Sopenharmony_ci	    (strcmp(name, "sbat") == 0))
5762306a36Sopenharmony_ci		return (PLPKS_WORLDREADABLE | PLPKS_SIGNEDUPDATE);
5862306a36Sopenharmony_ci	else
5962306a36Sopenharmony_ci		return PLPKS_SIGNEDUPDATE;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic const char * const plpks_var_names[] = {
6362306a36Sopenharmony_ci	"PK",
6462306a36Sopenharmony_ci	"KEK",
6562306a36Sopenharmony_ci	"db",
6662306a36Sopenharmony_ci	"dbx",
6762306a36Sopenharmony_ci	"grubdb",
6862306a36Sopenharmony_ci	"grubdbx",
6962306a36Sopenharmony_ci	"sbat",
7062306a36Sopenharmony_ci	"moduledb",
7162306a36Sopenharmony_ci	"trustedcadb",
7262306a36Sopenharmony_ci	NULL,
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int plpks_get_variable(const char *key, u64 key_len, u8 *data,
7662306a36Sopenharmony_ci			      u64 *data_size)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct plpks_var var = {0};
7962306a36Sopenharmony_ci	int rc = 0;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	// We subtract 1 from key_len because we don't need to include the
8262306a36Sopenharmony_ci	// null terminator at the end of the string
8362306a36Sopenharmony_ci	var.name = kcalloc(key_len - 1, sizeof(wchar_t), GFP_KERNEL);
8462306a36Sopenharmony_ci	if (!var.name)
8562306a36Sopenharmony_ci		return -ENOMEM;
8662306a36Sopenharmony_ci	rc = utf8s_to_utf16s(key, key_len - 1, UTF16_LITTLE_ENDIAN, (wchar_t *)var.name,
8762306a36Sopenharmony_ci			     key_len - 1);
8862306a36Sopenharmony_ci	if (rc < 0)
8962306a36Sopenharmony_ci		goto err;
9062306a36Sopenharmony_ci	var.namelen = rc * 2;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	var.os = PLPKS_VAR_LINUX;
9362306a36Sopenharmony_ci	if (data) {
9462306a36Sopenharmony_ci		var.data = data;
9562306a36Sopenharmony_ci		var.datalen = *data_size;
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci	rc = plpks_read_os_var(&var);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (rc)
10062306a36Sopenharmony_ci		goto err;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	*data_size = var.datalen;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cierr:
10562306a36Sopenharmony_ci	kfree(var.name);
10662306a36Sopenharmony_ci	if (rc && rc != -ENOENT) {
10762306a36Sopenharmony_ci		pr_err("Failed to read variable '%s': %d\n", key, rc);
10862306a36Sopenharmony_ci		// Return -EIO since userspace probably doesn't care about the
10962306a36Sopenharmony_ci		// specific error
11062306a36Sopenharmony_ci		rc = -EIO;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci	return rc;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic int plpks_set_variable(const char *key, u64 key_len, u8 *data,
11662306a36Sopenharmony_ci			      u64 data_size)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct plpks_var var = {0};
11962306a36Sopenharmony_ci	int rc = 0;
12062306a36Sopenharmony_ci	u64 flags;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	// Secure variables need to be prefixed with 8 bytes of flags.
12362306a36Sopenharmony_ci	// We only want to perform the write if we have at least one byte of data.
12462306a36Sopenharmony_ci	if (data_size <= sizeof(flags))
12562306a36Sopenharmony_ci		return -EINVAL;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	// We subtract 1 from key_len because we don't need to include the
12862306a36Sopenharmony_ci	// null terminator at the end of the string
12962306a36Sopenharmony_ci	var.name = kcalloc(key_len - 1, sizeof(wchar_t), GFP_KERNEL);
13062306a36Sopenharmony_ci	if (!var.name)
13162306a36Sopenharmony_ci		return -ENOMEM;
13262306a36Sopenharmony_ci	rc = utf8s_to_utf16s(key, key_len - 1, UTF16_LITTLE_ENDIAN, (wchar_t *)var.name,
13362306a36Sopenharmony_ci			     key_len - 1);
13462306a36Sopenharmony_ci	if (rc < 0)
13562306a36Sopenharmony_ci		goto err;
13662306a36Sopenharmony_ci	var.namelen = rc * 2;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	// Flags are contained in the first 8 bytes of the buffer, and are always big-endian
13962306a36Sopenharmony_ci	flags = be64_to_cpup((__be64 *)data);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	var.datalen = data_size - sizeof(flags);
14262306a36Sopenharmony_ci	var.data = data + sizeof(flags);
14362306a36Sopenharmony_ci	var.os = PLPKS_VAR_LINUX;
14462306a36Sopenharmony_ci	var.policy = get_policy(key);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	// Unlike in the read case, the plpks error code can be useful to
14762306a36Sopenharmony_ci	// userspace on write, so we return it rather than just -EIO
14862306a36Sopenharmony_ci	rc = plpks_signed_update_var(&var, flags);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cierr:
15162306a36Sopenharmony_ci	kfree(var.name);
15262306a36Sopenharmony_ci	return rc;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci// PLPKS dynamic secure boot doesn't give us a format string in the same way OPAL does.
15662306a36Sopenharmony_ci// Instead, report the format using the SB_VERSION variable in the keystore.
15762306a36Sopenharmony_ci// The string is made up by us, and takes the form "ibm,plpks-sb-v<n>" (or "ibm,plpks-sb-unknown"
15862306a36Sopenharmony_ci// if the SB_VERSION variable doesn't exist). Hypervisor defines the SB_VERSION variable as a
15962306a36Sopenharmony_ci// "1 byte unsigned integer value".
16062306a36Sopenharmony_cistatic ssize_t plpks_secvar_format(char *buf, size_t bufsize)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct plpks_var var = {0};
16362306a36Sopenharmony_ci	ssize_t ret;
16462306a36Sopenharmony_ci	u8 version;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	var.component = NULL;
16762306a36Sopenharmony_ci	// Only the signed variables have null bytes in their names, this one doesn't
16862306a36Sopenharmony_ci	var.name = "SB_VERSION";
16962306a36Sopenharmony_ci	var.namelen = strlen(var.name);
17062306a36Sopenharmony_ci	var.datalen = 1;
17162306a36Sopenharmony_ci	var.data = &version;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	// Unlike the other vars, SB_VERSION is owned by firmware instead of the OS
17462306a36Sopenharmony_ci	ret = plpks_read_fw_var(&var);
17562306a36Sopenharmony_ci	if (ret) {
17662306a36Sopenharmony_ci		if (ret == -ENOENT) {
17762306a36Sopenharmony_ci			ret = snprintf(buf, bufsize, "ibm,plpks-sb-unknown");
17862306a36Sopenharmony_ci		} else {
17962306a36Sopenharmony_ci			pr_err("Error %ld reading SB_VERSION from firmware\n", ret);
18062306a36Sopenharmony_ci			ret = -EIO;
18162306a36Sopenharmony_ci		}
18262306a36Sopenharmony_ci		goto err;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	ret = snprintf(buf, bufsize, "ibm,plpks-sb-v%hhu", version);
18662306a36Sopenharmony_cierr:
18762306a36Sopenharmony_ci	return ret;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic int plpks_max_size(u64 *max_size)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	// The max object size reported by the hypervisor is accurate for the
19362306a36Sopenharmony_ci	// object itself, but we use the first 8 bytes of data on write as the
19462306a36Sopenharmony_ci	// signed update flags, so the max size a user can write is larger.
19562306a36Sopenharmony_ci	*max_size = (u64)plpks_get_maxobjectsize() + sizeof(u64);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return 0;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic const struct secvar_operations plpks_secvar_ops = {
20262306a36Sopenharmony_ci	.get = plpks_get_variable,
20362306a36Sopenharmony_ci	.set = plpks_set_variable,
20462306a36Sopenharmony_ci	.format = plpks_secvar_format,
20562306a36Sopenharmony_ci	.max_size = plpks_max_size,
20662306a36Sopenharmony_ci	.config_attrs = config_attrs,
20762306a36Sopenharmony_ci	.var_names = plpks_var_names,
20862306a36Sopenharmony_ci};
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic int plpks_secvar_init(void)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	if (!plpks_is_available())
21362306a36Sopenharmony_ci		return -ENODEV;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	return set_secvar_ops(&plpks_secvar_ops);
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_cimachine_device_initcall(pseries, plpks_secvar_init);
218