Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only
2 : : /*
3 : : * Architecture specific sysfs attributes in /sys/kernel
4 : : *
5 : : * Copyright (C) 2007, Intel Corp.
6 : : * Huang Ying <ying.huang@intel.com>
7 : : * Copyright (C) 2013, 2013 Red Hat, Inc.
8 : : * Dave Young <dyoung@redhat.com>
9 : : */
10 : :
11 : : #include <linux/kobject.h>
12 : : #include <linux/string.h>
13 : : #include <linux/sysfs.h>
14 : : #include <linux/init.h>
15 : : #include <linux/stat.h>
16 : : #include <linux/slab.h>
17 : : #include <linux/mm.h>
18 : : #include <linux/io.h>
19 : :
20 : : #include <asm/setup.h>
21 : :
22 : 0 : static ssize_t version_show(struct kobject *kobj,
23 : : struct kobj_attribute *attr, char *buf)
24 : : {
25 : 0 : return sprintf(buf, "0x%04x\n", boot_params.hdr.version);
26 : : }
27 : :
28 : : static struct kobj_attribute boot_params_version_attr = __ATTR_RO(version);
29 : :
30 : 0 : static ssize_t boot_params_data_read(struct file *fp, struct kobject *kobj,
31 : : struct bin_attribute *bin_attr,
32 : : char *buf, loff_t off, size_t count)
33 : : {
34 : 0 : memcpy(buf, (void *)&boot_params + off, count);
35 : 0 : return count;
36 : : }
37 : :
38 : : static struct bin_attribute boot_params_data_attr = {
39 : : .attr = {
40 : : .name = "data",
41 : : .mode = S_IRUGO,
42 : : },
43 : : .read = boot_params_data_read,
44 : : .size = sizeof(boot_params),
45 : : };
46 : :
47 : : static struct attribute *boot_params_version_attrs[] = {
48 : : &boot_params_version_attr.attr,
49 : : NULL,
50 : : };
51 : :
52 : : static struct bin_attribute *boot_params_data_attrs[] = {
53 : : &boot_params_data_attr,
54 : : NULL,
55 : : };
56 : :
57 : : static const struct attribute_group boot_params_attr_group = {
58 : : .attrs = boot_params_version_attrs,
59 : : .bin_attrs = boot_params_data_attrs,
60 : : };
61 : :
62 : 0 : static int kobj_to_setup_data_nr(struct kobject *kobj, int *nr)
63 : : {
64 : 0 : const char *name;
65 : :
66 : 0 : name = kobject_name(kobj);
67 : 0 : return kstrtoint(name, 10, nr);
68 : : }
69 : :
70 : 0 : static int get_setup_data_paddr(int nr, u64 *paddr)
71 : : {
72 : 0 : int i = 0;
73 : 0 : struct setup_data *data;
74 : 0 : u64 pa_data = boot_params.hdr.setup_data;
75 : :
76 [ # # ]: 0 : while (pa_data) {
77 [ # # ]: 0 : if (nr == i) {
78 : 0 : *paddr = pa_data;
79 : 0 : return 0;
80 : : }
81 : 0 : data = memremap(pa_data, sizeof(*data), MEMREMAP_WB);
82 [ # # ]: 0 : if (!data)
83 : : return -ENOMEM;
84 : :
85 : 0 : pa_data = data->next;
86 : 0 : memunmap(data);
87 : 0 : i++;
88 : : }
89 : : return -EINVAL;
90 : : }
91 : :
92 : 0 : static int __init get_setup_data_size(int nr, size_t *size)
93 : : {
94 : 0 : int i = 0;
95 : 0 : struct setup_data *data;
96 : 0 : u64 pa_data = boot_params.hdr.setup_data;
97 : :
98 [ # # ]: 0 : while (pa_data) {
99 : 0 : data = memremap(pa_data, sizeof(*data), MEMREMAP_WB);
100 [ # # ]: 0 : if (!data)
101 : : return -ENOMEM;
102 [ # # ]: 0 : if (nr == i) {
103 [ # # ]: 0 : if (data->type == SETUP_INDIRECT &&
104 [ # # ]: 0 : ((struct setup_indirect *)data->data)->type != SETUP_INDIRECT)
105 : 0 : *size = ((struct setup_indirect *)data->data)->len;
106 : : else
107 : 0 : *size = data->len;
108 : :
109 : 0 : memunmap(data);
110 : 0 : return 0;
111 : : }
112 : :
113 : 0 : pa_data = data->next;
114 : 0 : memunmap(data);
115 : 0 : i++;
116 : : }
117 : : return -EINVAL;
118 : : }
119 : :
120 : 0 : static ssize_t type_show(struct kobject *kobj,
121 : : struct kobj_attribute *attr, char *buf)
122 : : {
123 : 0 : int nr, ret;
124 : 0 : u64 paddr;
125 : 0 : struct setup_data *data;
126 : :
127 : 0 : ret = kobj_to_setup_data_nr(kobj, &nr);
128 [ # # ]: 0 : if (ret)
129 : 0 : return ret;
130 : :
131 : 0 : ret = get_setup_data_paddr(nr, &paddr);
132 [ # # ]: 0 : if (ret)
133 : 0 : return ret;
134 : 0 : data = memremap(paddr, sizeof(*data), MEMREMAP_WB);
135 [ # # ]: 0 : if (!data)
136 : : return -ENOMEM;
137 : :
138 [ # # ]: 0 : if (data->type == SETUP_INDIRECT)
139 : 0 : ret = sprintf(buf, "0x%x\n", ((struct setup_indirect *)data->data)->type);
140 : : else
141 : 0 : ret = sprintf(buf, "0x%x\n", data->type);
142 : 0 : memunmap(data);
143 : 0 : return ret;
144 : : }
145 : :
146 : 0 : static ssize_t setup_data_data_read(struct file *fp,
147 : : struct kobject *kobj,
148 : : struct bin_attribute *bin_attr,
149 : : char *buf,
150 : : loff_t off, size_t count)
151 : : {
152 : 0 : int nr, ret = 0;
153 : 0 : u64 paddr, len;
154 : 0 : struct setup_data *data;
155 : 0 : void *p;
156 : :
157 : 0 : ret = kobj_to_setup_data_nr(kobj, &nr);
158 [ # # ]: 0 : if (ret)
159 : 0 : return ret;
160 : :
161 : 0 : ret = get_setup_data_paddr(nr, &paddr);
162 [ # # ]: 0 : if (ret)
163 : 0 : return ret;
164 : 0 : data = memremap(paddr, sizeof(*data), MEMREMAP_WB);
165 [ # # ]: 0 : if (!data)
166 : : return -ENOMEM;
167 : :
168 [ # # ]: 0 : if (data->type == SETUP_INDIRECT &&
169 [ # # ]: 0 : ((struct setup_indirect *)data->data)->type != SETUP_INDIRECT) {
170 : 0 : paddr = ((struct setup_indirect *)data->data)->addr;
171 : 0 : len = ((struct setup_indirect *)data->data)->len;
172 : : } else {
173 : 0 : paddr += sizeof(*data);
174 : 0 : len = data->len;
175 : : }
176 : :
177 [ # # ]: 0 : if (off > len) {
178 : 0 : ret = -EINVAL;
179 : 0 : goto out;
180 : : }
181 : :
182 : 0 : if (count > len - off)
183 : : count = len - off;
184 : :
185 [ # # ]: 0 : if (!count)
186 : 0 : goto out;
187 : :
188 : 0 : ret = count;
189 : 0 : p = memremap(paddr, len, MEMREMAP_WB);
190 [ # # ]: 0 : if (!p) {
191 : 0 : ret = -ENOMEM;
192 : 0 : goto out;
193 : : }
194 : 0 : memcpy(buf, p + off, count);
195 : 0 : memunmap(p);
196 : 0 : out:
197 : 0 : memunmap(data);
198 : 0 : return ret;
199 : : }
200 : :
201 : : static struct kobj_attribute type_attr = __ATTR_RO(type);
202 : :
203 : : static struct bin_attribute data_attr __ro_after_init = {
204 : : .attr = {
205 : : .name = "data",
206 : : .mode = S_IRUGO,
207 : : },
208 : : .read = setup_data_data_read,
209 : : };
210 : :
211 : : static struct attribute *setup_data_type_attrs[] = {
212 : : &type_attr.attr,
213 : : NULL,
214 : : };
215 : :
216 : : static struct bin_attribute *setup_data_data_attrs[] = {
217 : : &data_attr,
218 : : NULL,
219 : : };
220 : :
221 : : static const struct attribute_group setup_data_attr_group = {
222 : : .attrs = setup_data_type_attrs,
223 : : .bin_attrs = setup_data_data_attrs,
224 : : };
225 : :
226 : 0 : static int __init create_setup_data_node(struct kobject *parent,
227 : : struct kobject **kobjp, int nr)
228 : : {
229 : 0 : int ret = 0;
230 : 0 : size_t size;
231 : 0 : struct kobject *kobj;
232 : 0 : char name[16]; /* should be enough for setup_data nodes numbers */
233 : 0 : snprintf(name, 16, "%d", nr);
234 : :
235 : 0 : kobj = kobject_create_and_add(name, parent);
236 [ # # ]: 0 : if (!kobj)
237 : : return -ENOMEM;
238 : :
239 : 0 : ret = get_setup_data_size(nr, &size);
240 [ # # ]: 0 : if (ret)
241 : 0 : goto out_kobj;
242 : :
243 : 0 : data_attr.size = size;
244 : 0 : ret = sysfs_create_group(kobj, &setup_data_attr_group);
245 [ # # ]: 0 : if (ret)
246 : 0 : goto out_kobj;
247 : 0 : *kobjp = kobj;
248 : :
249 : 0 : return 0;
250 : 0 : out_kobj:
251 : 0 : kobject_put(kobj);
252 : 0 : return ret;
253 : : }
254 : :
255 : 0 : static void __init cleanup_setup_data_node(struct kobject *kobj)
256 : : {
257 : 0 : sysfs_remove_group(kobj, &setup_data_attr_group);
258 : 0 : kobject_put(kobj);
259 : 0 : }
260 : :
261 : 0 : static int __init get_setup_data_total_num(u64 pa_data, int *nr)
262 : : {
263 : 0 : int ret = 0;
264 : 0 : struct setup_data *data;
265 : :
266 : 0 : *nr = 0;
267 [ # # ]: 0 : while (pa_data) {
268 : 0 : *nr += 1;
269 : 0 : data = memremap(pa_data, sizeof(*data), MEMREMAP_WB);
270 [ # # ]: 0 : if (!data) {
271 : 0 : ret = -ENOMEM;
272 : 0 : goto out;
273 : : }
274 : 0 : pa_data = data->next;
275 : 0 : memunmap(data);
276 : : }
277 : :
278 : 0 : out:
279 : 0 : return ret;
280 : : }
281 : :
282 : 78 : static int __init create_setup_data_nodes(struct kobject *parent)
283 : : {
284 : 78 : struct kobject *setup_data_kobj, **kobjp;
285 : 78 : u64 pa_data;
286 : 78 : int i, j, nr, ret = 0;
287 : :
288 : 78 : pa_data = boot_params.hdr.setup_data;
289 [ - + ]: 78 : if (!pa_data)
290 : : return 0;
291 : :
292 : 0 : setup_data_kobj = kobject_create_and_add("setup_data", parent);
293 [ # # ]: 0 : if (!setup_data_kobj) {
294 : 0 : ret = -ENOMEM;
295 : 0 : goto out;
296 : : }
297 : :
298 : 0 : ret = get_setup_data_total_num(pa_data, &nr);
299 [ # # ]: 0 : if (ret)
300 : 0 : goto out_setup_data_kobj;
301 : :
302 : 0 : kobjp = kmalloc_array(nr, sizeof(*kobjp), GFP_KERNEL);
303 [ # # ]: 0 : if (!kobjp) {
304 : 0 : ret = -ENOMEM;
305 : 0 : goto out_setup_data_kobj;
306 : : }
307 : :
308 [ # # ]: 0 : for (i = 0; i < nr; i++) {
309 : 0 : ret = create_setup_data_node(setup_data_kobj, kobjp + i, i);
310 [ # # ]: 0 : if (ret)
311 : 0 : goto out_clean_nodes;
312 : : }
313 : :
314 : 0 : kfree(kobjp);
315 : 0 : return 0;
316 : :
317 : : out_clean_nodes:
318 [ # # ]: 0 : for (j = i - 1; j >= 0; j--)
319 : 0 : cleanup_setup_data_node(*(kobjp + j));
320 : 0 : kfree(kobjp);
321 : 0 : out_setup_data_kobj:
322 : 0 : kobject_put(setup_data_kobj);
323 : : out:
324 : : return ret;
325 : : }
326 : :
327 : 78 : static int __init boot_params_ksysfs_init(void)
328 : : {
329 : 78 : int ret;
330 : 78 : struct kobject *boot_params_kobj;
331 : :
332 : 78 : boot_params_kobj = kobject_create_and_add("boot_params",
333 : : kernel_kobj);
334 [ - + ]: 78 : if (!boot_params_kobj) {
335 : 0 : ret = -ENOMEM;
336 : 0 : goto out;
337 : : }
338 : :
339 : 78 : ret = sysfs_create_group(boot_params_kobj, &boot_params_attr_group);
340 [ - + ]: 78 : if (ret)
341 : 0 : goto out_boot_params_kobj;
342 : :
343 : 78 : ret = create_setup_data_nodes(boot_params_kobj);
344 [ - + ]: 78 : if (ret)
345 : 0 : goto out_create_group;
346 : :
347 : : return 0;
348 : : out_create_group:
349 : 0 : sysfs_remove_group(boot_params_kobj, &boot_params_attr_group);
350 : 0 : out_boot_params_kobj:
351 : 0 : kobject_put(boot_params_kobj);
352 : : out:
353 : : return ret;
354 : : }
355 : :
356 : : arch_initcall(boot_params_ksysfs_init);
|