1// SPDX-License-Identifier: GPL-2.0-only
2
3// Secure variable implementation using the PowerVM LPAR Platform KeyStore (PLPKS)
4//
5// Copyright 2022, 2023 IBM Corporation
6// Authors: Russell Currey
7//          Andrew Donnellan
8//          Nayna Jain
9
10#define pr_fmt(fmt) "secvar: "fmt
11
12#include <linux/printk.h>
13#include <linux/init.h>
14#include <linux/types.h>
15#include <linux/slab.h>
16#include <linux/string.h>
17#include <linux/kobject.h>
18#include <linux/nls.h>
19#include <asm/machdep.h>
20#include <asm/secvar.h>
21#include <asm/plpks.h>
22
23// Config attributes for sysfs
24#define PLPKS_CONFIG_ATTR(name, fmt, func)			\
25	static ssize_t name##_show(struct kobject *kobj,	\
26				   struct kobj_attribute *attr,	\
27				   char *buf)			\
28	{							\
29		return sysfs_emit(buf, fmt, func());		\
30	}							\
31	static struct kobj_attribute attr_##name = __ATTR_RO(name)
32
33PLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version);
34PLPKS_CONFIG_ATTR(max_object_size, "%u\n", plpks_get_maxobjectsize);
35PLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize);
36PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);
37PLPKS_CONFIG_ATTR(supported_policies, "%08x\n", plpks_get_supportedpolicies);
38PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n", plpks_get_signedupdatealgorithms);
39
40static const struct attribute *config_attrs[] = {
41	&attr_version.attr,
42	&attr_max_object_size.attr,
43	&attr_total_size.attr,
44	&attr_used_space.attr,
45	&attr_supported_policies.attr,
46	&attr_signed_update_algorithms.attr,
47	NULL,
48};
49
50static u32 get_policy(const char *name)
51{
52	if ((strcmp(name, "db") == 0) ||
53	    (strcmp(name, "dbx") == 0) ||
54	    (strcmp(name, "grubdb") == 0) ||
55	    (strcmp(name, "grubdbx") == 0) ||
56	    (strcmp(name, "sbat") == 0))
57		return (PLPKS_WORLDREADABLE | PLPKS_SIGNEDUPDATE);
58	else
59		return PLPKS_SIGNEDUPDATE;
60}
61
62static const char * const plpks_var_names[] = {
63	"PK",
64	"KEK",
65	"db",
66	"dbx",
67	"grubdb",
68	"grubdbx",
69	"sbat",
70	"moduledb",
71	"trustedcadb",
72	NULL,
73};
74
75static int plpks_get_variable(const char *key, u64 key_len, u8 *data,
76			      u64 *data_size)
77{
78	struct plpks_var var = {0};
79	int rc = 0;
80
81	// We subtract 1 from key_len because we don't need to include the
82	// null terminator at the end of the string
83	var.name = kcalloc(key_len - 1, sizeof(wchar_t), GFP_KERNEL);
84	if (!var.name)
85		return -ENOMEM;
86	rc = utf8s_to_utf16s(key, key_len - 1, UTF16_LITTLE_ENDIAN, (wchar_t *)var.name,
87			     key_len - 1);
88	if (rc < 0)
89		goto err;
90	var.namelen = rc * 2;
91
92	var.os = PLPKS_VAR_LINUX;
93	if (data) {
94		var.data = data;
95		var.datalen = *data_size;
96	}
97	rc = plpks_read_os_var(&var);
98
99	if (rc)
100		goto err;
101
102	*data_size = var.datalen;
103
104err:
105	kfree(var.name);
106	if (rc && rc != -ENOENT) {
107		pr_err("Failed to read variable '%s': %d\n", key, rc);
108		// Return -EIO since userspace probably doesn't care about the
109		// specific error
110		rc = -EIO;
111	}
112	return rc;
113}
114
115static int plpks_set_variable(const char *key, u64 key_len, u8 *data,
116			      u64 data_size)
117{
118	struct plpks_var var = {0};
119	int rc = 0;
120	u64 flags;
121
122	// Secure variables need to be prefixed with 8 bytes of flags.
123	// We only want to perform the write if we have at least one byte of data.
124	if (data_size <= sizeof(flags))
125		return -EINVAL;
126
127	// We subtract 1 from key_len because we don't need to include the
128	// null terminator at the end of the string
129	var.name = kcalloc(key_len - 1, sizeof(wchar_t), GFP_KERNEL);
130	if (!var.name)
131		return -ENOMEM;
132	rc = utf8s_to_utf16s(key, key_len - 1, UTF16_LITTLE_ENDIAN, (wchar_t *)var.name,
133			     key_len - 1);
134	if (rc < 0)
135		goto err;
136	var.namelen = rc * 2;
137
138	// Flags are contained in the first 8 bytes of the buffer, and are always big-endian
139	flags = be64_to_cpup((__be64 *)data);
140
141	var.datalen = data_size - sizeof(flags);
142	var.data = data + sizeof(flags);
143	var.os = PLPKS_VAR_LINUX;
144	var.policy = get_policy(key);
145
146	// Unlike in the read case, the plpks error code can be useful to
147	// userspace on write, so we return it rather than just -EIO
148	rc = plpks_signed_update_var(&var, flags);
149
150err:
151	kfree(var.name);
152	return rc;
153}
154
155// PLPKS dynamic secure boot doesn't give us a format string in the same way OPAL does.
156// Instead, report the format using the SB_VERSION variable in the keystore.
157// The string is made up by us, and takes the form "ibm,plpks-sb-v<n>" (or "ibm,plpks-sb-unknown"
158// if the SB_VERSION variable doesn't exist). Hypervisor defines the SB_VERSION variable as a
159// "1 byte unsigned integer value".
160static ssize_t plpks_secvar_format(char *buf, size_t bufsize)
161{
162	struct plpks_var var = {0};
163	ssize_t ret;
164	u8 version;
165
166	var.component = NULL;
167	// Only the signed variables have null bytes in their names, this one doesn't
168	var.name = "SB_VERSION";
169	var.namelen = strlen(var.name);
170	var.datalen = 1;
171	var.data = &version;
172
173	// Unlike the other vars, SB_VERSION is owned by firmware instead of the OS
174	ret = plpks_read_fw_var(&var);
175	if (ret) {
176		if (ret == -ENOENT) {
177			ret = snprintf(buf, bufsize, "ibm,plpks-sb-unknown");
178		} else {
179			pr_err("Error %ld reading SB_VERSION from firmware\n", ret);
180			ret = -EIO;
181		}
182		goto err;
183	}
184
185	ret = snprintf(buf, bufsize, "ibm,plpks-sb-v%hhu", version);
186err:
187	return ret;
188}
189
190static int plpks_max_size(u64 *max_size)
191{
192	// The max object size reported by the hypervisor is accurate for the
193	// object itself, but we use the first 8 bytes of data on write as the
194	// signed update flags, so the max size a user can write is larger.
195	*max_size = (u64)plpks_get_maxobjectsize() + sizeof(u64);
196
197	return 0;
198}
199
200
201static const struct secvar_operations plpks_secvar_ops = {
202	.get = plpks_get_variable,
203	.set = plpks_set_variable,
204	.format = plpks_secvar_format,
205	.max_size = plpks_max_size,
206	.config_attrs = config_attrs,
207	.var_names = plpks_var_names,
208};
209
210static int plpks_secvar_init(void)
211{
212	if (!plpks_is_available())
213		return -ENODEV;
214
215	return set_secvar_ops(&plpks_secvar_ops);
216}
217machine_device_initcall(pseries, plpks_secvar_init);
218