xref: /linux/arch/x86/kernel/ksysfs.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
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 static ssize_t version_show(struct kobject *kobj,
23 			    struct kobj_attribute *attr, char *buf)
24 {
25 	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 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 	memcpy(buf, (void *)&boot_params + off, count);
35 	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 static int kobj_to_setup_data_nr(struct kobject *kobj, int *nr)
63 {
64 	const char *name;
65 
66 	name = kobject_name(kobj);
67 	return kstrtoint(name, 10, nr);
68 }
69 
70 static int get_setup_data_paddr(int nr, u64 *paddr)
71 {
72 	int i = 0;
73 	struct setup_data *data;
74 	u64 pa_data = boot_params.hdr.setup_data;
75 
76 	while (pa_data) {
77 		if (nr == i) {
78 			*paddr = pa_data;
79 			return 0;
80 		}
81 		data = memremap(pa_data, sizeof(*data), MEMREMAP_WB);
82 		if (!data)
83 			return -ENOMEM;
84 
85 		pa_data = data->next;
86 		memunmap(data);
87 		i++;
88 	}
89 	return -EINVAL;
90 }
91 
92 static int __init get_setup_data_size(int nr, size_t *size)
93 {
94 	u64 pa_data = boot_params.hdr.setup_data, pa_next;
95 	struct setup_indirect *indirect;
96 	struct setup_data *data;
97 	int i = 0;
98 	u32 len;
99 
100 	while (pa_data) {
101 		data = memremap(pa_data, sizeof(*data), MEMREMAP_WB);
102 		if (!data)
103 			return -ENOMEM;
104 		pa_next = data->next;
105 
106 		if (nr == i) {
107 			if (data->type == SETUP_INDIRECT) {
108 				len = sizeof(*data) + data->len;
109 				memunmap(data);
110 				data = memremap(pa_data, len, MEMREMAP_WB);
111 				if (!data)
112 					return -ENOMEM;
113 
114 				indirect = (struct setup_indirect *)data->data;
115 
116 				if (indirect->type != SETUP_INDIRECT)
117 					*size = indirect->len;
118 				else
119 					*size = data->len;
120 			} else {
121 				*size = data->len;
122 			}
123 
124 			memunmap(data);
125 			return 0;
126 		}
127 
128 		pa_data = pa_next;
129 		memunmap(data);
130 		i++;
131 	}
132 	return -EINVAL;
133 }
134 
135 static ssize_t type_show(struct kobject *kobj,
136 			 struct kobj_attribute *attr, char *buf)
137 {
138 	struct setup_indirect *indirect;
139 	struct setup_data *data;
140 	int nr, ret;
141 	u64 paddr;
142 	u32 len;
143 
144 	ret = kobj_to_setup_data_nr(kobj, &nr);
145 	if (ret)
146 		return ret;
147 
148 	ret = get_setup_data_paddr(nr, &paddr);
149 	if (ret)
150 		return ret;
151 	data = memremap(paddr, sizeof(*data), MEMREMAP_WB);
152 	if (!data)
153 		return -ENOMEM;
154 
155 	if (data->type == SETUP_INDIRECT) {
156 		len = sizeof(*data) + data->len;
157 		memunmap(data);
158 		data = memremap(paddr, len, MEMREMAP_WB);
159 		if (!data)
160 			return -ENOMEM;
161 
162 		indirect = (struct setup_indirect *)data->data;
163 
164 		ret = sprintf(buf, "0x%x\n", indirect->type);
165 	} else {
166 		ret = sprintf(buf, "0x%x\n", data->type);
167 	}
168 
169 	memunmap(data);
170 	return ret;
171 }
172 
173 static ssize_t setup_data_data_read(struct file *fp,
174 				    struct kobject *kobj,
175 				    struct bin_attribute *bin_attr,
176 				    char *buf,
177 				    loff_t off, size_t count)
178 {
179 	struct setup_indirect *indirect;
180 	struct setup_data *data;
181 	int nr, ret = 0;
182 	u64 paddr, len;
183 	void *p;
184 
185 	ret = kobj_to_setup_data_nr(kobj, &nr);
186 	if (ret)
187 		return ret;
188 
189 	ret = get_setup_data_paddr(nr, &paddr);
190 	if (ret)
191 		return ret;
192 	data = memremap(paddr, sizeof(*data), MEMREMAP_WB);
193 	if (!data)
194 		return -ENOMEM;
195 
196 	if (data->type == SETUP_INDIRECT) {
197 		len = sizeof(*data) + data->len;
198 		memunmap(data);
199 		data = memremap(paddr, len, MEMREMAP_WB);
200 		if (!data)
201 			return -ENOMEM;
202 
203 		indirect = (struct setup_indirect *)data->data;
204 
205 		if (indirect->type != SETUP_INDIRECT) {
206 			paddr = indirect->addr;
207 			len = indirect->len;
208 		} else {
209 			/*
210 			 * Even though this is technically undefined, return
211 			 * the data as though it is a normal setup_data struct.
212 			 * This will at least allow it to be inspected.
213 			 */
214 			paddr += sizeof(*data);
215 			len = data->len;
216 		}
217 	} else {
218 		paddr += sizeof(*data);
219 		len = data->len;
220 	}
221 
222 	if (off > len) {
223 		ret = -EINVAL;
224 		goto out;
225 	}
226 
227 	if (count > len - off)
228 		count = len - off;
229 
230 	if (!count)
231 		goto out;
232 
233 	ret = count;
234 	p = memremap(paddr, len, MEMREMAP_WB);
235 	if (!p) {
236 		ret = -ENOMEM;
237 		goto out;
238 	}
239 	memcpy(buf, p + off, count);
240 	memunmap(p);
241 out:
242 	memunmap(data);
243 	return ret;
244 }
245 
246 static struct kobj_attribute type_attr = __ATTR_RO(type);
247 
248 static struct bin_attribute data_attr __ro_after_init = {
249 	.attr = {
250 		.name = "data",
251 		.mode = S_IRUGO,
252 	},
253 	.read = setup_data_data_read,
254 };
255 
256 static struct attribute *setup_data_type_attrs[] = {
257 	&type_attr.attr,
258 	NULL,
259 };
260 
261 static struct bin_attribute *setup_data_data_attrs[] = {
262 	&data_attr,
263 	NULL,
264 };
265 
266 static const struct attribute_group setup_data_attr_group = {
267 	.attrs = setup_data_type_attrs,
268 	.bin_attrs = setup_data_data_attrs,
269 };
270 
271 static int __init create_setup_data_node(struct kobject *parent,
272 					 struct kobject **kobjp, int nr)
273 {
274 	int ret = 0;
275 	size_t size;
276 	struct kobject *kobj;
277 	char name[16]; /* should be enough for setup_data nodes numbers */
278 	snprintf(name, 16, "%d", nr);
279 
280 	kobj = kobject_create_and_add(name, parent);
281 	if (!kobj)
282 		return -ENOMEM;
283 
284 	ret = get_setup_data_size(nr, &size);
285 	if (ret)
286 		goto out_kobj;
287 
288 	data_attr.size = size;
289 	ret = sysfs_create_group(kobj, &setup_data_attr_group);
290 	if (ret)
291 		goto out_kobj;
292 	*kobjp = kobj;
293 
294 	return 0;
295 out_kobj:
296 	kobject_put(kobj);
297 	return ret;
298 }
299 
300 static void __init cleanup_setup_data_node(struct kobject *kobj)
301 {
302 	sysfs_remove_group(kobj, &setup_data_attr_group);
303 	kobject_put(kobj);
304 }
305 
306 static int __init get_setup_data_total_num(u64 pa_data, int *nr)
307 {
308 	int ret = 0;
309 	struct setup_data *data;
310 
311 	*nr = 0;
312 	while (pa_data) {
313 		*nr += 1;
314 		data = memremap(pa_data, sizeof(*data), MEMREMAP_WB);
315 		if (!data) {
316 			ret = -ENOMEM;
317 			goto out;
318 		}
319 		pa_data = data->next;
320 		memunmap(data);
321 	}
322 
323 out:
324 	return ret;
325 }
326 
327 static int __init create_setup_data_nodes(struct kobject *parent)
328 {
329 	struct kobject *setup_data_kobj, **kobjp;
330 	u64 pa_data;
331 	int i, j, nr, ret = 0;
332 
333 	pa_data = boot_params.hdr.setup_data;
334 	if (!pa_data)
335 		return 0;
336 
337 	setup_data_kobj = kobject_create_and_add("setup_data", parent);
338 	if (!setup_data_kobj) {
339 		ret = -ENOMEM;
340 		goto out;
341 	}
342 
343 	ret = get_setup_data_total_num(pa_data, &nr);
344 	if (ret)
345 		goto out_setup_data_kobj;
346 
347 	kobjp = kmalloc_array(nr, sizeof(*kobjp), GFP_KERNEL);
348 	if (!kobjp) {
349 		ret = -ENOMEM;
350 		goto out_setup_data_kobj;
351 	}
352 
353 	for (i = 0; i < nr; i++) {
354 		ret = create_setup_data_node(setup_data_kobj, kobjp + i, i);
355 		if (ret)
356 			goto out_clean_nodes;
357 	}
358 
359 	kfree(kobjp);
360 	return 0;
361 
362 out_clean_nodes:
363 	for (j = i - 1; j >= 0; j--)
364 		cleanup_setup_data_node(*(kobjp + j));
365 	kfree(kobjp);
366 out_setup_data_kobj:
367 	kobject_put(setup_data_kobj);
368 out:
369 	return ret;
370 }
371 
372 static int __init boot_params_ksysfs_init(void)
373 {
374 	int ret;
375 	struct kobject *boot_params_kobj;
376 
377 	boot_params_kobj = kobject_create_and_add("boot_params",
378 						  kernel_kobj);
379 	if (!boot_params_kobj) {
380 		ret = -ENOMEM;
381 		goto out;
382 	}
383 
384 	ret = sysfs_create_group(boot_params_kobj, &boot_params_attr_group);
385 	if (ret)
386 		goto out_boot_params_kobj;
387 
388 	ret = create_setup_data_nodes(boot_params_kobj);
389 	if (ret)
390 		goto out_create_group;
391 
392 	return 0;
393 out_create_group:
394 	sysfs_remove_group(boot_params_kobj, &boot_params_attr_group);
395 out_boot_params_kobj:
396 	kobject_put(boot_params_kobj);
397 out:
398 	return ret;
399 }
400 
401 arch_initcall(boot_params_ksysfs_init);
402