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 1 : static int __init create_setup_data_nodes(struct kobject *parent)
283 : {
284 1 : struct kobject *setup_data_kobj, **kobjp;
285 1 : u64 pa_data;
286 1 : int i, j, nr, ret = 0;
287 :
288 1 : pa_data = boot_params.hdr.setup_data;
289 1 : 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 0 : 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 1 : static int __init boot_params_ksysfs_init(void)
328 : {
329 1 : int ret;
330 1 : struct kobject *boot_params_kobj;
331 :
332 1 : boot_params_kobj = kobject_create_and_add("boot_params",
333 : kernel_kobj);
334 1 : if (!boot_params_kobj) {
335 0 : ret = -ENOMEM;
336 0 : goto out;
337 : }
338 :
339 1 : ret = sysfs_create_group(boot_params_kobj, &boot_params_attr_group);
340 1 : if (ret)
341 0 : goto out_boot_params_kobj;
342 :
343 1 : ret = create_setup_data_nodes(boot_params_kobj);
344 1 : if (ret)
345 0 : goto out_create_group;
346 :
347 : return 0;
348 0 : 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);
|