diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-01-15 13:54:39 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-01-15 13:54:39 -0500 |
commit | 255e6140fa76ec9d0e24f201427e7e9a9573f681 (patch) | |
tree | 242eda3c33f43faff66591f1e125169bb9a602bb /drivers/firmware | |
parent | f4d3935e4f4884ba80561db5549394afb8eef8f7 (diff) | |
parent | 0100a3e67a9cef64d72cd3a1da86f3ddbee50363 (diff) |
Merge branch 'efi-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull EFI fixes from Ingo Molnar:
"A number of regression fixes:
- Fix a boot hang on machines that have somewhat unusual memory map
entries of phys_addr=0x0 num_pages=0, which broke due to a recent
commit. This commit got cherry-picked from the v4.11 queue because
the bug is affecting real machines.
- Fix a boot hang also reported by KASAN, caused by incorrect init
ordering introduced by a recent optimization.
- Fix a recent robustification fix to allocate_new_fdt_and_exit_boot()
that introduced an invalid assumption. Neither bugs were seen in
the wild AFAIK"
* 'efi-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
efi/x86: Prune invalid memory map entries and fix boot regression
x86/efi: Don't allocate memmap through memblock after mm_init()
efi/libstub/arm*: Pass latest memory map to the kernel
Diffstat (limited to 'drivers/firmware')
-rw-r--r-- | drivers/firmware/efi/fake_mem.c | 3 | ||||
-rw-r--r-- | drivers/firmware/efi/libstub/efistub.h | 8 | ||||
-rw-r--r-- | drivers/firmware/efi/libstub/fdt.c | 87 | ||||
-rw-r--r-- | drivers/firmware/efi/memmap.c | 38 |
4 files changed, 95 insertions, 41 deletions
diff --git a/drivers/firmware/efi/fake_mem.c b/drivers/firmware/efi/fake_mem.c index 520a40e5e0e4..6c7d60c239b5 100644 --- a/drivers/firmware/efi/fake_mem.c +++ b/drivers/firmware/efi/fake_mem.c | |||
@@ -71,8 +71,7 @@ void __init efi_fake_memmap(void) | |||
71 | } | 71 | } |
72 | 72 | ||
73 | /* allocate memory for new EFI memmap */ | 73 | /* allocate memory for new EFI memmap */ |
74 | new_memmap_phy = memblock_alloc(efi.memmap.desc_size * new_nr_map, | 74 | new_memmap_phy = efi_memmap_alloc(new_nr_map); |
75 | PAGE_SIZE); | ||
76 | if (!new_memmap_phy) | 75 | if (!new_memmap_phy) |
77 | return; | 76 | return; |
78 | 77 | ||
diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index b98824e3800a..0e2a96b12cb3 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h | |||
@@ -39,14 +39,6 @@ efi_status_t efi_file_close(void *handle); | |||
39 | 39 | ||
40 | unsigned long get_dram_base(efi_system_table_t *sys_table_arg); | 40 | unsigned long get_dram_base(efi_system_table_t *sys_table_arg); |
41 | 41 | ||
42 | efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, | ||
43 | unsigned long orig_fdt_size, | ||
44 | void *fdt, int new_fdt_size, char *cmdline_ptr, | ||
45 | u64 initrd_addr, u64 initrd_size, | ||
46 | efi_memory_desc_t *memory_map, | ||
47 | unsigned long map_size, unsigned long desc_size, | ||
48 | u32 desc_ver); | ||
49 | |||
50 | efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, | 42 | efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, |
51 | void *handle, | 43 | void *handle, |
52 | unsigned long *new_fdt_addr, | 44 | unsigned long *new_fdt_addr, |
diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c index a6a93116a8f0..921dfa047202 100644 --- a/drivers/firmware/efi/libstub/fdt.c +++ b/drivers/firmware/efi/libstub/fdt.c | |||
@@ -16,13 +16,10 @@ | |||
16 | 16 | ||
17 | #include "efistub.h" | 17 | #include "efistub.h" |
18 | 18 | ||
19 | efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, | 19 | static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, |
20 | unsigned long orig_fdt_size, | 20 | unsigned long orig_fdt_size, |
21 | void *fdt, int new_fdt_size, char *cmdline_ptr, | 21 | void *fdt, int new_fdt_size, char *cmdline_ptr, |
22 | u64 initrd_addr, u64 initrd_size, | 22 | u64 initrd_addr, u64 initrd_size) |
23 | efi_memory_desc_t *memory_map, | ||
24 | unsigned long map_size, unsigned long desc_size, | ||
25 | u32 desc_ver) | ||
26 | { | 23 | { |
27 | int node, num_rsv; | 24 | int node, num_rsv; |
28 | int status; | 25 | int status; |
@@ -101,25 +98,23 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, | |||
101 | if (status) | 98 | if (status) |
102 | goto fdt_set_fail; | 99 | goto fdt_set_fail; |
103 | 100 | ||
104 | fdt_val64 = cpu_to_fdt64((u64)(unsigned long)memory_map); | 101 | fdt_val64 = U64_MAX; /* placeholder */ |
105 | status = fdt_setprop(fdt, node, "linux,uefi-mmap-start", | 102 | status = fdt_setprop(fdt, node, "linux,uefi-mmap-start", |
106 | &fdt_val64, sizeof(fdt_val64)); | 103 | &fdt_val64, sizeof(fdt_val64)); |
107 | if (status) | 104 | if (status) |
108 | goto fdt_set_fail; | 105 | goto fdt_set_fail; |
109 | 106 | ||
110 | fdt_val32 = cpu_to_fdt32(map_size); | 107 | fdt_val32 = U32_MAX; /* placeholder */ |
111 | status = fdt_setprop(fdt, node, "linux,uefi-mmap-size", | 108 | status = fdt_setprop(fdt, node, "linux,uefi-mmap-size", |
112 | &fdt_val32, sizeof(fdt_val32)); | 109 | &fdt_val32, sizeof(fdt_val32)); |
113 | if (status) | 110 | if (status) |
114 | goto fdt_set_fail; | 111 | goto fdt_set_fail; |
115 | 112 | ||
116 | fdt_val32 = cpu_to_fdt32(desc_size); | ||
117 | status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-size", | 113 | status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-size", |
118 | &fdt_val32, sizeof(fdt_val32)); | 114 | &fdt_val32, sizeof(fdt_val32)); |
119 | if (status) | 115 | if (status) |
120 | goto fdt_set_fail; | 116 | goto fdt_set_fail; |
121 | 117 | ||
122 | fdt_val32 = cpu_to_fdt32(desc_ver); | ||
123 | status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-ver", | 118 | status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-ver", |
124 | &fdt_val32, sizeof(fdt_val32)); | 119 | &fdt_val32, sizeof(fdt_val32)); |
125 | if (status) | 120 | if (status) |
@@ -148,6 +143,43 @@ fdt_set_fail: | |||
148 | return EFI_LOAD_ERROR; | 143 | return EFI_LOAD_ERROR; |
149 | } | 144 | } |
150 | 145 | ||
146 | static efi_status_t update_fdt_memmap(void *fdt, struct efi_boot_memmap *map) | ||
147 | { | ||
148 | int node = fdt_path_offset(fdt, "/chosen"); | ||
149 | u64 fdt_val64; | ||
150 | u32 fdt_val32; | ||
151 | int err; | ||
152 | |||
153 | if (node < 0) | ||
154 | return EFI_LOAD_ERROR; | ||
155 | |||
156 | fdt_val64 = cpu_to_fdt64((unsigned long)*map->map); | ||
157 | err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-start", | ||
158 | &fdt_val64, sizeof(fdt_val64)); | ||
159 | if (err) | ||
160 | return EFI_LOAD_ERROR; | ||
161 | |||
162 | fdt_val32 = cpu_to_fdt32(*map->map_size); | ||
163 | err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-size", | ||
164 | &fdt_val32, sizeof(fdt_val32)); | ||
165 | if (err) | ||
166 | return EFI_LOAD_ERROR; | ||
167 | |||
168 | fdt_val32 = cpu_to_fdt32(*map->desc_size); | ||
169 | err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-desc-size", | ||
170 | &fdt_val32, sizeof(fdt_val32)); | ||
171 | if (err) | ||
172 | return EFI_LOAD_ERROR; | ||
173 | |||
174 | fdt_val32 = cpu_to_fdt32(*map->desc_ver); | ||
175 | err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-desc-ver", | ||
176 | &fdt_val32, sizeof(fdt_val32)); | ||
177 | if (err) | ||
178 | return EFI_LOAD_ERROR; | ||
179 | |||
180 | return EFI_SUCCESS; | ||
181 | } | ||
182 | |||
151 | #ifndef EFI_FDT_ALIGN | 183 | #ifndef EFI_FDT_ALIGN |
152 | #define EFI_FDT_ALIGN EFI_PAGE_SIZE | 184 | #define EFI_FDT_ALIGN EFI_PAGE_SIZE |
153 | #endif | 185 | #endif |
@@ -243,20 +275,10 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, | |||
243 | goto fail; | 275 | goto fail; |
244 | } | 276 | } |
245 | 277 | ||
246 | /* | ||
247 | * Now that we have done our final memory allocation (and free) | ||
248 | * we can get the memory map key needed for | ||
249 | * exit_boot_services(). | ||
250 | */ | ||
251 | status = efi_get_memory_map(sys_table, &map); | ||
252 | if (status != EFI_SUCCESS) | ||
253 | goto fail_free_new_fdt; | ||
254 | |||
255 | status = update_fdt(sys_table, | 278 | status = update_fdt(sys_table, |
256 | (void *)fdt_addr, fdt_size, | 279 | (void *)fdt_addr, fdt_size, |
257 | (void *)*new_fdt_addr, new_fdt_size, | 280 | (void *)*new_fdt_addr, new_fdt_size, |
258 | cmdline_ptr, initrd_addr, initrd_size, | 281 | cmdline_ptr, initrd_addr, initrd_size); |
259 | memory_map, map_size, desc_size, desc_ver); | ||
260 | 282 | ||
261 | /* Succeeding the first time is the expected case. */ | 283 | /* Succeeding the first time is the expected case. */ |
262 | if (status == EFI_SUCCESS) | 284 | if (status == EFI_SUCCESS) |
@@ -266,20 +288,16 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, | |||
266 | /* | 288 | /* |
267 | * We need to allocate more space for the new | 289 | * We need to allocate more space for the new |
268 | * device tree, so free existing buffer that is | 290 | * device tree, so free existing buffer that is |
269 | * too small. Also free memory map, as we will need | 291 | * too small. |
270 | * to get new one that reflects the free/alloc we do | ||
271 | * on the device tree buffer. | ||
272 | */ | 292 | */ |
273 | efi_free(sys_table, new_fdt_size, *new_fdt_addr); | 293 | efi_free(sys_table, new_fdt_size, *new_fdt_addr); |
274 | sys_table->boottime->free_pool(memory_map); | ||
275 | new_fdt_size += EFI_PAGE_SIZE; | 294 | new_fdt_size += EFI_PAGE_SIZE; |
276 | } else { | 295 | } else { |
277 | pr_efi_err(sys_table, "Unable to construct new device tree.\n"); | 296 | pr_efi_err(sys_table, "Unable to construct new device tree.\n"); |
278 | goto fail_free_mmap; | 297 | goto fail_free_new_fdt; |
279 | } | 298 | } |
280 | } | 299 | } |
281 | 300 | ||
282 | sys_table->boottime->free_pool(memory_map); | ||
283 | priv.runtime_map = runtime_map; | 301 | priv.runtime_map = runtime_map; |
284 | priv.runtime_entry_count = &runtime_entry_count; | 302 | priv.runtime_entry_count = &runtime_entry_count; |
285 | status = efi_exit_boot_services(sys_table, handle, &map, &priv, | 303 | status = efi_exit_boot_services(sys_table, handle, &map, &priv, |
@@ -288,6 +306,16 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, | |||
288 | if (status == EFI_SUCCESS) { | 306 | if (status == EFI_SUCCESS) { |
289 | efi_set_virtual_address_map_t *svam; | 307 | efi_set_virtual_address_map_t *svam; |
290 | 308 | ||
309 | status = update_fdt_memmap((void *)*new_fdt_addr, &map); | ||
310 | if (status != EFI_SUCCESS) { | ||
311 | /* | ||
312 | * The kernel won't get far without the memory map, but | ||
313 | * may still be able to print something meaningful so | ||
314 | * return success here. | ||
315 | */ | ||
316 | return EFI_SUCCESS; | ||
317 | } | ||
318 | |||
291 | /* Install the new virtual address map */ | 319 | /* Install the new virtual address map */ |
292 | svam = sys_table->runtime->set_virtual_address_map; | 320 | svam = sys_table->runtime->set_virtual_address_map; |
293 | status = svam(runtime_entry_count * desc_size, desc_size, | 321 | status = svam(runtime_entry_count * desc_size, desc_size, |
@@ -319,9 +347,6 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, | |||
319 | 347 | ||
320 | pr_efi_err(sys_table, "Exit boot services failed.\n"); | 348 | pr_efi_err(sys_table, "Exit boot services failed.\n"); |
321 | 349 | ||
322 | fail_free_mmap: | ||
323 | sys_table->boottime->free_pool(memory_map); | ||
324 | |||
325 | fail_free_new_fdt: | 350 | fail_free_new_fdt: |
326 | efi_free(sys_table, new_fdt_size, *new_fdt_addr); | 351 | efi_free(sys_table, new_fdt_size, *new_fdt_addr); |
327 | 352 | ||
diff --git a/drivers/firmware/efi/memmap.c b/drivers/firmware/efi/memmap.c index f03ddecd232b..78686443cb37 100644 --- a/drivers/firmware/efi/memmap.c +++ b/drivers/firmware/efi/memmap.c | |||
@@ -9,6 +9,44 @@ | |||
9 | #include <linux/efi.h> | 9 | #include <linux/efi.h> |
10 | #include <linux/io.h> | 10 | #include <linux/io.h> |
11 | #include <asm/early_ioremap.h> | 11 | #include <asm/early_ioremap.h> |
12 | #include <linux/memblock.h> | ||
13 | #include <linux/slab.h> | ||
14 | |||
15 | static phys_addr_t __init __efi_memmap_alloc_early(unsigned long size) | ||
16 | { | ||
17 | return memblock_alloc(size, 0); | ||
18 | } | ||
19 | |||
20 | static phys_addr_t __init __efi_memmap_alloc_late(unsigned long size) | ||
21 | { | ||
22 | unsigned int order = get_order(size); | ||
23 | struct page *p = alloc_pages(GFP_KERNEL, order); | ||
24 | |||
25 | if (!p) | ||
26 | return 0; | ||
27 | |||
28 | return PFN_PHYS(page_to_pfn(p)); | ||
29 | } | ||
30 | |||
31 | /** | ||
32 | * efi_memmap_alloc - Allocate memory for the EFI memory map | ||
33 | * @num_entries: Number of entries in the allocated map. | ||
34 | * | ||
35 | * Depending on whether mm_init() has already been invoked or not, | ||
36 | * either memblock or "normal" page allocation is used. | ||
37 | * | ||
38 | * Returns the physical address of the allocated memory map on | ||
39 | * success, zero on failure. | ||
40 | */ | ||
41 | phys_addr_t __init efi_memmap_alloc(unsigned int num_entries) | ||
42 | { | ||
43 | unsigned long size = num_entries * efi.memmap.desc_size; | ||
44 | |||
45 | if (slab_is_available()) | ||
46 | return __efi_memmap_alloc_late(size); | ||
47 | |||
48 | return __efi_memmap_alloc_early(size); | ||
49 | } | ||
12 | 50 | ||
13 | /** | 51 | /** |
14 | * __efi_memmap_init - Common code for mapping the EFI memory map | 52 | * __efi_memmap_init - Common code for mapping the EFI memory map |