-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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>
- Loading branch information
Dave Young
authored and
Matt Fleming
committed
Dec 29, 2013
1 parent
456a29d
commit 5039e31
Showing
3 changed files
with
378 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
What: /sys/kernel/boot_params | ||
Date: December 2013 | ||
Contact: Dave Young <dyoung@redhat.com> | ||
Description: The /sys/kernel/boot_params directory contains two | ||
files: "data" and "version" and one subdirectory "setup_data". | ||
It is used to export the kernel boot parameters of an x86 | ||
platform to userspace for kexec and debugging purpose. | ||
|
||
If there's no setup_data in boot_params the subdirectory will | ||
not be created. | ||
|
||
"data" file is the binary representation of struct boot_params. | ||
|
||
"version" file is the string representation of boot | ||
protocol version. | ||
|
||
"setup_data" subdirectory contains the setup_data data | ||
structure in boot_params. setup_data is maintained in kernel | ||
as a link list. In "setup_data" subdirectory there's one | ||
subdirectory for each link list node named with the number | ||
of the list nodes. The list node subdirectory contains two | ||
files "type" and "data". "type" file is the string | ||
representation of setup_data type. "data" file is the binary | ||
representation of setup_data payload. | ||
|
||
The whole boot_params directory structure is like below: | ||
/sys/kernel/boot_params | ||
|__ data | ||
|__ setup_data | ||
| |__ 0 | ||
| | |__ data | ||
| | |__ type | ||
| |__ 1 | ||
| |__ data | ||
| |__ type | ||
|__ version | ||
|
||
Users: Kexec |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,339 @@ | ||
/* | ||
* Architecture specific sysfs attributes in /sys/kernel | ||
* | ||
* Copyright (C) 2007, Intel Corp. | ||
* Huang Ying <ying.huang@intel.com> | ||
* Copyright (C) 2013, 2013 Red Hat, Inc. | ||
* Dave Young <dyoung@redhat.com> | ||
* | ||
* This file is released under the GPLv2 | ||
*/ | ||
|
||
#include <linux/kobject.h> | ||
#include <linux/string.h> | ||
#include <linux/sysfs.h> | ||
#include <linux/init.h> | ||
#include <linux/stat.h> | ||
#include <linux/slab.h> | ||
#include <linux/mm.h> | ||
|
||
#include <asm/setup.h> | ||
|
||
static ssize_t version_show(struct kobject *kobj, | ||
struct kobj_attribute *attr, char *buf) | ||
{ | ||
return sprintf(buf, "0x%04x\n", boot_params.hdr.version); | ||
} | ||
|
||
static struct kobj_attribute boot_params_version_attr = __ATTR_RO(version); | ||
|
||
static ssize_t boot_params_data_read(struct file *fp, struct kobject *kobj, | ||
struct bin_attribute *bin_attr, | ||
char *buf, loff_t off, size_t count) | ||
{ | ||
memcpy(buf, (void *)&boot_params + off, count); | ||
return count; | ||
} | ||
|
||
static struct bin_attribute boot_params_data_attr = { | ||
.attr = { | ||
.name = "data", | ||
.mode = S_IRUGO, | ||
}, | ||
.read = boot_params_data_read, | ||
.size = sizeof(boot_params), | ||
}; | ||
|
||
static struct attribute *boot_params_version_attrs[] = { | ||
&boot_params_version_attr.attr, | ||
NULL, | ||
}; | ||
|
||
static struct bin_attribute *boot_params_data_attrs[] = { | ||
&boot_params_data_attr, | ||
NULL, | ||
}; | ||
|
||
static struct attribute_group boot_params_attr_group = { | ||
.attrs = boot_params_version_attrs, | ||
.bin_attrs = boot_params_data_attrs, | ||
}; | ||
|
||
static int kobj_to_setup_data_nr(struct kobject *kobj, int *nr) | ||
{ | ||
const char *name; | ||
|
||
name = kobject_name(kobj); | ||
return kstrtoint(name, 10, nr); | ||
} | ||
|
||
static int get_setup_data_paddr(int nr, u64 *paddr) | ||
{ | ||
int i = 0; | ||
struct setup_data *data; | ||
u64 pa_data = boot_params.hdr.setup_data; | ||
|
||
while (pa_data) { | ||
if (nr == i) { | ||
*paddr = pa_data; | ||
return 0; | ||
} | ||
data = ioremap_cache(pa_data, sizeof(*data)); | ||
if (!data) | ||
return -ENOMEM; | ||
|
||
pa_data = data->next; | ||
iounmap(data); | ||
i++; | ||
} | ||
return -EINVAL; | ||
} | ||
|
||
static int __init get_setup_data_size(int nr, size_t *size) | ||
{ | ||
int i = 0; | ||
struct setup_data *data; | ||
u64 pa_data = boot_params.hdr.setup_data; | ||
|
||
while (pa_data) { | ||
data = ioremap_cache(pa_data, sizeof(*data)); | ||
if (!data) | ||
return -ENOMEM; | ||
if (nr == i) { | ||
*size = data->len; | ||
iounmap(data); | ||
return 0; | ||
} | ||
|
||
pa_data = data->next; | ||
iounmap(data); | ||
i++; | ||
} | ||
return -EINVAL; | ||
} | ||
|
||
static ssize_t type_show(struct kobject *kobj, | ||
struct kobj_attribute *attr, char *buf) | ||
{ | ||
int nr, ret; | ||
u64 paddr; | ||
struct setup_data *data; | ||
|
||
ret = kobj_to_setup_data_nr(kobj, &nr); | ||
if (ret) | ||
return ret; | ||
|
||
ret = get_setup_data_paddr(nr, &paddr); | ||
if (ret) | ||
return ret; | ||
data = ioremap_cache(paddr, sizeof(*data)); | ||
if (!data) | ||
return -ENOMEM; | ||
|
||
ret = sprintf(buf, "0x%x\n", data->type); | ||
iounmap(data); | ||
return ret; | ||
} | ||
|
||
static ssize_t setup_data_data_read(struct file *fp, | ||
struct kobject *kobj, | ||
struct bin_attribute *bin_attr, | ||
char *buf, | ||
loff_t off, size_t count) | ||
{ | ||
int nr, ret = 0; | ||
u64 paddr; | ||
struct setup_data *data; | ||
void *p; | ||
|
||
ret = kobj_to_setup_data_nr(kobj, &nr); | ||
if (ret) | ||
return ret; | ||
|
||
ret = get_setup_data_paddr(nr, &paddr); | ||
if (ret) | ||
return ret; | ||
data = ioremap_cache(paddr, sizeof(*data)); | ||
if (!data) | ||
return -ENOMEM; | ||
|
||
if (off > data->len) { | ||
ret = -EINVAL; | ||
goto out; | ||
} | ||
|
||
if (count > data->len - off) | ||
count = data->len - off; | ||
|
||
if (!count) | ||
goto out; | ||
|
||
ret = count; | ||
p = ioremap_cache(paddr + sizeof(*data), data->len); | ||
if (!p) { | ||
ret = -ENOMEM; | ||
goto out; | ||
} | ||
memcpy(buf, p + off, count); | ||
iounmap(p); | ||
out: | ||
iounmap(data); | ||
return ret; | ||
} | ||
|
||
static struct kobj_attribute type_attr = __ATTR_RO(type); | ||
|
||
static struct bin_attribute data_attr = { | ||
.attr = { | ||
.name = "data", | ||
.mode = S_IRUGO, | ||
}, | ||
.read = setup_data_data_read, | ||
}; | ||
|
||
static struct attribute *setup_data_type_attrs[] = { | ||
&type_attr.attr, | ||
NULL, | ||
}; | ||
|
||
static struct bin_attribute *setup_data_data_attrs[] = { | ||
&data_attr, | ||
NULL, | ||
}; | ||
|
||
static struct attribute_group setup_data_attr_group = { | ||
.attrs = setup_data_type_attrs, | ||
.bin_attrs = setup_data_data_attrs, | ||
}; | ||
|
||
static int __init create_setup_data_node(struct kobject *parent, | ||
struct kobject **kobjp, int nr) | ||
{ | ||
int ret = 0; | ||
size_t size; | ||
struct kobject *kobj; | ||
char name[16]; /* should be enough for setup_data nodes numbers */ | ||
snprintf(name, 16, "%d", nr); | ||
|
||
kobj = kobject_create_and_add(name, parent); | ||
if (!kobj) | ||
return -ENOMEM; | ||
|
||
ret = get_setup_data_size(nr, &size); | ||
if (ret) | ||
goto out_kobj; | ||
|
||
data_attr.size = size; | ||
ret = sysfs_create_group(kobj, &setup_data_attr_group); | ||
if (ret) | ||
goto out_kobj; | ||
*kobjp = kobj; | ||
|
||
return 0; | ||
out_kobj: | ||
kobject_put(kobj); | ||
return ret; | ||
} | ||
|
||
static void __init cleanup_setup_data_node(struct kobject *kobj) | ||
{ | ||
sysfs_remove_group(kobj, &setup_data_attr_group); | ||
kobject_put(kobj); | ||
} | ||
|
||
static int __init get_setup_data_total_num(u64 pa_data, int *nr) | ||
{ | ||
int ret = 0; | ||
struct setup_data *data; | ||
|
||
*nr = 0; | ||
while (pa_data) { | ||
*nr += 1; | ||
data = ioremap_cache(pa_data, sizeof(*data)); | ||
if (!data) { | ||
ret = -ENOMEM; | ||
goto out; | ||
} | ||
pa_data = data->next; | ||
iounmap(data); | ||
} | ||
|
||
out: | ||
return ret; | ||
} | ||
|
||
static int __init create_setup_data_nodes(struct kobject *parent) | ||
{ | ||
struct kobject *setup_data_kobj, **kobjp; | ||
u64 pa_data; | ||
int i, j, nr, ret = 0; | ||
|
||
pa_data = boot_params.hdr.setup_data; | ||
if (!pa_data) | ||
return 0; | ||
|
||
setup_data_kobj = kobject_create_and_add("setup_data", parent); | ||
if (!setup_data_kobj) { | ||
ret = -ENOMEM; | ||
goto out; | ||
} | ||
|
||
ret = get_setup_data_total_num(pa_data, &nr); | ||
if (ret) | ||
goto out_setup_data_kobj; | ||
|
||
kobjp = kmalloc(sizeof(*kobjp) * nr, GFP_KERNEL); | ||
if (!kobjp) { | ||
ret = -ENOMEM; | ||
goto out_setup_data_kobj; | ||
} | ||
|
||
for (i = 0; i < nr; i++) { | ||
ret = create_setup_data_node(setup_data_kobj, kobjp + i, i); | ||
if (ret) | ||
goto out_clean_nodes; | ||
} | ||
|
||
kfree(kobjp); | ||
return 0; | ||
|
||
out_clean_nodes: | ||
for (j = i - 1; j > 0; j--) | ||
cleanup_setup_data_node(*(kobjp + j)); | ||
kfree(kobjp); | ||
out_setup_data_kobj: | ||
kobject_put(setup_data_kobj); | ||
out: | ||
return ret; | ||
} | ||
|
||
static int __init boot_params_ksysfs_init(void) | ||
{ | ||
int ret; | ||
struct kobject *boot_params_kobj; | ||
|
||
boot_params_kobj = kobject_create_and_add("boot_params", | ||
kernel_kobj); | ||
if (!boot_params_kobj) { | ||
ret = -ENOMEM; | ||
goto out; | ||
} | ||
|
||
ret = sysfs_create_group(boot_params_kobj, &boot_params_attr_group); | ||
if (ret) | ||
goto out_boot_params_kobj; | ||
|
||
ret = create_setup_data_nodes(boot_params_kobj); | ||
if (ret) | ||
goto out_create_group; | ||
|
||
return 0; | ||
out_create_group: | ||
sysfs_remove_group(boot_params_kobj, &boot_params_attr_group); | ||
out_boot_params_kobj: | ||
kobject_put(boot_params_kobj); | ||
out: | ||
return ret; | ||
} | ||
|
||
arch_initcall(boot_params_ksysfs_init); |