Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86: Export x86 boot_params to sysfs

kexec-tools use boot_params for getting the 1st kernel hardware_subarch,
the kexec kernel EFI runtime support also needs to read the old efi_info
from boot_params. Currently it exists in debugfs which is not a good
place for such infomation. Per HPA, we should avoid "sploit debugfs".

In this patch /sys/kernel/boot_params are exported, also the setup_data is
exported as a subdirectory. kexec-tools is using debugfs for hardware_subarch
for a long time now so we're not removing it yet.

Structure is like below:

/sys/kernel/boot_params
|__ data /* boot_params in binary*/
|__ setup_data
| |__ 0 /* the first setup_data node */
| | |__ data /* setup_data node 0 in binary*/
| | |__ type /* setup_data type of setup_data node 0, hex string */
[snip]
|__ version /* boot protocal version (in hex, "0x" prefixed)*/

Signed-off-by: Dave Young <dyoung@redhat.com>
Acked-by: Borislav Petkov <bp@suse.de>
Tested-by: Toshi Kani <toshi.kani@hp.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>

authored by

Dave Young and committed by
Matt Fleming
5039e316 456a29dd

+378
+38
Documentation/ABI/testing/sysfs-kernel-boot_params
··· 1 + What: /sys/kernel/boot_params 2 + Date: December 2013 3 + Contact: Dave Young <dyoung@redhat.com> 4 + Description: 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 + 38 + Users: Kexec
+1
arch/x86/kernel/Makefile
··· 29 29 obj-y += syscall_$(BITS).o 30 30 obj-$(CONFIG_X86_64) += vsyscall_64.o 31 31 obj-$(CONFIG_X86_64) += vsyscall_emu_64.o 32 + obj-$(CONFIG_SYSFS) += ksysfs.o 32 33 obj-y += bootflag.o e820.o 33 34 obj-y += pci-dma.o quirks.o topology.o kdebugfs.o 34 35 obj-y += alternative.o i8253.o pci-nommu.o hw_breakpoint.o
+339
arch/x86/kernel/ksysfs.c
··· 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);