diff options
author | Dave Young <dyoung@redhat.com> | 2013-12-20 05:02:18 -0500 |
---|---|---|
committer | Matt Fleming <matt.fleming@intel.com> | 2013-12-21 10:29:36 -0500 |
commit | 926172d46038d7610b6b8d84e40db727cefb482d (patch) | |
tree | 8963aaa9fccd0a4ee0d59717b4de8e8cdfebbe62 /drivers/firmware | |
parent | a0998eb15afeffbf52a2c2829318f67df9ac57b8 (diff) |
efi: Export EFI runtime memory mapping to sysfs
kexec kernel will need exactly same mapping for EFI runtime memory
ranges. Thus here export the runtime ranges mapping to sysfs,
kexec-tools will assemble them and pass to 2nd kernel via setup_data.
Introducing a new directory /sys/firmware/efi/runtime-map just like
/sys/firmware/memmap. Containing below attribute in each file of that
directory:
attribute num_pages phys_addr type virt_addr
Signed-off-by: Dave Young <dyoung@redhat.com>
Tested-by: Toshi Kani <toshi.kani@hp.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Diffstat (limited to 'drivers/firmware')
-rw-r--r-- | drivers/firmware/efi/Kconfig | 11 | ||||
-rw-r--r-- | drivers/firmware/efi/Makefile | 1 | ||||
-rw-r--r-- | drivers/firmware/efi/efi.c | 4 | ||||
-rw-r--r-- | drivers/firmware/efi/runtime-map.c | 181 |
4 files changed, 197 insertions, 0 deletions
diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 3150aa4874e8..730f5f2e8b7f 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig | |||
@@ -39,4 +39,15 @@ config EFI_VARS_PSTORE_DEFAULT_DISABLE | |||
39 | config UEFI_CPER | 39 | config UEFI_CPER |
40 | def_bool n | 40 | def_bool n |
41 | 41 | ||
42 | config EFI_RUNTIME_MAP | ||
43 | bool "Export efi runtime maps to sysfs" | ||
44 | depends on X86 && EFI && KEXEC | ||
45 | default y | ||
46 | help | ||
47 | Export efi runtime memory maps to /sys/firmware/efi/runtime-map. | ||
48 | That memory map is used for example by kexec to set up efi virtual | ||
49 | mapping the 2nd kernel, but can also be used for debugging purposes. | ||
50 | |||
51 | See also Documentation/ABI/testing/sysfs-firmware-efi-runtime-map. | ||
52 | |||
42 | endmenu | 53 | endmenu |
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index 9ba156d3c775..a58e0f183a08 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile | |||
@@ -5,3 +5,4 @@ obj-y += efi.o vars.o | |||
5 | obj-$(CONFIG_EFI_VARS) += efivars.o | 5 | obj-$(CONFIG_EFI_VARS) += efivars.o |
6 | obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o | 6 | obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o |
7 | obj-$(CONFIG_UEFI_CPER) += cper.o | 7 | obj-$(CONFIG_UEFI_CPER) += cper.o |
8 | obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o | ||
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 72533af72b98..4753bac65279 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c | |||
@@ -167,6 +167,10 @@ static int __init efisubsys_init(void) | |||
167 | goto err_unregister; | 167 | goto err_unregister; |
168 | } | 168 | } |
169 | 169 | ||
170 | error = efi_runtime_map_init(efi_kobj); | ||
171 | if (error) | ||
172 | goto err_remove_group; | ||
173 | |||
170 | /* and the standard mountpoint for efivarfs */ | 174 | /* and the standard mountpoint for efivarfs */ |
171 | efivars_kobj = kobject_create_and_add("efivars", efi_kobj); | 175 | efivars_kobj = kobject_create_and_add("efivars", efi_kobj); |
172 | if (!efivars_kobj) { | 176 | if (!efivars_kobj) { |
diff --git a/drivers/firmware/efi/runtime-map.c b/drivers/firmware/efi/runtime-map.c new file mode 100644 index 000000000000..97cdd16a2169 --- /dev/null +++ b/drivers/firmware/efi/runtime-map.c | |||
@@ -0,0 +1,181 @@ | |||
1 | /* | ||
2 | * linux/drivers/efi/runtime-map.c | ||
3 | * Copyright (C) 2013 Red Hat, Inc., Dave Young <dyoung@redhat.com> | ||
4 | * | ||
5 | * This file is released under the GPLv2. | ||
6 | */ | ||
7 | |||
8 | #include <linux/string.h> | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/types.h> | ||
12 | #include <linux/efi.h> | ||
13 | #include <linux/slab.h> | ||
14 | |||
15 | #include <asm/setup.h> | ||
16 | |||
17 | static void *efi_runtime_map; | ||
18 | static int nr_efi_runtime_map; | ||
19 | static u32 efi_memdesc_size; | ||
20 | |||
21 | struct efi_runtime_map_entry { | ||
22 | efi_memory_desc_t md; | ||
23 | struct kobject kobj; /* kobject for each entry */ | ||
24 | }; | ||
25 | |||
26 | static struct efi_runtime_map_entry **map_entries; | ||
27 | |||
28 | struct map_attribute { | ||
29 | struct attribute attr; | ||
30 | ssize_t (*show)(struct efi_runtime_map_entry *entry, char *buf); | ||
31 | }; | ||
32 | |||
33 | static inline struct map_attribute *to_map_attr(struct attribute *attr) | ||
34 | { | ||
35 | return container_of(attr, struct map_attribute, attr); | ||
36 | } | ||
37 | |||
38 | static ssize_t type_show(struct efi_runtime_map_entry *entry, char *buf) | ||
39 | { | ||
40 | return snprintf(buf, PAGE_SIZE, "0x%x\n", entry->md.type); | ||
41 | } | ||
42 | |||
43 | #define EFI_RUNTIME_FIELD(var) entry->md.var | ||
44 | |||
45 | #define EFI_RUNTIME_U64_ATTR_SHOW(name) \ | ||
46 | static ssize_t name##_show(struct efi_runtime_map_entry *entry, char *buf) \ | ||
47 | { \ | ||
48 | return snprintf(buf, PAGE_SIZE, "0x%llx\n", EFI_RUNTIME_FIELD(name)); \ | ||
49 | } | ||
50 | |||
51 | EFI_RUNTIME_U64_ATTR_SHOW(phys_addr); | ||
52 | EFI_RUNTIME_U64_ATTR_SHOW(virt_addr); | ||
53 | EFI_RUNTIME_U64_ATTR_SHOW(num_pages); | ||
54 | EFI_RUNTIME_U64_ATTR_SHOW(attribute); | ||
55 | |||
56 | static inline struct efi_runtime_map_entry *to_map_entry(struct kobject *kobj) | ||
57 | { | ||
58 | return container_of(kobj, struct efi_runtime_map_entry, kobj); | ||
59 | } | ||
60 | |||
61 | static ssize_t map_attr_show(struct kobject *kobj, struct attribute *attr, | ||
62 | char *buf) | ||
63 | { | ||
64 | struct efi_runtime_map_entry *entry = to_map_entry(kobj); | ||
65 | struct map_attribute *map_attr = to_map_attr(attr); | ||
66 | |||
67 | return map_attr->show(entry, buf); | ||
68 | } | ||
69 | |||
70 | static struct map_attribute map_type_attr = __ATTR_RO(type); | ||
71 | static struct map_attribute map_phys_addr_attr = __ATTR_RO(phys_addr); | ||
72 | static struct map_attribute map_virt_addr_attr = __ATTR_RO(virt_addr); | ||
73 | static struct map_attribute map_num_pages_attr = __ATTR_RO(num_pages); | ||
74 | static struct map_attribute map_attribute_attr = __ATTR_RO(attribute); | ||
75 | |||
76 | /* | ||
77 | * These are default attributes that are added for every memmap entry. | ||
78 | */ | ||
79 | static struct attribute *def_attrs[] = { | ||
80 | &map_type_attr.attr, | ||
81 | &map_phys_addr_attr.attr, | ||
82 | &map_virt_addr_attr.attr, | ||
83 | &map_num_pages_attr.attr, | ||
84 | &map_attribute_attr.attr, | ||
85 | NULL | ||
86 | }; | ||
87 | |||
88 | static const struct sysfs_ops map_attr_ops = { | ||
89 | .show = map_attr_show, | ||
90 | }; | ||
91 | |||
92 | static void map_release(struct kobject *kobj) | ||
93 | { | ||
94 | struct efi_runtime_map_entry *entry; | ||
95 | |||
96 | entry = to_map_entry(kobj); | ||
97 | kfree(entry); | ||
98 | } | ||
99 | |||
100 | static struct kobj_type __refdata map_ktype = { | ||
101 | .sysfs_ops = &map_attr_ops, | ||
102 | .default_attrs = def_attrs, | ||
103 | .release = map_release, | ||
104 | }; | ||
105 | |||
106 | static struct kset *map_kset; | ||
107 | |||
108 | static struct efi_runtime_map_entry * | ||
109 | add_sysfs_runtime_map_entry(struct kobject *kobj, int nr) | ||
110 | { | ||
111 | int ret; | ||
112 | struct efi_runtime_map_entry *entry; | ||
113 | |||
114 | if (!map_kset) { | ||
115 | map_kset = kset_create_and_add("runtime-map", NULL, kobj); | ||
116 | if (!map_kset) | ||
117 | return ERR_PTR(-ENOMEM); | ||
118 | } | ||
119 | |||
120 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); | ||
121 | if (!entry) { | ||
122 | kset_unregister(map_kset); | ||
123 | return entry; | ||
124 | } | ||
125 | |||
126 | memcpy(&entry->md, efi_runtime_map + nr * efi_memdesc_size, | ||
127 | sizeof(efi_memory_desc_t)); | ||
128 | |||
129 | kobject_init(&entry->kobj, &map_ktype); | ||
130 | entry->kobj.kset = map_kset; | ||
131 | ret = kobject_add(&entry->kobj, NULL, "%d", nr); | ||
132 | if (ret) { | ||
133 | kobject_put(&entry->kobj); | ||
134 | kset_unregister(map_kset); | ||
135 | return ERR_PTR(ret); | ||
136 | } | ||
137 | |||
138 | return entry; | ||
139 | } | ||
140 | |||
141 | void efi_runtime_map_setup(void *map, int nr_entries, u32 desc_size) | ||
142 | { | ||
143 | efi_runtime_map = map; | ||
144 | nr_efi_runtime_map = nr_entries; | ||
145 | efi_memdesc_size = desc_size; | ||
146 | } | ||
147 | |||
148 | int __init efi_runtime_map_init(struct kobject *efi_kobj) | ||
149 | { | ||
150 | int i, j, ret = 0; | ||
151 | struct efi_runtime_map_entry *entry; | ||
152 | |||
153 | if (!efi_runtime_map) | ||
154 | return 0; | ||
155 | |||
156 | map_entries = kzalloc(nr_efi_runtime_map * sizeof(entry), GFP_KERNEL); | ||
157 | if (!map_entries) { | ||
158 | ret = -ENOMEM; | ||
159 | goto out; | ||
160 | } | ||
161 | |||
162 | for (i = 0; i < nr_efi_runtime_map; i++) { | ||
163 | entry = add_sysfs_runtime_map_entry(efi_kobj, i); | ||
164 | if (IS_ERR(entry)) { | ||
165 | ret = PTR_ERR(entry); | ||
166 | goto out_add_entry; | ||
167 | } | ||
168 | *(map_entries + i) = entry; | ||
169 | } | ||
170 | |||
171 | return 0; | ||
172 | out_add_entry: | ||
173 | for (j = i - 1; j > 0; j--) { | ||
174 | entry = *(map_entries + j); | ||
175 | kobject_put(&entry->kobj); | ||
176 | } | ||
177 | if (map_kset) | ||
178 | kset_unregister(map_kset); | ||
179 | out: | ||
180 | return ret; | ||
181 | } | ||