zero-size E820 memory (kernel start-up failure)
PATCH 1/1 zero-size E820 memory (kernel start-up failure)
Hi everybody.
This patch fixes the
Ok, booting the kernel. [hangs]
problem with BIOSes that report E820 memory maps with zero-size entries. Among the mainboards affected are
EPOX 4G4A+ (presumably)
EPOX 4BEA[-R]
Chaintech 9EJL .
In the past (kernels up to 2.4.20), these mainboards could only boot Linux with explicit 'mem=' boot parameters.
Up to 2.4.20, a zero-size E820 memory region (eg 0xa0000 - 0xa0000) reported by the BIOS causes 'sanitize_e820_map' to take the end of this region as the start of another region, producing two overlapping regions extending to the end of addressable memory. If the zero-size region is of a higher type than 'usable RAM' (eg 'reserved'), this causes all memory above the zero-size region to be marked unusable (leaving 640k as of the above example -- the kernel fails to start at all).
The patch adds code to remove empty entries before sorting regions. It was generated for a 2.4.20 kernel.
Best regards,
Alex
[ Please send answers to my Reply-To address.
I'm not subscribed to the kernel mailing list. ]
arch/i386/kernel/setup.c +15 -8
@@ -71,6 +71,9 @@* CacheSize bug workaround updates for AMD, Intel & VIA Cyrix. * Dave Jones <[email protected]>, September, October 2001. * +* Provisions for empty E820 memory regions (reported by certain BIOSes). +* Alex Achenbach <[email protected]>, December 2002. +* */ /*
@@ -501,7 +504,7 @@int chgidx, still_changing;int overlap_entries;int new_bios_entry; -int old_nr, new_nr; +int old_nr, new_nr, chg_nr;int i;/*
@@ -555,20 +558,24 @@for (i=0 ; i <2 *old_nr; i++) change_point[i] = &change_point_list[i]; -/* record all known change-points (starting and ending addresses) */ +/* record all known change-points (starting and ending addresses), +omitting those that are for empty memory regions */ chgidx =0 ;for (i=0 ; i < old_nr; i++) { - change_point[chgidx]->addr = biosmap[i].addr; - change_point[chgidx++]->pbios = &biosmap[i]; - change_point[chgidx]->addr = biosmap[i].addr + biosmap[i].size; - change_point[chgidx++]->pbios = &biosmap[i]; +if (biosmap[i].size !=0 ) { + change_point[chgidx]->addr = biosmap[i].addr; + change_point[chgidx++]->pbios = &biosmap[i]; + change_point[chgidx]->addr = biosmap[i].addr + biosmap[i].size; + change_point[chgidx++]->pbios = &biosmap[i]; + } } + chg_nr = chgidx;/* true number of change-points */ /* sort change-point list by memory addresses (low -> high) */ still_changing =1 ;while (still_changing) { still_changing =0 ; -for (i=1 ; i <2 *old_nr; i++) { +for (i=1 ; i < chg_nr; i++) {/* if <current_addr> > <last_addr>, swap */ /* or, if current=<start_addr> & last=<end_addr>, swap */ if ((change_point[i]->addr < change_point[i-1]->addr) ||
@@ -591,7 +598,7 @@ last_type =0 ;/* start with undefined memory type */ last_addr =0 ;/* start with 0 as last starting address */ /* loop through change-points, determining affect on the new bios map */ -for (chgidx=0 ; chgidx <2 *old_nr; chgidx++) +for (chgidx=0 ; chgidx < chg_nr; chgidx++) {/* keep track of all overlapping bios entries */ if (change_point[chgidx]->addr == change_point[chgidx]->pbios->addr)
Quoted 10 lines · Alexander Achenbach
Up to 2.4.20, a zero-size E820 memory region (eg
0xa0000-0xa0000) reported by the BIOS causes 'sanitize_e820_map' to take the end of this region as the start of another region, producing two overlapping regions extending to the end of addressable memory. If the zero-size region is of a higher type than 'usable RAM' (eg 'reserved'), this causes all memory above the zero-size region to be marked unusable (leaving 640k as of the above example -- the kernel fails to start at all).The patch adds code to remove empty entries before sorting regions. It was generated for a 2.4.20 kernel.
Looks good to me. Care to do a similar patch for 2.5 ?
Dave
Signature · 2 lines
| Dave Jones. http://www.codemonkey.org.uk | SuSE Labs
Hi again.
Quoted 5 lines · Dave Jones
Quoted 3 lines
The patch adds code to remove empty entries before sorting regions. It was generated for a 2.4.20 kernel.
Looks good to me. Care to do a similar patch for 2.5 ?
This is it, to be applied against 2.5.52.
I've tested the kernel on an EPOX 4BEA-R and it boots as it should.
Cheers,
Alex
arch/i386/kernel/setup.c +15 -8
@@ -14,6 +14,9 @@* Moved CPU detection code to cpu/${cpu}.c * Patrick Mochel <[email protected]>, March 2002 * +* Provisions for empty E820 memory regions (reported by certain BIOSes). +* Alex Achenbach <[email protected]>, December 2002. +* */ /*
@@ -275,7 +278,7 @@int chgidx, still_changing;int overlap_entries;int new_bios_entry; -int old_nr, new_nr; +int old_nr, new_nr, chg_nr;int i;/*
@@ -329,20 +332,24 @@for (i=0 ; i <2 *old_nr; i++) change_point[i] = &change_point_list[i]; -/* record all known change-points (starting and ending addresses) */ +/* record all known change-points (starting and ending addresses), +omitting those that are for empty memory regions */ chgidx =0 ;for (i=0 ; i < old_nr; i++) { - change_point[chgidx]->addr = biosmap[i].addr; - change_point[chgidx++]->pbios = &biosmap[i]; - change_point[chgidx]->addr = biosmap[i].addr + biosmap[i].size; - change_point[chgidx++]->pbios = &biosmap[i]; +if (biosmap[i].size !=0 ) { + change_point[chgidx]->addr = biosmap[i].addr; + change_point[chgidx++]->pbios = &biosmap[i]; + change_point[chgidx]->addr = biosmap[i].addr + biosmap[i].size; + change_point[chgidx++]->pbios = &biosmap[i]; + } } + chg_nr = chgidx;/* true number of change-points */ /* sort change-point list by memory addresses (low -> high) */ still_changing =1 ;while (still_changing) { still_changing =0 ; -for (i=1 ; i <2 *old_nr; i++) { +for (i=1 ; i < chg_nr; i++) {/* if <current_addr> > <last_addr>, swap */ /* or, if current=<start_addr> & last=<end_addr>, swap */ if ((change_point[i]->addr < change_point[i-1]->addr) ||
@@ -365,7 +372,7 @@ last_type =0 ;/* start with undefined memory type */ last_addr =0 ;/* start with 0 as last starting address */ /* loop through change-points, determining affect on the new bios map */ -for (chgidx=0 ; chgidx <2 *old_nr; chgidx++) +for (chgidx=0 ; chgidx < chg_nr; chgidx++) {/* keep track of all overlapping bios entries */ if (change_point[chgidx]->addr == change_point[chgidx]->pbios->addr)