diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-10-03 10:46:41 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-10-03 10:46:41 -0400 |
commit | a758379b031f50b9def094aad071ef547a5cb335 (patch) | |
tree | 3a29fedfd77b578a568916ce4c3b6cf2750b8a77 | |
parent | 14f97d9713283adfadf2193e287e21d079f72ee7 (diff) | |
parent | 0ce3cc008ec04258b6a6314b09f1a6012810881a (diff) |
Merge branch 'core-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull EFI fixes from Ingo Molnar:
"Two EFI fixes: one for x86, one for ARM, fixing a boot crash bug that
can trigger under newer EFI firmware"
* 'core-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
arm64/efi: Fix boot crash by not padding between EFI_MEMORY_RUNTIME regions
x86/efi: Fix boot crash by mapping EFI memmap entries bottom-up at runtime, instead of top-down
-rw-r--r-- | arch/arm64/kernel/efi.c | 3 | ||||
-rw-r--r-- | arch/x86/platform/efi/efi.c | 67 | ||||
-rw-r--r-- | drivers/firmware/efi/libstub/arm-stub.c | 88 |
3 files changed, 141 insertions, 17 deletions
diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c index e8ca6eaedd02..13671a9cf016 100644 --- a/arch/arm64/kernel/efi.c +++ b/arch/arm64/kernel/efi.c | |||
@@ -258,7 +258,8 @@ static bool __init efi_virtmap_init(void) | |||
258 | */ | 258 | */ |
259 | if (!is_normal_ram(md)) | 259 | if (!is_normal_ram(md)) |
260 | prot = __pgprot(PROT_DEVICE_nGnRE); | 260 | prot = __pgprot(PROT_DEVICE_nGnRE); |
261 | else if (md->type == EFI_RUNTIME_SERVICES_CODE) | 261 | else if (md->type == EFI_RUNTIME_SERVICES_CODE || |
262 | !PAGE_ALIGNED(md->phys_addr)) | ||
262 | prot = PAGE_KERNEL_EXEC; | 263 | prot = PAGE_KERNEL_EXEC; |
263 | else | 264 | else |
264 | prot = PAGE_KERNEL; | 265 | prot = PAGE_KERNEL; |
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 1db84c0758b7..6a28ded74211 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c | |||
@@ -705,6 +705,70 @@ out: | |||
705 | } | 705 | } |
706 | 706 | ||
707 | /* | 707 | /* |
708 | * Iterate the EFI memory map in reverse order because the regions | ||
709 | * will be mapped top-down. The end result is the same as if we had | ||
710 | * mapped things forward, but doesn't require us to change the | ||
711 | * existing implementation of efi_map_region(). | ||
712 | */ | ||
713 | static inline void *efi_map_next_entry_reverse(void *entry) | ||
714 | { | ||
715 | /* Initial call */ | ||
716 | if (!entry) | ||
717 | return memmap.map_end - memmap.desc_size; | ||
718 | |||
719 | entry -= memmap.desc_size; | ||
720 | if (entry < memmap.map) | ||
721 | return NULL; | ||
722 | |||
723 | return entry; | ||
724 | } | ||
725 | |||
726 | /* | ||
727 | * efi_map_next_entry - Return the next EFI memory map descriptor | ||
728 | * @entry: Previous EFI memory map descriptor | ||
729 | * | ||
730 | * This is a helper function to iterate over the EFI memory map, which | ||
731 | * we do in different orders depending on the current configuration. | ||
732 | * | ||
733 | * To begin traversing the memory map @entry must be %NULL. | ||
734 | * | ||
735 | * Returns %NULL when we reach the end of the memory map. | ||
736 | */ | ||
737 | static void *efi_map_next_entry(void *entry) | ||
738 | { | ||
739 | if (!efi_enabled(EFI_OLD_MEMMAP) && efi_enabled(EFI_64BIT)) { | ||
740 | /* | ||
741 | * Starting in UEFI v2.5 the EFI_PROPERTIES_TABLE | ||
742 | * config table feature requires us to map all entries | ||
743 | * in the same order as they appear in the EFI memory | ||
744 | * map. That is to say, entry N must have a lower | ||
745 | * virtual address than entry N+1. This is because the | ||
746 | * firmware toolchain leaves relative references in | ||
747 | * the code/data sections, which are split and become | ||
748 | * separate EFI memory regions. Mapping things | ||
749 | * out-of-order leads to the firmware accessing | ||
750 | * unmapped addresses. | ||
751 | * | ||
752 | * Since we need to map things this way whether or not | ||
753 | * the kernel actually makes use of | ||
754 | * EFI_PROPERTIES_TABLE, let's just switch to this | ||
755 | * scheme by default for 64-bit. | ||
756 | */ | ||
757 | return efi_map_next_entry_reverse(entry); | ||
758 | } | ||
759 | |||
760 | /* Initial call */ | ||
761 | if (!entry) | ||
762 | return memmap.map; | ||
763 | |||
764 | entry += memmap.desc_size; | ||
765 | if (entry >= memmap.map_end) | ||
766 | return NULL; | ||
767 | |||
768 | return entry; | ||
769 | } | ||
770 | |||
771 | /* | ||
708 | * Map the efi memory ranges of the runtime services and update new_mmap with | 772 | * Map the efi memory ranges of the runtime services and update new_mmap with |
709 | * virtual addresses. | 773 | * virtual addresses. |
710 | */ | 774 | */ |
@@ -714,7 +778,8 @@ static void * __init efi_map_regions(int *count, int *pg_shift) | |||
714 | unsigned long left = 0; | 778 | unsigned long left = 0; |
715 | efi_memory_desc_t *md; | 779 | efi_memory_desc_t *md; |
716 | 780 | ||
717 | for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { | 781 | p = NULL; |
782 | while ((p = efi_map_next_entry(p))) { | ||
718 | md = p; | 783 | md = p; |
719 | if (!(md->attribute & EFI_MEMORY_RUNTIME)) { | 784 | if (!(md->attribute & EFI_MEMORY_RUNTIME)) { |
720 | #ifdef CONFIG_X86_64 | 785 | #ifdef CONFIG_X86_64 |
diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index e29560e6b40b..950c87f5d279 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c | |||
@@ -13,6 +13,7 @@ | |||
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/efi.h> | 15 | #include <linux/efi.h> |
16 | #include <linux/sort.h> | ||
16 | #include <asm/efi.h> | 17 | #include <asm/efi.h> |
17 | 18 | ||
18 | #include "efistub.h" | 19 | #include "efistub.h" |
@@ -305,6 +306,44 @@ fail: | |||
305 | */ | 306 | */ |
306 | #define EFI_RT_VIRTUAL_BASE 0x40000000 | 307 | #define EFI_RT_VIRTUAL_BASE 0x40000000 |
307 | 308 | ||
309 | static int cmp_mem_desc(const void *l, const void *r) | ||
310 | { | ||
311 | const efi_memory_desc_t *left = l, *right = r; | ||
312 | |||
313 | return (left->phys_addr > right->phys_addr) ? 1 : -1; | ||
314 | } | ||
315 | |||
316 | /* | ||
317 | * Returns whether region @left ends exactly where region @right starts, | ||
318 | * or false if either argument is NULL. | ||
319 | */ | ||
320 | static bool regions_are_adjacent(efi_memory_desc_t *left, | ||
321 | efi_memory_desc_t *right) | ||
322 | { | ||
323 | u64 left_end; | ||
324 | |||
325 | if (left == NULL || right == NULL) | ||
326 | return false; | ||
327 | |||
328 | left_end = left->phys_addr + left->num_pages * EFI_PAGE_SIZE; | ||
329 | |||
330 | return left_end == right->phys_addr; | ||
331 | } | ||
332 | |||
333 | /* | ||
334 | * Returns whether region @left and region @right have compatible memory type | ||
335 | * mapping attributes, and are both EFI_MEMORY_RUNTIME regions. | ||
336 | */ | ||
337 | static bool regions_have_compatible_memory_type_attrs(efi_memory_desc_t *left, | ||
338 | efi_memory_desc_t *right) | ||
339 | { | ||
340 | static const u64 mem_type_mask = EFI_MEMORY_WB | EFI_MEMORY_WT | | ||
341 | EFI_MEMORY_WC | EFI_MEMORY_UC | | ||
342 | EFI_MEMORY_RUNTIME; | ||
343 | |||
344 | return ((left->attribute ^ right->attribute) & mem_type_mask) == 0; | ||
345 | } | ||
346 | |||
308 | /* | 347 | /* |
309 | * efi_get_virtmap() - create a virtual mapping for the EFI memory map | 348 | * efi_get_virtmap() - create a virtual mapping for the EFI memory map |
310 | * | 349 | * |
@@ -317,33 +356,52 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, | |||
317 | int *count) | 356 | int *count) |
318 | { | 357 | { |
319 | u64 efi_virt_base = EFI_RT_VIRTUAL_BASE; | 358 | u64 efi_virt_base = EFI_RT_VIRTUAL_BASE; |
320 | efi_memory_desc_t *out = runtime_map; | 359 | efi_memory_desc_t *in, *prev = NULL, *out = runtime_map; |
321 | int l; | 360 | int l; |
322 | 361 | ||
323 | for (l = 0; l < map_size; l += desc_size) { | 362 | /* |
324 | efi_memory_desc_t *in = (void *)memory_map + l; | 363 | * To work around potential issues with the Properties Table feature |
364 | * introduced in UEFI 2.5, which may split PE/COFF executable images | ||
365 | * in memory into several RuntimeServicesCode and RuntimeServicesData | ||
366 | * regions, we need to preserve the relative offsets between adjacent | ||
367 | * EFI_MEMORY_RUNTIME regions with the same memory type attributes. | ||
368 | * The easiest way to find adjacent regions is to sort the memory map | ||
369 | * before traversing it. | ||
370 | */ | ||
371 | sort(memory_map, map_size / desc_size, desc_size, cmp_mem_desc, NULL); | ||
372 | |||
373 | for (l = 0; l < map_size; l += desc_size, prev = in) { | ||
325 | u64 paddr, size; | 374 | u64 paddr, size; |
326 | 375 | ||
376 | in = (void *)memory_map + l; | ||
327 | if (!(in->attribute & EFI_MEMORY_RUNTIME)) | 377 | if (!(in->attribute & EFI_MEMORY_RUNTIME)) |
328 | continue; | 378 | continue; |
329 | 379 | ||
380 | paddr = in->phys_addr; | ||
381 | size = in->num_pages * EFI_PAGE_SIZE; | ||
382 | |||
330 | /* | 383 | /* |
331 | * Make the mapping compatible with 64k pages: this allows | 384 | * Make the mapping compatible with 64k pages: this allows |
332 | * a 4k page size kernel to kexec a 64k page size kernel and | 385 | * a 4k page size kernel to kexec a 64k page size kernel and |
333 | * vice versa. | 386 | * vice versa. |
334 | */ | 387 | */ |
335 | paddr = round_down(in->phys_addr, SZ_64K); | 388 | if (!regions_are_adjacent(prev, in) || |
336 | size = round_up(in->num_pages * EFI_PAGE_SIZE + | 389 | !regions_have_compatible_memory_type_attrs(prev, in)) { |
337 | in->phys_addr - paddr, SZ_64K); | 390 | |
338 | 391 | paddr = round_down(in->phys_addr, SZ_64K); | |
339 | /* | 392 | size += in->phys_addr - paddr; |
340 | * Avoid wasting memory on PTEs by choosing a virtual base that | 393 | |
341 | * is compatible with section mappings if this region has the | 394 | /* |
342 | * appropriate size and physical alignment. (Sections are 2 MB | 395 | * Avoid wasting memory on PTEs by choosing a virtual |
343 | * on 4k granule kernels) | 396 | * base that is compatible with section mappings if this |
344 | */ | 397 | * region has the appropriate size and physical |
345 | if (IS_ALIGNED(in->phys_addr, SZ_2M) && size >= SZ_2M) | 398 | * alignment. (Sections are 2 MB on 4k granule kernels) |
346 | efi_virt_base = round_up(efi_virt_base, SZ_2M); | 399 | */ |
400 | if (IS_ALIGNED(in->phys_addr, SZ_2M) && size >= SZ_2M) | ||
401 | efi_virt_base = round_up(efi_virt_base, SZ_2M); | ||
402 | else | ||
403 | efi_virt_base = round_up(efi_virt_base, SZ_64K); | ||
404 | } | ||
347 | 405 | ||
348 | in->virt_addr = efi_virt_base + in->phys_addr - paddr; | 406 | in->virt_addr = efi_virt_base + in->phys_addr - paddr; |
349 | efi_virt_base += size; | 407 | efi_virt_base += size; |