]>
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> | |
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 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 = ioremap_cache(pa_data, sizeof(*data)); | |
82 | if (!data) | |
83 | return -ENOMEM; | |
84 | ||
85 | pa_data = data->next; | |
86 | iounmap(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 = ioremap_cache(pa_data, sizeof(*data)); | |
100 | if (!data) | |
101 | return -ENOMEM; | |
102 | if (nr == i) { | |
103 | *size = data->len; | |
104 | iounmap(data); | |
105 | return 0; | |
106 | } | |
107 | ||
108 | pa_data = data->next; | |
109 | iounmap(data); | |
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; | |
129 | data = ioremap_cache(paddr, sizeof(*data)); | |
130 | if (!data) | |
131 | return -ENOMEM; | |
132 | ||
133 | ret = sprintf(buf, "0x%x\n", data->type); | |
134 | iounmap(data); | |
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; | |
156 | data = ioremap_cache(paddr, sizeof(*data)); | |
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; | |
172 | p = ioremap_cache(paddr + sizeof(*data), data->len); | |
173 | if (!p) { | |
174 | ret = -ENOMEM; | |
175 | goto out; | |
176 | } | |
177 | memcpy(buf, p + off, count); | |
178 | iounmap(p); | |
179 | out: | |
180 | iounmap(data); | |
181 | return ret; | |
182 | } | |
183 | ||
184 | static struct kobj_attribute type_attr = __ATTR_RO(type); | |
185 | ||
186 | static struct bin_attribute data_attr = { | |
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 | ||
204 | static struct attribute_group setup_data_attr_group = { | |
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; | |
252 | data = ioremap_cache(pa_data, sizeof(*data)); | |
253 | if (!data) { | |
254 | ret = -ENOMEM; | |
255 | goto out; | |
256 | } | |
257 | pa_data = data->next; | |
258 | iounmap(data); | |
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 | ||
285 | kobjp = kmalloc(sizeof(*kobjp) * nr, GFP_KERNEL); | |
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: | |
301 | for (j = i - 1; j > 0; j--) | |
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); |