xref: /openbmc/linux/arch/x86/kernel/ksysfs.c (revision 4bb1eb3c)
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 	int i = 0;
95 	struct setup_data *data;
96 	u64 pa_data = boot_params.hdr.setup_data;
97 
98 	while (pa_data) {
99 		data = memremap(pa_data, sizeof(*data), MEMREMAP_WB);
100 		if (!data)
101 			return -ENOMEM;
102 		if (nr == i) {
103 			if (data->type == SETUP_INDIRECT &&
104 			    ((struct setup_indirect *)data->data)->type != SETUP_INDIRECT)
105 				*size = ((struct setup_indirect *)data->data)->len;
106 			else
107 				*size = data->len;
108 
109 			memunmap(data);
110 			return 0;
111 		}
112 
113 		pa_data = data->next;
114 		memunmap(data);
115 		i++;
116 	}
117 	return -EINVAL;
118 }
119 
120 static ssize_t type_show(struct kobject *kobj,
121 			 struct kobj_attribute *attr, char *buf)
122 {
123 	int nr, ret;
124 	u64 paddr;
125 	struct setup_data *data;
126 
127 	ret = kobj_to_setup_data_nr(kobj, &nr);
128 	if (ret)
129 		return ret;
130 
131 	ret = get_setup_data_paddr(nr, &paddr);
132 	if (ret)
133 		return ret;
134 	data = memremap(paddr, sizeof(*data), MEMREMAP_WB);
135 	if (!data)
136 		return -ENOMEM;
137 
138 	if (data->type == SETUP_INDIRECT)
139 		ret = sprintf(buf, "0x%x\n", ((struct setup_indirect *)data->data)->type);
140 	else
141 		ret = sprintf(buf, "0x%x\n", data->type);
142 	memunmap(data);
143 	return ret;
144 }
145 
146 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 	int nr, ret = 0;
153 	u64 paddr, len;
154 	struct setup_data *data;
155 	void *p;
156 
157 	ret = kobj_to_setup_data_nr(kobj, &nr);
158 	if (ret)
159 		return ret;
160 
161 	ret = get_setup_data_paddr(nr, &paddr);
162 	if (ret)
163 		return ret;
164 	data = memremap(paddr, sizeof(*data), MEMREMAP_WB);
165 	if (!data)
166 		return -ENOMEM;
167 
168 	if (data->type == SETUP_INDIRECT &&
169 	    ((struct setup_indirect *)data->data)->type != SETUP_INDIRECT) {
170 		paddr = ((struct setup_indirect *)data->data)->addr;
171 		len = ((struct setup_indirect *)data->data)->len;
172 	} else {
173 		paddr += sizeof(*data);
174 		len = data->len;
175 	}
176 
177 	if (off > len) {
178 		ret = -EINVAL;
179 		goto out;
180 	}
181 
182 	if (count > len - off)
183 		count = len - off;
184 
185 	if (!count)
186 		goto out;
187 
188 	ret = count;
189 	p = memremap(paddr, len, MEMREMAP_WB);
190 	if (!p) {
191 		ret = -ENOMEM;
192 		goto out;
193 	}
194 	memcpy(buf, p + off, count);
195 	memunmap(p);
196 out:
197 	memunmap(data);
198 	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 static int __init create_setup_data_node(struct kobject *parent,
227 					 struct kobject **kobjp, int nr)
228 {
229 	int ret = 0;
230 	size_t size;
231 	struct kobject *kobj;
232 	char name[16]; /* should be enough for setup_data nodes numbers */
233 	snprintf(name, 16, "%d", nr);
234 
235 	kobj = kobject_create_and_add(name, parent);
236 	if (!kobj)
237 		return -ENOMEM;
238 
239 	ret = get_setup_data_size(nr, &size);
240 	if (ret)
241 		goto out_kobj;
242 
243 	data_attr.size = size;
244 	ret = sysfs_create_group(kobj, &setup_data_attr_group);
245 	if (ret)
246 		goto out_kobj;
247 	*kobjp = kobj;
248 
249 	return 0;
250 out_kobj:
251 	kobject_put(kobj);
252 	return ret;
253 }
254 
255 static void __init cleanup_setup_data_node(struct kobject *kobj)
256 {
257 	sysfs_remove_group(kobj, &setup_data_attr_group);
258 	kobject_put(kobj);
259 }
260 
261 static int __init get_setup_data_total_num(u64 pa_data, int *nr)
262 {
263 	int ret = 0;
264 	struct setup_data *data;
265 
266 	*nr = 0;
267 	while (pa_data) {
268 		*nr += 1;
269 		data = memremap(pa_data, sizeof(*data), MEMREMAP_WB);
270 		if (!data) {
271 			ret = -ENOMEM;
272 			goto out;
273 		}
274 		pa_data = data->next;
275 		memunmap(data);
276 	}
277 
278 out:
279 	return ret;
280 }
281 
282 static int __init create_setup_data_nodes(struct kobject *parent)
283 {
284 	struct kobject *setup_data_kobj, **kobjp;
285 	u64 pa_data;
286 	int i, j, nr, ret = 0;
287 
288 	pa_data = boot_params.hdr.setup_data;
289 	if (!pa_data)
290 		return 0;
291 
292 	setup_data_kobj = kobject_create_and_add("setup_data", parent);
293 	if (!setup_data_kobj) {
294 		ret = -ENOMEM;
295 		goto out;
296 	}
297 
298 	ret = get_setup_data_total_num(pa_data, &nr);
299 	if (ret)
300 		goto out_setup_data_kobj;
301 
302 	kobjp = kmalloc_array(nr, sizeof(*kobjp), GFP_KERNEL);
303 	if (!kobjp) {
304 		ret = -ENOMEM;
305 		goto out_setup_data_kobj;
306 	}
307 
308 	for (i = 0; i < nr; i++) {
309 		ret = create_setup_data_node(setup_data_kobj, kobjp + i, i);
310 		if (ret)
311 			goto out_clean_nodes;
312 	}
313 
314 	kfree(kobjp);
315 	return 0;
316 
317 out_clean_nodes:
318 	for (j = i - 1; j >= 0; j--)
319 		cleanup_setup_data_node(*(kobjp + j));
320 	kfree(kobjp);
321 out_setup_data_kobj:
322 	kobject_put(setup_data_kobj);
323 out:
324 	return ret;
325 }
326 
327 static int __init boot_params_ksysfs_init(void)
328 {
329 	int ret;
330 	struct kobject *boot_params_kobj;
331 
332 	boot_params_kobj = kobject_create_and_add("boot_params",
333 						  kernel_kobj);
334 	if (!boot_params_kobj) {
335 		ret = -ENOMEM;
336 		goto out;
337 	}
338 
339 	ret = sysfs_create_group(boot_params_kobj, &boot_params_attr_group);
340 	if (ret)
341 		goto out_boot_params_kobj;
342 
343 	ret = create_setup_data_nodes(boot_params_kobj);
344 	if (ret)
345 		goto out_create_group;
346 
347 	return 0;
348 out_create_group:
349 	sysfs_remove_group(boot_params_kobj, &boot_params_attr_group);
350 out_boot_params_kobj:
351 	kobject_put(boot_params_kobj);
352 out:
353 	return ret;
354 }
355 
356 arch_initcall(boot_params_ksysfs_init);
357