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