Linux-libre 5.7.3-gnu
[librecmc/linux-libre.git] / arch / powerpc / kernel / secvar-sysfs.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2019 IBM Corporation <nayna@linux.ibm.com>
4  *
5  * This code exposes secure variables to user via sysfs
6  */
7
8 #define pr_fmt(fmt) "secvar-sysfs: "fmt
9
10 #include <linux/slab.h>
11 #include <linux/compat.h>
12 #include <linux/string.h>
13 #include <linux/of.h>
14 #include <asm/secvar.h>
15
16 #define NAME_MAX_SIZE      1024
17
18 static struct kobject *secvar_kobj;
19 static struct kset *secvar_kset;
20
21 static ssize_t format_show(struct kobject *kobj, struct kobj_attribute *attr,
22                            char *buf)
23 {
24         ssize_t rc = 0;
25         struct device_node *node;
26         const char *format;
27
28         node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
29         if (!of_device_is_available(node))
30                 return -ENODEV;
31
32         rc = of_property_read_string(node, "format", &format);
33         if (rc)
34                 return rc;
35
36         rc = sprintf(buf, "%s\n", format);
37
38         of_node_put(node);
39
40         return rc;
41 }
42
43
44 static ssize_t size_show(struct kobject *kobj, struct kobj_attribute *attr,
45                          char *buf)
46 {
47         uint64_t dsize;
48         int rc;
49
50         rc = secvar_ops->get(kobj->name, strlen(kobj->name) + 1, NULL, &dsize);
51         if (rc) {
52                 pr_err("Error retrieving %s variable size %d\n", kobj->name,
53                        rc);
54                 return rc;
55         }
56
57         return sprintf(buf, "%llu\n", dsize);
58 }
59
60 static ssize_t data_read(struct file *filep, struct kobject *kobj,
61                          struct bin_attribute *attr, char *buf, loff_t off,
62                          size_t count)
63 {
64         uint64_t dsize;
65         char *data;
66         int rc;
67
68         rc = secvar_ops->get(kobj->name, strlen(kobj->name) + 1, NULL, &dsize);
69         if (rc) {
70                 pr_err("Error getting %s variable size %d\n", kobj->name, rc);
71                 return rc;
72         }
73         pr_debug("dsize is %llu\n", dsize);
74
75         data = kzalloc(dsize, GFP_KERNEL);
76         if (!data)
77                 return -ENOMEM;
78
79         rc = secvar_ops->get(kobj->name, strlen(kobj->name) + 1, data, &dsize);
80         if (rc) {
81                 pr_err("Error getting %s variable %d\n", kobj->name, rc);
82                 goto data_fail;
83         }
84
85         rc = memory_read_from_buffer(buf, count, &off, data, dsize);
86
87 data_fail:
88         kfree(data);
89         return rc;
90 }
91
92 static ssize_t update_write(struct file *filep, struct kobject *kobj,
93                             struct bin_attribute *attr, char *buf, loff_t off,
94                             size_t count)
95 {
96         int rc;
97
98         pr_debug("count is %ld\n", count);
99         rc = secvar_ops->set(kobj->name, strlen(kobj->name) + 1, buf, count);
100         if (rc) {
101                 pr_err("Error setting the %s variable %d\n", kobj->name, rc);
102                 return rc;
103         }
104
105         return count;
106 }
107
108 static struct kobj_attribute format_attr = __ATTR_RO(format);
109
110 static struct kobj_attribute size_attr = __ATTR_RO(size);
111
112 static struct bin_attribute data_attr = __BIN_ATTR_RO(data, 0);
113
114 static struct bin_attribute update_attr = __BIN_ATTR_WO(update, 0);
115
116 static struct bin_attribute *secvar_bin_attrs[] = {
117         &data_attr,
118         &update_attr,
119         NULL,
120 };
121
122 static struct attribute *secvar_attrs[] = {
123         &size_attr.attr,
124         NULL,
125 };
126
127 static const struct attribute_group secvar_attr_group = {
128         .attrs = secvar_attrs,
129         .bin_attrs = secvar_bin_attrs,
130 };
131 __ATTRIBUTE_GROUPS(secvar_attr);
132
133 static struct kobj_type secvar_ktype = {
134         .sysfs_ops      = &kobj_sysfs_ops,
135         .default_groups = secvar_attr_groups,
136 };
137
138 static int update_kobj_size(void)
139 {
140
141         struct device_node *node;
142         u64 varsize;
143         int rc = 0;
144
145         node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
146         if (!of_device_is_available(node)) {
147                 rc = -ENODEV;
148                 goto out;
149         }
150
151         rc = of_property_read_u64(node, "max-var-size", &varsize);
152         if (rc)
153                 goto out;
154
155         data_attr.size = varsize;
156         update_attr.size = varsize;
157
158 out:
159         of_node_put(node);
160
161         return rc;
162 }
163
164 static int secvar_sysfs_load(void)
165 {
166         char *name;
167         uint64_t namesize = 0;
168         struct kobject *kobj;
169         int rc;
170
171         name = kzalloc(NAME_MAX_SIZE, GFP_KERNEL);
172         if (!name)
173                 return -ENOMEM;
174
175         do {
176                 rc = secvar_ops->get_next(name, &namesize, NAME_MAX_SIZE);
177                 if (rc) {
178                         if (rc != -ENOENT)
179                                 pr_err("error getting secvar from firmware %d\n",
180                                        rc);
181                         break;
182                 }
183
184                 kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
185                 if (!kobj) {
186                         rc = -ENOMEM;
187                         break;
188                 }
189
190                 kobject_init(kobj, &secvar_ktype);
191
192                 rc = kobject_add(kobj, &secvar_kset->kobj, "%s", name);
193                 if (rc) {
194                         pr_warn("kobject_add error %d for attribute: %s\n", rc,
195                                 name);
196                         kobject_put(kobj);
197                         kobj = NULL;
198                 }
199
200                 if (kobj)
201                         kobject_uevent(kobj, KOBJ_ADD);
202
203         } while (!rc);
204
205         kfree(name);
206         return rc;
207 }
208
209 static int secvar_sysfs_init(void)
210 {
211         int rc;
212
213         if (!secvar_ops) {
214                 pr_warn("secvar: failed to retrieve secvar operations.\n");
215                 return -ENODEV;
216         }
217
218         secvar_kobj = kobject_create_and_add("secvar", firmware_kobj);
219         if (!secvar_kobj) {
220                 pr_err("secvar: Failed to create firmware kobj\n");
221                 return -ENOMEM;
222         }
223
224         rc = sysfs_create_file(secvar_kobj, &format_attr.attr);
225         if (rc) {
226                 kobject_put(secvar_kobj);
227                 return -ENOMEM;
228         }
229
230         secvar_kset = kset_create_and_add("vars", NULL, secvar_kobj);
231         if (!secvar_kset) {
232                 pr_err("secvar: sysfs kobject registration failed.\n");
233                 kobject_put(secvar_kobj);
234                 return -ENOMEM;
235         }
236
237         rc = update_kobj_size();
238         if (rc) {
239                 pr_err("Cannot read the size of the attribute\n");
240                 return rc;
241         }
242
243         secvar_sysfs_load();
244
245         return 0;
246 }
247
248 late_initcall(secvar_sysfs_init);