Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86, efi: Do not reserve boot services regions within reserved areas

Commit 916f676f8dc started reserving boot service code since some systems
require you to keep that code around until SetVirtualAddressMap is called.

However, in some cases those areas will overlap with reserved regions.
The proper medium-term fix is to fix the bootloader to prevent the
conflicts from occurring by moving the kernel to a better position,
but the kernel should check for this possibility, and only reserve regions
which can be reserved.

Signed-off-by: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Link: http://lkml.kernel.org/r/4DF7A005.1050407@gmail.com
Acked-by: Matthew Garrett <mjg@redhat.com>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>

authored by

Maarten Lankhorst and committed by
Ingo Molnar
7d68dc3f c11760c6

+28 -7
+1 -1
arch/x86/include/asm/memblock.h
··· 4 4 #define ARCH_DISCARD_MEMBLOCK 5 5 6 6 u64 memblock_x86_find_in_range_size(u64 start, u64 *sizep, u64 align); 7 - void memblock_x86_to_bootmem(u64 start, u64 end); 8 7 9 8 void memblock_x86_reserve_range(u64 start, u64 end, char *name); 10 9 void memblock_x86_free_range(u64 start, u64 end); ··· 18 19 u64 memblock_x86_find_in_range_node(int nid, u64 start, u64 end, u64 size, u64 align); 19 20 u64 memblock_x86_free_memory_in_range(u64 addr, u64 limit); 20 21 u64 memblock_x86_memory_in_range(u64 addr, u64 limit); 22 + bool memblock_x86_check_reserved_size(u64 *addrp, u64 *sizep, u64 align); 21 23 22 24 #endif
+2 -2
arch/x86/mm/memblock.c
··· 8 8 #include <linux/range.h> 9 9 10 10 /* Check for already reserved areas */ 11 - static bool __init check_with_memblock_reserved_size(u64 *addrp, u64 *sizep, u64 align) 11 + bool __init memblock_x86_check_reserved_size(u64 *addrp, u64 *sizep, u64 align) 12 12 { 13 13 struct memblock_region *r; 14 14 u64 addr = *addrp, last; ··· 59 59 if (addr >= ei_last) 60 60 continue; 61 61 *sizep = ei_last - addr; 62 - while (check_with_memblock_reserved_size(&addr, sizep, align)) 62 + while (memblock_x86_check_reserved_size(&addr, sizep, align)) 63 63 ; 64 64 65 65 if (*sizep)
+25 -4
arch/x86/platform/efi/efi.c
··· 310 310 311 311 for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { 312 312 efi_memory_desc_t *md = p; 313 - unsigned long long start = md->phys_addr; 314 - unsigned long long size = md->num_pages << EFI_PAGE_SHIFT; 313 + u64 start = md->phys_addr; 314 + u64 size = md->num_pages << EFI_PAGE_SHIFT; 315 315 316 316 if (md->type != EFI_BOOT_SERVICES_CODE && 317 317 md->type != EFI_BOOT_SERVICES_DATA) 318 318 continue; 319 - 320 - memblock_x86_reserve_range(start, start + size, "EFI Boot"); 319 + /* Only reserve where possible: 320 + * - Not within any already allocated areas 321 + * - Not over any memory area (really needed, if above?) 322 + * - Not within any part of the kernel 323 + * - Not the bios reserved area 324 + */ 325 + if ((start+size >= virt_to_phys(_text) 326 + && start <= virt_to_phys(_end)) || 327 + !e820_all_mapped(start, start+size, E820_RAM) || 328 + memblock_x86_check_reserved_size(&start, &size, 329 + 1<<EFI_PAGE_SHIFT)) { 330 + /* Could not reserve, skip it */ 331 + md->num_pages = 0; 332 + memblock_dbg(PFX "Could not reserve boot range " 333 + "[0x%010llx-0x%010llx]\n", 334 + start, start+size-1); 335 + } else 336 + memblock_x86_reserve_range(start, start+size, 337 + "EFI Boot"); 321 338 } 322 339 } 323 340 ··· 349 332 350 333 if (md->type != EFI_BOOT_SERVICES_CODE && 351 334 md->type != EFI_BOOT_SERVICES_DATA) 335 + continue; 336 + 337 + /* Could not reserve boot area */ 338 + if (!size) 352 339 continue; 353 340 354 341 free_bootmem_late(start, size);