aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-kernel-boot_params38
-rw-r--r--arch/x86/kernel/Makefile1
-rw-r--r--arch/x86/kernel/ksysfs.c339
3 files changed, 378 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-kernel-boot_params b/Documentation/ABI/testing/sysfs-kernel-boot_params
new file mode 100644
index 000000000000..eca38ce2852d
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-kernel-boot_params
@@ -0,0 +1,38 @@
1What: /sys/kernel/boot_params
2Date: December 2013
3Contact: Dave Young <dyoung@redhat.com>
4Description: The /sys/kernel/boot_params directory contains two
5 files: "data" and "version" and one subdirectory "setup_data".
6 It is used to export the kernel boot parameters of an x86
7 platform to userspace for kexec and debugging purpose.
8
9 If there's no setup_data in boot_params the subdirectory will
10 not be created.
11
12 "data" file is the binary representation of struct boot_params.
13
14 "version" file is the string representation of boot
15 protocol version.
16
17 "setup_data" subdirectory contains the setup_data data
18 structure in boot_params. setup_data is maintained in kernel
19 as a link list. In "setup_data" subdirectory there's one
20 subdirectory for each link list node named with the number
21 of the list nodes. The list node subdirectory contains two
22 files "type" and "data". "type" file is the string
23 representation of setup_data type. "data" file is the binary
24 representation of setup_data payload.
25
26 The whole boot_params directory structure is like below:
27 /sys/kernel/boot_params
28 |__ data
29 |__ setup_data
30 | |__ 0
31 | | |__ data
32 | | |__ type
33 | |__ 1
34 | |__ data
35 | |__ type
36 |__ version
37
38Users: Kexec
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 9b0a34e2cd79..510cca5c5390 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_X86_64) += sys_x86_64.o x8664_ksyms_64.o
29obj-y += syscall_$(BITS).o 29obj-y += syscall_$(BITS).o
30obj-$(CONFIG_X86_64) += vsyscall_64.o 30obj-$(CONFIG_X86_64) += vsyscall_64.o
31obj-$(CONFIG_X86_64) += vsyscall_emu_64.o 31obj-$(CONFIG_X86_64) += vsyscall_emu_64.o
32obj-$(CONFIG_SYSFS) += ksysfs.o
32obj-y += bootflag.o e820.o 33obj-y += bootflag.o e820.o
33obj-y += pci-dma.o quirks.o topology.o kdebugfs.o 34obj-y += pci-dma.o quirks.o topology.o kdebugfs.o
34obj-y += alternative.o i8253.o pci-nommu.o hw_breakpoint.o 35obj-y += alternative.o i8253.o pci-nommu.o hw_breakpoint.o
diff --git a/arch/x86/kernel/ksysfs.c b/arch/x86/kernel/ksysfs.c
new file mode 100644
index 000000000000..eb53d153f307
--- /dev/null
+++ b/arch/x86/kernel/ksysfs.c
@@ -0,0 +1,339 @@
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
22static 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
28static struct kobj_attribute boot_params_version_attr = __ATTR_RO(version);
29
30static 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
38static 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
47static struct attribute *boot_params_version_attrs[] = {
48 &boot_params_version_attr.attr,
49 NULL,
50};
51
52static struct bin_attribute *boot_params_data_attrs[] = {
53 &boot_params_data_attr,
54 NULL,
55};
56
57static struct attribute_group boot_params_attr_group = {
58 .attrs = boot_params_version_attrs,
59 .bin_attrs = boot_params_data_attrs,
60};
61
62static 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
70static 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
92static 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
115static 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
138static 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);
179out:
180 iounmap(data);
181 return ret;
182}
183
184static struct kobj_attribute type_attr = __ATTR_RO(type);
185
186static struct bin_attribute data_attr = {
187 .attr = {
188 .name = "data",
189 .mode = S_IRUGO,
190 },
191 .read = setup_data_data_read,
192};
193
194static struct attribute *setup_data_type_attrs[] = {
195 &type_attr.attr,
196 NULL,
197};
198
199static struct bin_attribute *setup_data_data_attrs[] = {
200 &data_attr,
201 NULL,
202};
203
204static struct attribute_group setup_data_attr_group = {
205 .attrs = setup_data_type_attrs,
206 .bin_attrs = setup_data_data_attrs,
207};
208
209static 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;
233out_kobj:
234 kobject_put(kobj);
235 return ret;
236}
237
238static 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
244static 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
261out:
262 return ret;
263}
264
265static 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
300out_clean_nodes:
301 for (j = i - 1; j > 0; j--)
302 cleanup_setup_data_node(*(kobjp + j));
303 kfree(kobjp);
304out_setup_data_kobj:
305 kobject_put(setup_data_kobj);
306out:
307 return ret;
308}
309
310static 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;
331out_create_group:
332 sysfs_remove_group(boot_params_kobj, &boot_params_attr_group);
333out_boot_params_kobj:
334 kobject_put(boot_params_kobj);
335out:
336 return ret;
337}
338
339arch_initcall(boot_params_ksysfs_init);