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

[PATCH] x86_64: Avoid EBDA area in early boot allocator

Based on analysis&patch from Robert Hentosch

Observed on a Dell PE6850 with 16GB

The problem occurs very early on, when the kernel allocates space for the
temporary memory map called bootmap. The bootmap overlaps the EBDA region.
EBDA region is not historically reserved in the e820 mapping. When the
bootmap is freed it marks the EBDA region as usable.

If you notice in setup.c there is already code to work around the EBDA
in reserve_ebda_region(), this check however occurs after the bootmap
is allocated and doesn't prevent the bootmap from using this range.

AK: I redid the original patch. Thanks also to Jan Beulich for
spotting some mistakes.

Cc: Robert_Hentosch@dell.com
Cc: jbeulich@novell.com
Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by

Andi Kleen and committed by
Linus Torvalds
ac71d12c 8b1ffe95

+30 -8
+6
arch/x86_64/kernel/e820.c
··· 76 76 *addrp = __pa_symbol(&_end); 77 77 return 1; 78 78 } 79 + 80 + if (last >= ebda_addr && addr < ebda_addr + ebda_size) { 81 + *addrp = ebda_addr + ebda_size; 82 + return 1; 83 + } 84 + 79 85 /* XXX ramdisk image here? */ 80 86 return 0; 81 87 }
+22 -8
arch/x86_64/kernel/setup.c
··· 571 571 #endif 572 572 573 573 #define EBDA_ADDR_POINTER 0x40E 574 - static void __init reserve_ebda_region(void) 574 + 575 + unsigned __initdata ebda_addr; 576 + unsigned __initdata ebda_size; 577 + 578 + static void discover_ebda(void) 575 579 { 576 - unsigned int addr; 577 - /** 580 + /* 578 581 * there is a real-mode segmented pointer pointing to the 579 582 * 4K EBDA area at 0x40E 580 583 */ 581 - addr = *(unsigned short *)phys_to_virt(EBDA_ADDR_POINTER); 582 - addr <<= 4; 583 - if (addr) 584 - reserve_bootmem_generic(addr, PAGE_SIZE); 584 + ebda_addr = *(unsigned short *)EBDA_ADDR_POINTER; 585 + ebda_addr <<= 4; 586 + 587 + ebda_size = *(unsigned short *)(unsigned long)ebda_addr; 588 + 589 + /* Round EBDA up to pages */ 590 + if (ebda_size == 0) 591 + ebda_size = 1; 592 + ebda_size <<= 10; 593 + ebda_size = round_up(ebda_size + (ebda_addr & ~PAGE_MASK), PAGE_SIZE); 594 + if (ebda_size > 64*1024) 595 + ebda_size = 64*1024; 585 596 } 586 597 587 598 void __init setup_arch(char **cmdline_p) ··· 638 627 639 628 check_efer(); 640 629 630 + discover_ebda(); 631 + 641 632 init_memory_mapping(0, (end_pfn_map << PAGE_SHIFT)); 642 633 643 634 dmi_scan_machine(); ··· 682 669 reserve_bootmem_generic(0, PAGE_SIZE); 683 670 684 671 /* reserve ebda region */ 685 - reserve_ebda_region(); 672 + if (ebda_addr) 673 + reserve_bootmem_generic(ebda_addr, ebda_size); 686 674 687 675 #ifdef CONFIG_SMP 688 676 /*
+2
include/asm-x86_64/e820.h
··· 59 59 extern void __init parse_memmapopt(char *p, char **end); 60 60 61 61 extern struct e820map e820; 62 + 63 + extern unsigned ebda_addr, ebda_size; 62 64 #endif/*!__ASSEMBLY__*/ 63 65 64 66 #endif/*__E820_HEADER*/