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