blob: 518b9c37ec22064a39666568ac334bcb28057606 [file] [log] [blame]
Joel Stanleya1fccbf2020-06-23 17:25:56 +09301From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2From: Nayna Jain <nayna@linux.ibm.com>
3Date: Sun, 10 Nov 2019 21:10:34 -0600
Joel Stanleycb9bf572020-09-29 16:18:12 +09304Subject: [PATCH 11/19] powerpc: expose secure variables to userspace via sysfs
Joel Stanleya1fccbf2020-06-23 17:25:56 +09305
6PowerNV secure variables, which store the keys used for OS kernel
7verification, are managed by the firmware. These secure variables need to
8be accessed by the userspace for addition/deletion of the certificates.
9
10This patch adds the sysfs interface to expose secure variables for PowerNV
11secureboot. The users shall use this interface for manipulating
12the keys stored in the secure variables.
13
14Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
15Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
16Signed-off-by: Eric Richter <erichte@linux.ibm.com>
17Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
18Link: https://lore.kernel.org/r/1573441836-3632-3-git-send-email-nayna@linux.ibm.com
19(cherry picked from commit bd5d9c743d38f67d64ea1b512a461f6b5a5f6bec)
20Signed-off-by: Joel Stanley <joel@jms.id.au>
21---
22 Documentation/ABI/testing/sysfs-secvar | 46 +++++
23 arch/powerpc/Kconfig | 11 ++
24 arch/powerpc/kernel/Makefile | 1 +
25 arch/powerpc/kernel/secvar-sysfs.c | 248 +++++++++++++++++++++++++
26 4 files changed, 306 insertions(+)
27 create mode 100644 Documentation/ABI/testing/sysfs-secvar
28 create mode 100644 arch/powerpc/kernel/secvar-sysfs.c
29
30diff --git a/Documentation/ABI/testing/sysfs-secvar b/Documentation/ABI/testing/sysfs-secvar
31new file mode 100644
32index 000000000000..feebb8c57294
33--- /dev/null
34+++ b/Documentation/ABI/testing/sysfs-secvar
35@@ -0,0 +1,46 @@
36+What: /sys/firmware/secvar
37+Date: August 2019
38+Contact: Nayna Jain <nayna@linux.ibm.com>
39+Description: This directory is created if the POWER firmware supports OS
40+ secureboot, thereby secure variables. It exposes interface
41+ for reading/writing the secure variables
42+
43+What: /sys/firmware/secvar/vars
44+Date: August 2019
45+Contact: Nayna Jain <nayna@linux.ibm.com>
46+Description: This directory lists all the secure variables that are supported
47+ by the firmware.
48+
49+What: /sys/firmware/secvar/format
50+Date: August 2019
51+Contact: Nayna Jain <nayna@linux.ibm.com>
52+Description: A string indicating which backend is in use by the firmware.
53+ This determines the format of the variable and the accepted
54+ format of variable updates.
55+
56+What: /sys/firmware/secvar/vars/<variable name>
57+Date: August 2019
58+Contact: Nayna Jain <nayna@linux.ibm.com>
59+Description: Each secure variable is represented as a directory named as
60+ <variable_name>. The variable name is unique and is in ASCII
61+ representation. The data and size can be determined by reading
62+ their respective attribute files.
63+
64+What: /sys/firmware/secvar/vars/<variable_name>/size
65+Date: August 2019
66+Contact: Nayna Jain <nayna@linux.ibm.com>
67+Description: An integer representation of the size of the content of the
68+ variable. In other words, it represents the size of the data.
69+
70+What: /sys/firmware/secvar/vars/<variable_name>/data
71+Date: August 2019
72+Contact: Nayna Jain h<nayna@linux.ibm.com>
73+Description: A read-only file containing the value of the variable. The size
74+ of the file represents the maximum size of the variable data.
75+
76+What: /sys/firmware/secvar/vars/<variable_name>/update
77+Date: August 2019
78+Contact: Nayna Jain <nayna@linux.ibm.com>
79+Description: A write-only file that is used to submit the new value for the
80+ variable. The size of the file represents the maximum size of
81+ the variable data that can be written.
82diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
Joel Stanleycb9bf572020-09-29 16:18:12 +093083index 32ce6c0b43f1..cc6cdf821604 100644
Joel Stanleya1fccbf2020-06-23 17:25:56 +093084--- a/arch/powerpc/Kconfig
85+++ b/arch/powerpc/Kconfig
Joel Stanleycb9bf572020-09-29 16:18:12 +093086@@ -946,6 +946,17 @@ config PPC_SECURE_BOOT
Joel Stanleya1fccbf2020-06-23 17:25:56 +093087 to enable OS secure boot on systems that have firmware support for
88 it. If in doubt say N.
89
90+config PPC_SECVAR_SYSFS
91+ bool "Enable sysfs interface for POWER secure variables"
92+ default y
93+ depends on PPC_SECURE_BOOT
94+ depends on SYSFS
95+ help
96+ POWER secure variables are managed and controlled by firmware.
97+ These variables are exposed to userspace via sysfs to enable
98+ read/write operations on these variables. Say Y if you have
99+ secure boot enabled and want to expose variables to userspace.
100+
101 endmenu
102
103 config ISA_DMA_API
104diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
105index 93b0336090f2..b97c018a2f53 100644
106--- a/arch/powerpc/kernel/Makefile
107+++ b/arch/powerpc/kernel/Makefile
108@@ -159,6 +159,7 @@ obj-y += ucall.o
109 endif
110
111 obj-$(CONFIG_PPC_SECURE_BOOT) += secure_boot.o ima_arch.o secvar-ops.o
112+obj-$(CONFIG_PPC_SECVAR_SYSFS) += secvar-sysfs.o
113
114 # Disable GCOV, KCOV & sanitizers in odd or sensitive code
115 GCOV_PROFILE_prom_init.o := n
116diff --git a/arch/powerpc/kernel/secvar-sysfs.c b/arch/powerpc/kernel/secvar-sysfs.c
117new file mode 100644
118index 000000000000..a0a78aba2083
119--- /dev/null
120+++ b/arch/powerpc/kernel/secvar-sysfs.c
121@@ -0,0 +1,248 @@
122+// SPDX-License-Identifier: GPL-2.0+
123+/*
124+ * Copyright (C) 2019 IBM Corporation <nayna@linux.ibm.com>
125+ *
126+ * This code exposes secure variables to user via sysfs
127+ */
128+
129+#define pr_fmt(fmt) "secvar-sysfs: "fmt
130+
131+#include <linux/slab.h>
132+#include <linux/compat.h>
133+#include <linux/string.h>
134+#include <linux/of.h>
135+#include <asm/secvar.h>
136+
137+#define NAME_MAX_SIZE 1024
138+
139+static struct kobject *secvar_kobj;
140+static struct kset *secvar_kset;
141+
142+static ssize_t format_show(struct kobject *kobj, struct kobj_attribute *attr,
143+ char *buf)
144+{
145+ ssize_t rc = 0;
146+ struct device_node *node;
147+ const char *format;
148+
149+ node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
150+ if (!of_device_is_available(node))
151+ return -ENODEV;
152+
153+ rc = of_property_read_string(node, "format", &format);
154+ if (rc)
155+ return rc;
156+
157+ rc = sprintf(buf, "%s\n", format);
158+
159+ of_node_put(node);
160+
161+ return rc;
162+}
163+
164+
165+static ssize_t size_show(struct kobject *kobj, struct kobj_attribute *attr,
166+ char *buf)
167+{
168+ uint64_t dsize;
169+ int rc;
170+
171+ rc = secvar_ops->get(kobj->name, strlen(kobj->name) + 1, NULL, &dsize);
172+ if (rc) {
173+ pr_err("Error retrieving %s variable size %d\n", kobj->name,
174+ rc);
175+ return rc;
176+ }
177+
178+ return sprintf(buf, "%llu\n", dsize);
179+}
180+
181+static ssize_t data_read(struct file *filep, struct kobject *kobj,
182+ struct bin_attribute *attr, char *buf, loff_t off,
183+ size_t count)
184+{
185+ uint64_t dsize;
186+ char *data;
187+ int rc;
188+
189+ rc = secvar_ops->get(kobj->name, strlen(kobj->name) + 1, NULL, &dsize);
190+ if (rc) {
191+ pr_err("Error getting %s variable size %d\n", kobj->name, rc);
192+ return rc;
193+ }
194+ pr_debug("dsize is %llu\n", dsize);
195+
196+ data = kzalloc(dsize, GFP_KERNEL);
197+ if (!data)
198+ return -ENOMEM;
199+
200+ rc = secvar_ops->get(kobj->name, strlen(kobj->name) + 1, data, &dsize);
201+ if (rc) {
202+ pr_err("Error getting %s variable %d\n", kobj->name, rc);
203+ goto data_fail;
204+ }
205+
206+ rc = memory_read_from_buffer(buf, count, &off, data, dsize);
207+
208+data_fail:
209+ kfree(data);
210+ return rc;
211+}
212+
213+static ssize_t update_write(struct file *filep, struct kobject *kobj,
214+ struct bin_attribute *attr, char *buf, loff_t off,
215+ size_t count)
216+{
217+ int rc;
218+
219+ pr_debug("count is %ld\n", count);
220+ rc = secvar_ops->set(kobj->name, strlen(kobj->name) + 1, buf, count);
221+ if (rc) {
222+ pr_err("Error setting the %s variable %d\n", kobj->name, rc);
223+ return rc;
224+ }
225+
226+ return count;
227+}
228+
229+static struct kobj_attribute format_attr = __ATTR_RO(format);
230+
231+static struct kobj_attribute size_attr = __ATTR_RO(size);
232+
233+static struct bin_attribute data_attr = __BIN_ATTR_RO(data, 0);
234+
235+static struct bin_attribute update_attr = __BIN_ATTR_WO(update, 0);
236+
237+static struct bin_attribute *secvar_bin_attrs[] = {
238+ &data_attr,
239+ &update_attr,
240+ NULL,
241+};
242+
243+static struct attribute *secvar_attrs[] = {
244+ &size_attr.attr,
245+ NULL,
246+};
247+
248+static const struct attribute_group secvar_attr_group = {
249+ .attrs = secvar_attrs,
250+ .bin_attrs = secvar_bin_attrs,
251+};
252+__ATTRIBUTE_GROUPS(secvar_attr);
253+
254+static struct kobj_type secvar_ktype = {
255+ .sysfs_ops = &kobj_sysfs_ops,
256+ .default_groups = secvar_attr_groups,
257+};
258+
259+static int update_kobj_size(void)
260+{
261+
262+ struct device_node *node;
263+ u64 varsize;
264+ int rc = 0;
265+
266+ node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
267+ if (!of_device_is_available(node)) {
268+ rc = -ENODEV;
269+ goto out;
270+ }
271+
272+ rc = of_property_read_u64(node, "max-var-size", &varsize);
273+ if (rc)
274+ goto out;
275+
276+ data_attr.size = varsize;
277+ update_attr.size = varsize;
278+
279+out:
280+ of_node_put(node);
281+
282+ return rc;
283+}
284+
285+static int secvar_sysfs_load(void)
286+{
287+ char *name;
288+ uint64_t namesize = 0;
289+ struct kobject *kobj;
290+ int rc;
291+
292+ name = kzalloc(NAME_MAX_SIZE, GFP_KERNEL);
293+ if (!name)
294+ return -ENOMEM;
295+
296+ do {
297+ rc = secvar_ops->get_next(name, &namesize, NAME_MAX_SIZE);
298+ if (rc) {
299+ if (rc != -ENOENT)
300+ pr_err("error getting secvar from firmware %d\n",
301+ rc);
302+ break;
303+ }
304+
305+ kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
306+ if (!kobj) {
307+ rc = -ENOMEM;
308+ break;
309+ }
310+
311+ kobject_init(kobj, &secvar_ktype);
312+
313+ rc = kobject_add(kobj, &secvar_kset->kobj, "%s", name);
314+ if (rc) {
315+ pr_warn("kobject_add error %d for attribute: %s\n", rc,
316+ name);
317+ kobject_put(kobj);
318+ kobj = NULL;
319+ }
320+
321+ if (kobj)
322+ kobject_uevent(kobj, KOBJ_ADD);
323+
324+ } while (!rc);
325+
326+ kfree(name);
327+ return rc;
328+}
329+
330+static int secvar_sysfs_init(void)
331+{
332+ int rc;
333+
334+ if (!secvar_ops) {
335+ pr_warn("secvar: failed to retrieve secvar operations.\n");
336+ return -ENODEV;
337+ }
338+
339+ secvar_kobj = kobject_create_and_add("secvar", firmware_kobj);
340+ if (!secvar_kobj) {
341+ pr_err("secvar: Failed to create firmware kobj\n");
342+ return -ENOMEM;
343+ }
344+
345+ rc = sysfs_create_file(secvar_kobj, &format_attr.attr);
346+ if (rc) {
347+ kobject_put(secvar_kobj);
348+ return -ENOMEM;
349+ }
350+
351+ secvar_kset = kset_create_and_add("vars", NULL, secvar_kobj);
352+ if (!secvar_kset) {
353+ pr_err("secvar: sysfs kobject registration failed.\n");
354+ kobject_put(secvar_kobj);
355+ return -ENOMEM;
356+ }
357+
358+ rc = update_kobj_size();
359+ if (rc) {
360+ pr_err("Cannot read the size of the attribute\n");
361+ return rc;
362+ }
363+
364+ secvar_sysfs_load();
365+
366+ return 0;
367+}
368+
369+late_initcall(secvar_sysfs_init);