diff options
-rw-r--r-- | Documentation/ABI/testing/sysfs-firmware-efi-runtime-map | 34 | ||||
-rw-r--r-- | arch/x86/platform/efi/efi.c | 46 | ||||
-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 | ||||
-rw-r--r-- | include/linux/efi.h | 13 |
7 files changed, 287 insertions, 3 deletions
diff --git a/Documentation/ABI/testing/sysfs-firmware-efi-runtime-map b/Documentation/ABI/testing/sysfs-firmware-efi-runtime-map new file mode 100644 index 000000000000..c61b9b348e99 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-firmware-efi-runtime-map | |||
@@ -0,0 +1,34 @@ | |||
1 | What: /sys/firmware/efi/runtime-map/ | ||
2 | Date: December 2013 | ||
3 | Contact: Dave Young <dyoung@redhat.com> | ||
4 | Description: Switching efi runtime services to virtual mode requires | ||
5 | that all efi memory ranges which have the runtime attribute | ||
6 | bit set to be mapped to virtual addresses. | ||
7 | |||
8 | The efi runtime services can only be switched to virtual | ||
9 | mode once without rebooting. The kexec kernel must maintain | ||
10 | the same physical to virtual address mappings as the first | ||
11 | kernel. The mappings are exported to sysfs so userspace tools | ||
12 | can reassemble them and pass them into the kexec kernel. | ||
13 | |||
14 | /sys/firmware/efi/runtime-map/ is the directory the kernel | ||
15 | exports that information in. | ||
16 | |||
17 | subdirectories are named with the number of the memory range: | ||
18 | |||
19 | /sys/firmware/efi/runtime-map/0 | ||
20 | /sys/firmware/efi/runtime-map/1 | ||
21 | /sys/firmware/efi/runtime-map/2 | ||
22 | /sys/firmware/efi/runtime-map/3 | ||
23 | ... | ||
24 | |||
25 | Each subdirectory contains five files: | ||
26 | |||
27 | attribute : The attributes of the memory range. | ||
28 | num_pages : The size of the memory range in pages. | ||
29 | phys_addr : The physical address of the memory range. | ||
30 | type : The type of the memory range. | ||
31 | virt_addr : The virtual address of the memory range. | ||
32 | |||
33 | Above values are all hexadecimal numbers with the '0x' prefix. | ||
34 | Users: Kexec | ||
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 28591072fbb7..74fe7a719508 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c | |||
@@ -76,6 +76,9 @@ static __initdata efi_config_table_type_t arch_tables[] = { | |||
76 | {NULL_GUID, NULL, NULL}, | 76 | {NULL_GUID, NULL, NULL}, |
77 | }; | 77 | }; |
78 | 78 | ||
79 | static void *efi_runtime_map; | ||
80 | static int nr_efi_runtime_map; | ||
81 | |||
79 | /* | 82 | /* |
80 | * Returns 1 if 'facility' is enabled, 0 otherwise. | 83 | * Returns 1 if 'facility' is enabled, 0 otherwise. |
81 | */ | 84 | */ |
@@ -824,6 +827,39 @@ static void __init get_systab_virt_addr(efi_memory_desc_t *md) | |||
824 | } | 827 | } |
825 | } | 828 | } |
826 | 829 | ||
830 | static int __init save_runtime_map(void) | ||
831 | { | ||
832 | efi_memory_desc_t *md; | ||
833 | void *tmp, *p, *q = NULL; | ||
834 | int count = 0; | ||
835 | |||
836 | for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { | ||
837 | md = p; | ||
838 | |||
839 | if (!(md->attribute & EFI_MEMORY_RUNTIME) || | ||
840 | (md->type == EFI_BOOT_SERVICES_CODE) || | ||
841 | (md->type == EFI_BOOT_SERVICES_DATA)) | ||
842 | continue; | ||
843 | tmp = krealloc(q, (count + 1) * memmap.desc_size, GFP_KERNEL); | ||
844 | if (!tmp) | ||
845 | goto out; | ||
846 | q = tmp; | ||
847 | |||
848 | memcpy(q + count * memmap.desc_size, md, memmap.desc_size); | ||
849 | count++; | ||
850 | } | ||
851 | |||
852 | efi_runtime_map = q; | ||
853 | nr_efi_runtime_map = count; | ||
854 | efi_runtime_map_setup(efi_runtime_map, nr_efi_runtime_map, | ||
855 | boot_params.efi_info.efi_memdesc_size); | ||
856 | |||
857 | return 0; | ||
858 | out: | ||
859 | kfree(q); | ||
860 | return -ENOMEM; | ||
861 | } | ||
862 | |||
827 | /* | 863 | /* |
828 | * Map efi memory ranges for runtime serivce and update new_memmap with virtual | 864 | * Map efi memory ranges for runtime serivce and update new_memmap with virtual |
829 | * addresses. | 865 | * addresses. |
@@ -849,7 +885,7 @@ static void * __init efi_map_regions(int *count) | |||
849 | tmp = krealloc(new_memmap, (*count + 1) * memmap.desc_size, | 885 | tmp = krealloc(new_memmap, (*count + 1) * memmap.desc_size, |
850 | GFP_KERNEL); | 886 | GFP_KERNEL); |
851 | if (!tmp) | 887 | if (!tmp) |
852 | goto out_krealloc; | 888 | goto out; |
853 | new_memmap = tmp; | 889 | new_memmap = tmp; |
854 | memcpy(new_memmap + (*count * memmap.desc_size), md, | 890 | memcpy(new_memmap + (*count * memmap.desc_size), md, |
855 | memmap.desc_size); | 891 | memmap.desc_size); |
@@ -857,7 +893,7 @@ static void * __init efi_map_regions(int *count) | |||
857 | } | 893 | } |
858 | 894 | ||
859 | return new_memmap; | 895 | return new_memmap; |
860 | out_krealloc: | 896 | out: |
861 | kfree(new_memmap); | 897 | kfree(new_memmap); |
862 | return NULL; | 898 | return NULL; |
863 | } | 899 | } |
@@ -883,7 +919,7 @@ void __init efi_enter_virtual_mode(void) | |||
883 | { | 919 | { |
884 | efi_status_t status; | 920 | efi_status_t status; |
885 | void *new_memmap = NULL; | 921 | void *new_memmap = NULL; |
886 | int count = 0; | 922 | int err, count = 0; |
887 | 923 | ||
888 | efi.systab = NULL; | 924 | efi.systab = NULL; |
889 | 925 | ||
@@ -904,6 +940,10 @@ void __init efi_enter_virtual_mode(void) | |||
904 | return; | 940 | return; |
905 | } | 941 | } |
906 | 942 | ||
943 | err = save_runtime_map(); | ||
944 | if (err) | ||
945 | pr_err("Error saving runtime map, efi runtime on kexec non-functional!!\n"); | ||
946 | |||
907 | BUG_ON(!efi.systab); | 947 | BUG_ON(!efi.systab); |
908 | 948 | ||
909 | efi_setup_page_tables(); | 949 | efi_setup_page_tables(); |
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 | } | ||
diff --git a/include/linux/efi.h b/include/linux/efi.h index fb60b10b7bd9..e64540746c63 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h | |||
@@ -872,4 +872,17 @@ int efivars_sysfs_init(void); | |||
872 | 872 | ||
873 | #endif /* CONFIG_EFI_VARS */ | 873 | #endif /* CONFIG_EFI_VARS */ |
874 | 874 | ||
875 | #ifdef CONFIG_EFI_RUNTIME_MAP | ||
876 | int efi_runtime_map_init(struct kobject *); | ||
877 | void efi_runtime_map_setup(void *, int, u32); | ||
878 | #else | ||
879 | static inline int efi_runtime_map_init(struct kobject *kobj) | ||
880 | { | ||
881 | return 0; | ||
882 | } | ||
883 | |||
884 | static inline void | ||
885 | efi_runtime_map_setup(void *map, int nr_entries, u32 desc_size) {} | ||
886 | #endif | ||
887 | |||
875 | #endif /* _LINUX_EFI_H */ | 888 | #endif /* _LINUX_EFI_H */ |