Commit | Line | Data |
---|---|---|
55716d26 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
5039e316 DY |
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> | |
5039e316 DY |
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> | |
f7750a79 | 18 | #include <linux/io.h> |
5039e316 DY |
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 | ||
45bd07ad | 57 | static const struct attribute_group boot_params_attr_group = { |
5039e316 DY |
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 | } | |
f7750a79 | 81 | data = memremap(pa_data, sizeof(*data), MEMREMAP_WB); |
5039e316 DY |
82 | if (!data) |
83 | return -ENOMEM; | |
84 | ||
85 | pa_data = data->next; | |
f7750a79 | 86 | memunmap(data); |
5039e316 DY |
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) { | |
f7750a79 | 99 | data = memremap(pa_data, sizeof(*data), MEMREMAP_WB); |
5039e316 DY |
100 | if (!data) |
101 | return -ENOMEM; | |
102 | if (nr == i) { | |
103 | *size = data->len; | |
f7750a79 | 104 | memunmap(data); |
5039e316 DY |
105 | return 0; |
106 | } | |
107 | ||
108 | pa_data = data->next; | |
f7750a79 | 109 | memunmap(data); |
5039e316 DY |
110 | i++; |
111 | } | |
112 | return -EINVAL; | |
113 | } | |
114 | ||
115 | static ssize_t type_show(struct kobject *kobj, | |
116 | struct kobj_attribute *attr, char *buf) | |
117 | { | |
118 | int nr, ret; | |
119 | u64 paddr; | |
120 | struct setup_data *data; | |
121 | ||
122 | ret = kobj_to_setup_data_nr(kobj, &nr); | |
123 | if (ret) | |
124 | return ret; | |
125 | ||
126 | ret = get_setup_data_paddr(nr, &paddr); | |
127 | if (ret) | |
128 | return ret; | |
f7750a79 | 129 | data = memremap(paddr, sizeof(*data), MEMREMAP_WB); |
5039e316 DY |
130 | if (!data) |
131 | return -ENOMEM; | |
132 | ||
133 | ret = sprintf(buf, "0x%x\n", data->type); | |
f7750a79 | 134 | memunmap(data); |
5039e316 DY |
135 | return ret; |
136 | } | |
137 | ||
138 | static ssize_t setup_data_data_read(struct file *fp, | |
139 | struct kobject *kobj, | |
140 | struct bin_attribute *bin_attr, | |
141 | char *buf, | |
142 | loff_t off, size_t count) | |
143 | { | |
144 | int nr, ret = 0; | |
145 | u64 paddr; | |
146 | struct setup_data *data; | |
147 | void *p; | |
148 | ||
149 | ret = kobj_to_setup_data_nr(kobj, &nr); | |
150 | if (ret) | |
151 | return ret; | |
152 | ||
153 | ret = get_setup_data_paddr(nr, &paddr); | |
154 | if (ret) | |
155 | return ret; | |
f7750a79 | 156 | data = memremap(paddr, sizeof(*data), MEMREMAP_WB); |
5039e316 DY |
157 | if (!data) |
158 | return -ENOMEM; | |
159 | ||
160 | if (off > data->len) { | |
161 | ret = -EINVAL; | |
162 | goto out; | |
163 | } | |
164 | ||
165 | if (count > data->len - off) | |
166 | count = data->len - off; | |
167 | ||
168 | if (!count) | |
169 | goto out; | |
170 | ||
171 | ret = count; | |
f7750a79 | 172 | p = memremap(paddr + sizeof(*data), data->len, MEMREMAP_WB); |
5039e316 DY |
173 | if (!p) { |
174 | ret = -ENOMEM; | |
175 | goto out; | |
176 | } | |
177 | memcpy(buf, p + off, count); | |
f7750a79 | 178 | memunmap(p); |
5039e316 | 179 | out: |
f7750a79 | 180 | memunmap(data); |
5039e316 DY |
181 | return ret; |
182 | } | |
183 | ||
184 | static struct kobj_attribute type_attr = __ATTR_RO(type); | |
185 | ||
404f6aac | 186 | static struct bin_attribute data_attr __ro_after_init = { |
5039e316 DY |
187 | .attr = { |
188 | .name = "data", | |
189 | .mode = S_IRUGO, | |
190 | }, | |
191 | .read = setup_data_data_read, | |
192 | }; | |
193 | ||
194 | static struct attribute *setup_data_type_attrs[] = { | |
195 | &type_attr.attr, | |
196 | NULL, | |
197 | }; | |
198 | ||
199 | static struct bin_attribute *setup_data_data_attrs[] = { | |
200 | &data_attr, | |
201 | NULL, | |
202 | }; | |
203 | ||
45bd07ad | 204 | static const struct attribute_group setup_data_attr_group = { |
5039e316 DY |
205 | .attrs = setup_data_type_attrs, |
206 | .bin_attrs = setup_data_data_attrs, | |
207 | }; | |
208 | ||
209 | static int __init create_setup_data_node(struct kobject *parent, | |
210 | struct kobject **kobjp, int nr) | |
211 | { | |
212 | int ret = 0; | |
213 | size_t size; | |
214 | struct kobject *kobj; | |
215 | char name[16]; /* should be enough for setup_data nodes numbers */ | |
216 | snprintf(name, 16, "%d", nr); | |
217 | ||
218 | kobj = kobject_create_and_add(name, parent); | |
219 | if (!kobj) | |
220 | return -ENOMEM; | |
221 | ||
222 | ret = get_setup_data_size(nr, &size); | |
223 | if (ret) | |
224 | goto out_kobj; | |
225 | ||
226 | data_attr.size = size; | |
227 | ret = sysfs_create_group(kobj, &setup_data_attr_group); | |
228 | if (ret) | |
229 | goto out_kobj; | |
230 | *kobjp = kobj; | |
231 | ||
232 | return 0; | |
233 | out_kobj: | |
234 | kobject_put(kobj); | |
235 | return ret; | |
236 | } | |
237 | ||
238 | static void __init cleanup_setup_data_node(struct kobject *kobj) | |
239 | { | |
240 | sysfs_remove_group(kobj, &setup_data_attr_group); | |
241 | kobject_put(kobj); | |
242 | } | |
243 | ||
244 | static int __init get_setup_data_total_num(u64 pa_data, int *nr) | |
245 | { | |
246 | int ret = 0; | |
247 | struct setup_data *data; | |
248 | ||
249 | *nr = 0; | |
250 | while (pa_data) { | |
251 | *nr += 1; | |
f7750a79 | 252 | data = memremap(pa_data, sizeof(*data), MEMREMAP_WB); |
5039e316 DY |
253 | if (!data) { |
254 | ret = -ENOMEM; | |
255 | goto out; | |
256 | } | |
257 | pa_data = data->next; | |
f7750a79 | 258 | memunmap(data); |
5039e316 DY |
259 | } |
260 | ||
261 | out: | |
262 | return ret; | |
263 | } | |
264 | ||
265 | static int __init create_setup_data_nodes(struct kobject *parent) | |
266 | { | |
267 | struct kobject *setup_data_kobj, **kobjp; | |
268 | u64 pa_data; | |
269 | int i, j, nr, ret = 0; | |
270 | ||
271 | pa_data = boot_params.hdr.setup_data; | |
272 | if (!pa_data) | |
273 | return 0; | |
274 | ||
275 | setup_data_kobj = kobject_create_and_add("setup_data", parent); | |
276 | if (!setup_data_kobj) { | |
277 | ret = -ENOMEM; | |
278 | goto out; | |
279 | } | |
280 | ||
281 | ret = get_setup_data_total_num(pa_data, &nr); | |
282 | if (ret) | |
283 | goto out_setup_data_kobj; | |
284 | ||
6da2ec56 | 285 | kobjp = kmalloc_array(nr, sizeof(*kobjp), GFP_KERNEL); |
5039e316 DY |
286 | if (!kobjp) { |
287 | ret = -ENOMEM; | |
288 | goto out_setup_data_kobj; | |
289 | } | |
290 | ||
291 | for (i = 0; i < nr; i++) { | |
292 | ret = create_setup_data_node(setup_data_kobj, kobjp + i, i); | |
293 | if (ret) | |
294 | goto out_clean_nodes; | |
295 | } | |
296 | ||
297 | kfree(kobjp); | |
298 | return 0; | |
299 | ||
300 | out_clean_nodes: | |
7d709943 | 301 | for (j = i - 1; j >= 0; j--) |
5039e316 DY |
302 | cleanup_setup_data_node(*(kobjp + j)); |
303 | kfree(kobjp); | |
304 | out_setup_data_kobj: | |
305 | kobject_put(setup_data_kobj); | |
306 | out: | |
307 | return ret; | |
308 | } | |
309 | ||
310 | static int __init boot_params_ksysfs_init(void) | |
311 | { | |
312 | int ret; | |
313 | struct kobject *boot_params_kobj; | |
314 | ||
315 | boot_params_kobj = kobject_create_and_add("boot_params", | |
316 | kernel_kobj); | |
317 | if (!boot_params_kobj) { | |
318 | ret = -ENOMEM; | |
319 | goto out; | |
320 | } | |
321 | ||
322 | ret = sysfs_create_group(boot_params_kobj, &boot_params_attr_group); | |
323 | if (ret) | |
324 | goto out_boot_params_kobj; | |
325 | ||
326 | ret = create_setup_data_nodes(boot_params_kobj); | |
327 | if (ret) | |
328 | goto out_create_group; | |
329 | ||
330 | return 0; | |
331 | out_create_group: | |
332 | sysfs_remove_group(boot_params_kobj, &boot_params_attr_group); | |
333 | out_boot_params_kobj: | |
334 | kobject_put(boot_params_kobj); | |
335 | out: | |
336 | return ret; | |
337 | } | |
338 | ||
339 | arch_initcall(boot_params_ksysfs_init); |