Commit ac71d12c authored by Andi Kleen's avatar Andi Kleen Committed by Linus Torvalds

[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: default avatarAndi Kleen <ak@suse.de>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 8b1ffe95
...@@ -76,6 +76,12 @@ static inline int bad_addr(unsigned long *addrp, unsigned long size) ...@@ -76,6 +76,12 @@ static inline int bad_addr(unsigned long *addrp, unsigned long size)
*addrp = __pa_symbol(&_end); *addrp = __pa_symbol(&_end);
return 1; return 1;
} }
if (last >= ebda_addr && addr < ebda_addr + ebda_size) {
*addrp = ebda_addr + ebda_size;
return 1;
}
/* XXX ramdisk image here? */ /* XXX ramdisk image here? */
return 0; return 0;
} }
......
...@@ -571,17 +571,28 @@ static inline void copy_edd(void) ...@@ -571,17 +571,28 @@ static inline void copy_edd(void)
#endif #endif
#define EBDA_ADDR_POINTER 0x40E #define EBDA_ADDR_POINTER 0x40E
static void __init reserve_ebda_region(void)
unsigned __initdata ebda_addr;
unsigned __initdata ebda_size;
static void discover_ebda(void)
{ {
unsigned int addr; /*
/**
* there is a real-mode segmented pointer pointing to the * there is a real-mode segmented pointer pointing to the
* 4K EBDA area at 0x40E * 4K EBDA area at 0x40E
*/ */
addr = *(unsigned short *)phys_to_virt(EBDA_ADDR_POINTER); ebda_addr = *(unsigned short *)EBDA_ADDR_POINTER;
addr <<= 4; ebda_addr <<= 4;
if (addr)
reserve_bootmem_generic(addr, PAGE_SIZE); ebda_size = *(unsigned short *)(unsigned long)ebda_addr;
/* Round EBDA up to pages */
if (ebda_size == 0)
ebda_size = 1;
ebda_size <<= 10;
ebda_size = round_up(ebda_size + (ebda_addr & ~PAGE_MASK), PAGE_SIZE);
if (ebda_size > 64*1024)
ebda_size = 64*1024;
} }
void __init setup_arch(char **cmdline_p) void __init setup_arch(char **cmdline_p)
...@@ -627,6 +638,8 @@ void __init setup_arch(char **cmdline_p) ...@@ -627,6 +638,8 @@ void __init setup_arch(char **cmdline_p)
check_efer(); check_efer();
discover_ebda();
init_memory_mapping(0, (end_pfn_map << PAGE_SHIFT)); init_memory_mapping(0, (end_pfn_map << PAGE_SHIFT));
dmi_scan_machine(); dmi_scan_machine();
...@@ -669,7 +682,8 @@ void __init setup_arch(char **cmdline_p) ...@@ -669,7 +682,8 @@ void __init setup_arch(char **cmdline_p)
reserve_bootmem_generic(0, PAGE_SIZE); reserve_bootmem_generic(0, PAGE_SIZE);
/* reserve ebda region */ /* reserve ebda region */
reserve_ebda_region(); if (ebda_addr)
reserve_bootmem_generic(ebda_addr, ebda_size);
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
/* /*
......
...@@ -59,6 +59,8 @@ extern void __init parse_memopt(char *p, char **end); ...@@ -59,6 +59,8 @@ extern void __init parse_memopt(char *p, char **end);
extern void __init parse_memmapopt(char *p, char **end); extern void __init parse_memmapopt(char *p, char **end);
extern struct e820map e820; extern struct e820map e820;
extern unsigned ebda_addr, ebda_size;
#endif/*!__ASSEMBLY__*/ #endif/*!__ASSEMBLY__*/
#endif/*__E820_HEADER*/ #endif/*__E820_HEADER*/
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment