linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* 2.4.2-ac6 hangs on boot w/AMD Elan SC520 dev board
@ 2001-02-28  3:12 Brian Moyle
  2001-02-28  9:49 ` Alan Cox
  2001-02-28 11:45 ` Alan Cox
  0 siblings, 2 replies; 7+ messages in thread
From: Brian Moyle @ 2001-02-28  3:12 UTC (permalink / raw)
  To: linux-kernel; +Cc: bmoyle

Kernel 2.4.2-ac6 hangs while booting an AMD Elan SC520 development board.

Long-story-short
================
If I define "STANDARD_MEMORY_BIOS_CALL" in setup.S and misc.c, it boots 
fine.  Here are the results:

memory map that hangs (added debugging to setup.S to determine E820 map):
   hand-copied physical RAM map:
    bios-e820: 000000000009f400 @ 0000000000000000 (usable)
    bios-e820: 0000000000000c00 @ 000000000009f400 (reserved)
    bios-e820: 0000000003f00000 @ 0000000000100000 (usable)
    bios-e820: 0000000003f00000 @ 0000000000100000 (usable)
    bios-e820: 0000000000100000 @ 00000000fff00000 (reserved)

memory map that works (#define STANDARD_MEMORY_BIOS_CALL in setup.S & misc.c):
   BIOS-provided physical RAM map:
    BIOS-88: 000000000009f000 @ 0000000000000000 (usable)
    BIOS-88: 0000000003f00000 @ 0000000000100000 (usable)


Is this the result of a BIOS problem?


Long-story-long
===============
(when STANDARD_MEMORY_BIOS_CALL is not defined)

Booting from a floppy, I see the following message:

   "Uncompressing Linux... Ok, booting the kernel."

That's the last print statement I see.

Adding debug, it appears to go through the following:

   init/main.c:
      start_kernel {
         ...
         setup_arch(&command_line);
         ...
      }

   arch/i386/kernel/setup.c:
      setup_arch {
         ...
         for (i = 0; i < e820.nr_map; i++) {
            ...
            free_bootmem(PFN_PHYS(curr_pfn), PFN_PHYS(size)); ...
            ...
         }
         ...
      }

   mm/bootmem.c:
      free_bootmem{
         return(free_bootmem_core(contig_page_data.bdata, addr, size));
      }

   mm/bootmem.c:
      free_bootmem_core {
         ...
         for (i = sidx; i < eidx; i++) {
            ...
            if (!test_and_clear_bit(i, bdata->node_bootmem_map)) {
               BUG();
            }
         }
      }

   include/asm-i386/page.h:
      BUG {
         printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__);
         ...
      }

   (at this point, it appears to be in an infinite printk loop <?>)

I didn't spend much time looking into the printk loop, but it seems to 
end up there, even if CONFIG_DEBUG_BUGVERBOSE is not defined, as if the 
".byte 0x0f,0x0b" is causing the loop to begin.

Any ideas/suggestions/comments?

Brian
bmoyle@mvista.com


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: 2.4.2-ac6 hangs on boot w/AMD Elan SC520 dev board
  2001-02-28  3:12 2.4.2-ac6 hangs on boot w/AMD Elan SC520 dev board Brian Moyle
@ 2001-02-28  9:49 ` Alan Cox
  2001-02-28 11:45 ` Alan Cox
  1 sibling, 0 replies; 7+ messages in thread
From: Alan Cox @ 2001-02-28  9:49 UTC (permalink / raw)
  To: Brian Moyle; +Cc: linux-kernel, bmoyle

> Kernel 2.4.2-ac6 hangs while booting an AMD Elan SC520 development board.
> 
> memory map that hangs (added debugging to setup.S to determine E820 map):
>    hand-copied physical RAM map:
>     bios-e820: 000000000009f400 @ 0000000000000000 (usable)
>     bios-e820: 0000000000000c00 @ 000000000009f400 (reserved)
>     bios-e820: 0000000003f00000 @ 0000000000100000 (usable)
>     bios-e820: 0000000003f00000 @ 0000000000100000 (usable)

The same block listed twice. Thats probably the trigger. I'll take a look
see if there is an obvious cause



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: 2.4.2-ac6 hangs on boot w/AMD Elan SC520 dev board
  2001-02-28  3:12 2.4.2-ac6 hangs on boot w/AMD Elan SC520 dev board Brian Moyle
  2001-02-28  9:49 ` Alan Cox
@ 2001-02-28 11:45 ` Alan Cox
  2001-03-01  8:56   ` Brian Moyle
  2001-03-01 12:12   ` Pavel Machek
  1 sibling, 2 replies; 7+ messages in thread
From: Alan Cox @ 2001-02-28 11:45 UTC (permalink / raw)
  To: Brian Moyle; +Cc: linux-kernel, bmoyle

> memory map that hangs (added debugging to setup.S to determine E820 map):
>    hand-copied physical RAM map:
>     bios-e820: 000000000009f400 @ 0000000000000000 (usable)
>     bios-e820: 0000000000000c00 @ 000000000009f400 (reserved)
>     bios-e820: 0000000003f00000 @ 0000000000100000 (usable)
>     bios-e820: 0000000003f00000 @ 0000000000100000 (usable)
>     bios-e820: 0000000000100000 @ 00000000fff00000 (reserved)
>    (at this point, it appears to be in an infinite printk loop <?>)
> 
> I didn't spend much time looking into the printk loop, but it seems to 
> end up there, even if CONFIG_DEBUG_BUGVERBOSE is not defined, as if the 
> ".byte 0x0f,0x0b" is causing the loop to begin.
> 
> Any ideas/suggestions/comments?

Having been over the code the problem is indeed the bios reporting overlapping
/duplicated ranges. That will cause a crash in mm/bootmem when we try and free
the range twice.

I suspect you need to add some code to take the E820 map and remove any
overlaps from it, favouring ROM over RAM if the types disagree (for safety),
and filter them before you register them with the bootmem in 
arch/i386/kernel/setup.c




^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: 2.4.2-ac6 hangs on boot w/AMD Elan SC520 dev board
  2001-02-28 11:45 ` Alan Cox
@ 2001-03-01  8:56   ` Brian Moyle
  2001-03-02  1:32     ` Brian Moyle
  2001-03-01 12:12   ` Pavel Machek
  1 sibling, 1 reply; 7+ messages in thread
From: Brian Moyle @ 2001-03-01  8:56 UTC (permalink / raw)
  To: Alan Cox; +Cc: linux-kernel, bmoyle

[-- Attachment #1: Type: text/plain, Size: 610 bytes --]

I believe the following patch fixes the problem (it worked on the few machines I tested).

Is this along the lines of what you were thinking?

Brian
bmoyle@mvista.com

Alan Cox wrote:
> Having been over the code the problem is indeed the bios reporting overlapping
> /duplicated ranges. That will cause a crash in mm/bootmem when we try and free
> the range twice.
> 
> I suspect you need to add some code to take the E820 map and remove any
> overlaps from it, favouring ROM over RAM if the types disagree (for safety),
> and filter them before you register them with the bootmem in
> arch/i386/kernel/setup.c

[-- Attachment #2: e820bios.patch --]
[-- Type: text/plain, Size: 5107 bytes --]

--- linux-2.4.2-ac6/arch/i386/kernel/setup.c	Thu Mar  1 00:24:26 2001
+++ linux/arch/i386/kernel/setup.c	Thu Mar  1 00:29:34 2001
@@ -58,6 +58,9 @@
  *  Massive cleanup of CPU detection and bug handling;
  *  Transmeta CPU detection,
  *  H. Peter Anvin <hpa@zytor.com>, November 2000
+ *
+ *  Added E820 sanitization routine (removes overlapping memory regions);
+ *  Brian Moyle <bmoyle@mvista.com>, February 2001
  */
 
 /*
@@ -438,6 +441,126 @@
 }
 
 /*
+ * Sanitize the BIOS e820 map.
+ *
+ * Some e820 responses include overlapping entries.  The following 
+ * replaces the original e820 map with a new one, removing overlaps.
+ *
+ */
+static int __init sanitize_e820_map(struct e820entry * biosmap, char * pnr_map)
+{
+	struct change_member {
+		struct e820entry *pbios; /* pointer to original bios entry */
+		unsigned long long addr; /* address for this change point */
+	};
+	struct change_member change_point_list[2*E820MAX];
+	struct change_member *change_point[2*E820MAX];
+	struct e820entry *overlap_list[E820MAX];
+	struct e820entry new_bios[E820MAX];
+	struct change_member *change_tmp;
+	unsigned long current_type, last_type;
+	unsigned long long last_addr;
+	int chgidx, still_changing;
+	int overlap_entries;
+	int new_bios_entry;
+	int old_nr, new_nr;
+	int i;
+
+	/* if there's only one memory region, don't bother */
+	if (*pnr_map < 2)
+		return -1;
+
+	old_nr = *pnr_map;
+
+	/* bail out if we find any unreasonable addresses in bios map */
+	for (i=0; i<old_nr; i++)
+		if (biosmap[i].addr + biosmap[i].size < biosmap[i].addr)
+			return -1;
+
+	/* create pointers for initial change-point information (for sorting) */
+	for (i=0; i < 2*old_nr; i++)
+		change_point[i] = &change_point_list[i];
+
+	/* record all known change-points (starting and ending addresses) */
+	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];
+	}
+
+	/* 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++)
+		{
+			if (change_point[i]->addr < change_point[i-1]->addr)  {
+				change_tmp = change_point[i];
+				change_point[i] = change_point[i-1];
+				change_point[i-1] = change_tmp;
+				still_changing=1;
+			}
+		}
+	}
+
+	/* create a new bios memory map, removing overlaps */
+	overlap_entries=0;	 /* number of entries in the overlap table */
+	new_bios_entry=0;	 /* index for creating new bios map entries */
+	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++)
+	{
+		/* keep track of all overlapping bios entries */
+		if (change_point[chgidx]->addr == change_point[chgidx]->pbios->addr)
+		{
+			/* add map entry to overlap list (> 1 entry implies an overlap) */
+			overlap_list[overlap_entries++]=change_point[chgidx]->pbios;
+		}
+		else
+		{
+			/* remove entry from list (order independent, so swap with last) */
+			for (i=0; i<overlap_entries; i++)
+			{
+				if (overlap_list[i] == change_point[chgidx]->pbios)
+					overlap_list[i] = overlap_list[overlap_entries-1];
+			}
+			overlap_entries--;
+		}
+		/* if there are overlapping entries, decide which "type" to use */
+		/* (larger value takes precedence -- 1=usable, 2,3,4,+=unusable) */
+		current_type = 0;
+		for (i=0; i<overlap_entries; i++)
+			if (overlap_list[i]->type > current_type)
+				current_type = overlap_list[i]->type;
+		/* continue building up new bios map based on this information */
+		if (current_type != last_type)	{
+			if (last_type != 0)	 {
+				new_bios[new_bios_entry].size =
+					change_point[chgidx]->addr - last_addr;
+				if (++new_bios_entry >= E820MAX)
+					break;	/* no more space left for new bios entries */
+			}
+			if (current_type != 0)	{
+				new_bios[new_bios_entry].addr = change_point[chgidx]->addr;
+				new_bios[new_bios_entry].type = current_type;
+				last_addr=change_point[chgidx]->addr;
+			}
+			last_type = current_type;
+		}
+	}
+	new_nr = new_bios_entry;   /* retain count for new bios entries */
+
+	/* copy new bios mapping into original location */
+	memcpy(biosmap, new_bios, new_nr*sizeof(struct e820entry));
+	*pnr_map = new_nr;
+
+	return 0;
+}
+
+/*
  * Copy the BIOS e820 map into a safe place.
  *
  * Sanity-check it while we're at it..
@@ -504,6 +627,7 @@
 	 * Otherwise fake a memory map; one section from 0k->640k,
 	 * the next section from 1mb->appropriate_mem_k
 	 */
+	sanitize_e820_map(E820_MAP, &E820_MAP_NR);
 	if (copy_e820_map(E820_MAP, E820_MAP_NR) < 0) {
 		unsigned long mem_size;
 
@@ -2267,7 +2391,7 @@
 			p+=sprintf(p, " (%dMhz/%dMhz FSB)\n", c->cpuclock,
 						c->busclock);
 		else if(c->busclock)
-			p+sprintf(p, " (%dMhz FSB)\n", c->busclock);
+			p+=sprintf(p, " (%dMhz FSB)\n", c->busclock);
 		else
 			p+=sprintf(p, "\n");
 			

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: 2.4.2-ac6 hangs on boot w/AMD Elan SC520 dev board
  2001-02-28 11:45 ` Alan Cox
  2001-03-01  8:56   ` Brian Moyle
@ 2001-03-01 12:12   ` Pavel Machek
  2001-04-05  0:36     ` Brian Moyle
  1 sibling, 1 reply; 7+ messages in thread
From: Pavel Machek @ 2001-03-01 12:12 UTC (permalink / raw)
  To: Alan Cox; +Cc: Brian Moyle, linux-kernel

Hi!

> >     bios-e820: 000000000009f400 @ 0000000000000000 (usable)
> >     bios-e820: 0000000000000c00 @ 000000000009f400 (reserved)
> >     bios-e820: 0000000003f00000 @ 0000000000100000 (usable)
> >     bios-e820: 0000000003f00000 @ 0000000000100000 (usable)
> >     bios-e820: 0000000000100000 @ 00000000fff00000 (reserved)
> >    (at this point, it appears to be in an infinite printk loop <?>)
> > 
> > I didn't spend much time looking into the printk loop, but it seems to 
> > end up there, even if CONFIG_DEBUG_BUGVERBOSE is not defined, as if the 
> > ".byte 0x0f,0x0b" is causing the loop to begin.
> > 
> > Any ideas/suggestions/comments?
> 
> Having been over the code the problem is indeed the bios reporting overlapping
> /duplicated ranges. That will cause a crash in mm/bootmem when we try and free
> the range twice.
> 
> I suspect you need to add some code to take the E820 map and remove any
> overlaps from it, favouring ROM over RAM if the types disagree (for safety),
> and filter them before you register them with the bootmem in 
> arch/i386/kernel/setup.c

...plus prining ?@#@&#&$ BIOS reports invalid mem map
seems like good idea, so that bios bugs are fixed.

-- 
Philips Velo 1: 1"x4"x8", 300gram, 60, 12MB, 40bogomips, linux, mutt,
details at http://atrey.karlin.mff.cuni.cz/~pavel/velo/index.html.


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: 2.4.2-ac6 hangs on boot w/AMD Elan SC520 dev board
  2001-03-01  8:56   ` Brian Moyle
@ 2001-03-02  1:32     ` Brian Moyle
  0 siblings, 0 replies; 7+ messages in thread
From: Brian Moyle @ 2001-03-02  1:32 UTC (permalink / raw)
  To: Alan Cox; +Cc: linux-kernel, bmoyle

[-- Attachment #1: Type: text/plain, Size: 244 bytes --]

I spent some time testing the previous patch today.  I found a couple of corner cases that weren't handled correctly in the first version.

I've attached a new version (against 2.4.2-ac7) that should fix those problems.

Brian
bmoyle@mvista.com

[-- Attachment #2: e820bios.patch2 --]
[-- Type: text/plain, Size: 6299 bytes --]

--- linux-2.4.2-ac7/arch/i386/kernel/setup.c	Thu Mar  1 15:39:08 2001
+++ linux/arch/i386/kernel/setup.c	Thu Mar  1 15:59:06 2001
@@ -58,6 +58,9 @@
  *  Massive cleanup of CPU detection and bug handling;
  *  Transmeta CPU detection,
  *  H. Peter Anvin <hpa@zytor.com>, November 2000
+ *
+ *  Added E820 sanitization routine (removes overlapping memory regions);
+ *  Brian Moyle <bmoyle@mvista.com>, February 2001
  */
 
 /*
@@ -438,6 +441,170 @@
 }
 
 /*
+ * Sanitize the BIOS e820 map.
+ *
+ * Some e820 responses include overlapping entries.  The following 
+ * replaces the original e820 map with a new one, removing overlaps.
+ *
+ */
+static int __init sanitize_e820_map(struct e820entry * biosmap, char * pnr_map)
+{
+	struct change_member {
+		struct e820entry *pbios; /* pointer to original bios entry */
+		unsigned long long addr; /* address for this change point */
+	};
+	struct change_member change_point_list[2*E820MAX];
+	struct change_member *change_point[2*E820MAX];
+	struct e820entry *overlap_list[E820MAX];
+	struct e820entry new_bios[E820MAX];
+	struct change_member *change_tmp;
+	unsigned long current_type, last_type;
+	unsigned long long last_addr;
+	int chgidx, still_changing;
+	int overlap_entries;
+	int new_bios_entry;
+	int old_nr, new_nr;
+	int i;
+
+	/*
+		Visually we're performing the following (1,2,3,4 = memory types)...
+
+		Sample memory map (w/overlaps):
+		   ____22__________________
+		   ______________________4_
+		   ____1111________________
+		   _44_____________________
+		   11111111________________
+		   ____________________33__
+		   ___________44___________
+		   __________33333_________
+		   ______________22________
+		   ___________________2222_
+		   _________111111111______
+		   _____________________11_
+		   _________________4______
+
+		Sanitized equivalent (no overlap):
+		   1_______________________
+		   _44_____________________
+		   ___1____________________
+		   ____22__________________
+		   ______11________________
+		   _________1______________
+		   __________3_____________
+		   ___________44___________
+		   _____________33_________
+		   _______________2________
+		   ________________1_______
+		   _________________4______
+		   ___________________2____
+		   ____________________33__
+		   ______________________4_
+	*/
+
+	/* if there's only one memory region, don't bother */
+	if (*pnr_map < 2)
+		return -1;
+
+	old_nr = *pnr_map;
+
+	/* bail out if we find any unreasonable addresses in bios map */
+	for (i=0; i<old_nr; i++)
+		if (biosmap[i].addr + biosmap[i].size < biosmap[i].addr)
+			return -1;
+
+	/* create pointers for initial change-point information (for sorting) */
+	for (i=0; i < 2*old_nr; i++)
+		change_point[i] = &change_point_list[i];
+
+	/* record all known change-points (starting and ending addresses) */
+	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];
+	}
+
+	/* 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++)  {
+			/* 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) ||
+				((change_point[i]->addr == change_point[i-1]->addr) &&
+				 (change_point[i]->addr == change_point[i]->pbios->addr) &&
+				 (change_point[i-1]->addr != change_point[i-1]->pbios->addr))
+			   )
+			{
+				change_tmp = change_point[i];
+				change_point[i] = change_point[i-1];
+				change_point[i-1] = change_tmp;
+				still_changing=1;
+			}
+		}
+	}
+
+	/* create a new bios memory map, removing overlaps */
+	overlap_entries=0;	 /* number of entries in the overlap table */
+	new_bios_entry=0;	 /* index for creating new bios map entries */
+	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++)
+	{
+		/* keep track of all overlapping bios entries */
+		if (change_point[chgidx]->addr == change_point[chgidx]->pbios->addr)
+		{
+			/* add map entry to overlap list (> 1 entry implies an overlap) */
+			overlap_list[overlap_entries++]=change_point[chgidx]->pbios;
+		}
+		else
+		{
+			/* remove entry from list (order independent, so swap with last) */
+			for (i=0; i<overlap_entries; i++)
+			{
+				if (overlap_list[i] == change_point[chgidx]->pbios)
+					overlap_list[i] = overlap_list[overlap_entries-1];
+			}
+			overlap_entries--;
+		}
+		/* if there are overlapping entries, decide which "type" to use */
+		/* (larger value takes precedence -- 1=usable, 2,3,4,4+=unusable) */
+		current_type = 0;
+		for (i=0; i<overlap_entries; i++)
+			if (overlap_list[i]->type > current_type)
+				current_type = overlap_list[i]->type;
+		/* continue building up new bios map based on this information */
+		if (current_type != last_type)	{
+			if (last_type != 0)	 {
+				new_bios[new_bios_entry].size =
+					change_point[chgidx]->addr - last_addr;
+				/* move forward only if the new size was non-zero */
+				if (new_bios[new_bios_entry].size != 0)
+					if (++new_bios_entry >= E820MAX)
+						break; 	/* no more space left for new bios entries */
+			}
+			if (current_type != 0)	{
+				new_bios[new_bios_entry].addr = change_point[chgidx]->addr;
+				new_bios[new_bios_entry].type = current_type;
+				last_addr=change_point[chgidx]->addr;
+			}
+			last_type = current_type;
+		}
+	}
+	new_nr = new_bios_entry;   /* retain count for new bios entries */
+
+	/* copy new bios mapping into original location */
+	memcpy(biosmap, new_bios, new_nr*sizeof(struct e820entry));
+	*pnr_map = new_nr;
+
+	return 0;
+}
+
+/*
  * Copy the BIOS e820 map into a safe place.
  *
  * Sanity-check it while we're at it..
@@ -504,6 +671,7 @@
 	 * Otherwise fake a memory map; one section from 0k->640k,
 	 * the next section from 1mb->appropriate_mem_k
 	 */
+	sanitize_e820_map(E820_MAP, &E820_MAP_NR);
 	if (copy_e820_map(E820_MAP, E820_MAP_NR) < 0) {
 		unsigned long mem_size;
 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: 2.4.2-ac6 hangs on boot w/AMD Elan SC520 dev board
  2001-03-01 12:12   ` Pavel Machek
@ 2001-04-05  0:36     ` Brian Moyle
  0 siblings, 0 replies; 7+ messages in thread
From: Brian Moyle @ 2001-04-05  0:36 UTC (permalink / raw)
  To: Pavel Machek; +Cc: Alan Cox, linux-kernel, bmoyle

[-- Attachment #1: Type: text/plain, Size: 836 bytes --]

Changes are isolated to sanitize_e820_map().

(patch is against linux-2.4.3-ac3)

This change informs the user when overlaping memory regions have been
found in their e820 map.  If overlaps were found, it displays the 
original mapping and then creates an adjusted map (w/o overlaps).  If 
no overlaps were found, it leaves the original bios-provided map alone.  
Formatting changes were also made so source fits w/in 80 columns.

Hope it helps,

Brian

Pavel Machek wrote:
> > I suspect you need to add some code to take the E820 map and remove any
> > overlaps from it, favouring ROM over RAM if the types disagree (for safety),
> > and filter them before you register them with the bootmem in
> > arch/i386/kernel/setup.c
> 
> ...plus prining ?@#@&#&$ BIOS reports invalid mem map
> seems like good idea, so that bios bugs are fixed.

[-- Attachment #2: e820bios.patch3 --]
[-- Type: text/plain, Size: 6939 bytes --]

--- linux-2.4.3-ac3/arch/i386/kernel/setup.c	Wed Apr  4 16:30:35 2001
+++ linux/arch/i386/kernel/setup.c	Wed Apr  4 16:34:03 2001
@@ -447,8 +447,8 @@
 /*
  * Sanitize the BIOS e820 map.
  *
- * Some e820 responses include overlapping entries.  The following 
- * replaces the original e820 map with a new one, removing overlaps.
+ * Some e820 responses include overlapping memory regions.  If overlaps are
+ * found, we'll replace the original e820 map with a new one (w/o overlaps).
  *
  */
 static int __init sanitize_e820_map(struct e820entry * biosmap, char * pnr_map)
@@ -464,14 +464,14 @@
 	struct change_member *change_tmp;
 	unsigned long current_type, last_type;
 	unsigned long long last_addr;
+	int overlap_entries, overlaps_found;
 	int chgidx, still_changing;
-	int overlap_entries;
 	int new_bios_entry;
 	int old_nr, new_nr;
 	int i;
 
 	/*
-		Visually we're performing the following (1,2,3,4 = memory types)...
+		Visually, we're performing the following (1,2,3,4 = mem types):
 
 		Sample memory map (w/overlaps):
 		   ____22__________________
@@ -517,7 +517,7 @@
 		if (biosmap[i].addr + biosmap[i].size < biosmap[i].addr)
 			return -1;
 
-	/* create pointers for initial change-point information (for sorting) */
+	/* create pointers for initial change-point info (used for sorting) */
 	for (i=0; i < 2*old_nr; i++)
 		change_point[i] = &change_point_list[i];
 
@@ -535,13 +535,15 @@
 	while (still_changing)	{
 		still_changing = 0;
 		for (i=1; i < 2*old_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) ||
-				((change_point[i]->addr == change_point[i-1]->addr) &&
-				 (change_point[i]->addr == change_point[i]->pbios->addr) &&
-				 (change_point[i-1]->addr != change_point[i-1]->pbios->addr))
-			   )
+			/* if <current_addr> < <last_addr>, swap */
+			/* or, if curr=<start_addr> & last=<end_addr>, swap */
+			if ((change_point[i]->addr<change_point[i-1]->addr) ||
+				((change_point[i]->addr ==
+					change_point[i-1]->addr) &&
+				 (change_point[i]->addr ==
+					change_point[i]->pbios->addr) &&
+				 (change_point[i-1]->addr !=
+					change_point[i-1]->pbios->addr)))
 			{
 				change_tmp = change_point[i];
 				change_point[i] = change_point[i-1];
@@ -552,47 +554,72 @@
 	}
 
 	/* create a new bios memory map, removing overlaps */
-	overlap_entries=0;	 /* number of entries in the overlap table */
-	new_bios_entry=0;	 /* index for creating new bios map entries */
-	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 */
+	overlaps_found=0;   /* indicates whether or not an overlap was found */
+	overlap_entries=0;  /* number of entries in the overlap table */
+	new_bios_entry=0;   /* index for creating new bios map entries */
+	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 new bios map */
 	for (chgidx=0; chgidx < 2*old_nr; chgidx++)
 	{
 		/* keep track of all overlapping bios entries */
-		if (change_point[chgidx]->addr == change_point[chgidx]->pbios->addr)
+		if (change_point[chgidx]->addr ==
+			change_point[chgidx]->pbios->addr)
 		{
-			/* add map entry to overlap list (> 1 entry implies an overlap) */
-			overlap_list[overlap_entries++]=change_point[chgidx]->pbios;
+			/* add entry to overlap list (>1 implies overlap) */
+			overlap_list[overlap_entries++]=
+				change_point[chgidx]->pbios;
+			/* check for legitimately overlapped memory regions */
+			if (overlap_entries == 2)
+			{
+				/* note: it's possible to have 2 entries w/out
+				   an overlap ([0]=end_addr, [1]=start_addr) */
+				if ((overlap_list[0]->addr +
+					overlap_list[0]->size) !=
+						(overlap_list[1]->addr))
+				{
+					overlaps_found = 1;
+				}
+			}
+			else if (overlap_entries > 1)
+			{
+				overlaps_found = 1;
+			}
 		}
 		else
 		{
-			/* remove entry from list (order independent, so swap with last) */
+			/* remove entry from list (order independent, so swap
+			   with last) */
 			for (i=0; i<overlap_entries; i++)
 			{
-				if (overlap_list[i] == change_point[chgidx]->pbios)
-					overlap_list[i] = overlap_list[overlap_entries-1];
+				if (overlap_list[i] ==
+					change_point[chgidx]->pbios)
+				{
+					overlap_list[i] =
+						overlap_list[overlap_entries-1];
+				}
 			}
 			overlap_entries--;
 		}
-		/* if there are overlapping entries, decide which "type" to use */
-		/* (larger value takes precedence -- 1=usable, 2,3,4,4+=unusable) */
+		/* if overlapping entries, decide which "type" to use */
+		/* (larger takes precedence -- 1=usable, 2,3,4,4+=unusable) */
 		current_type = 0;
 		for (i=0; i<overlap_entries; i++)
 			if (overlap_list[i]->type > current_type)
 				current_type = overlap_list[i]->type;
-		/* continue building up new bios map based on this information */
+		/* continue building new bios map based on this information */
 		if (current_type != last_type)	{
 			if (last_type != 0)	 {
 				new_bios[new_bios_entry].size =
 					change_point[chgidx]->addr - last_addr;
-				/* move forward only if the new size was non-zero */
+				/* move forward if the new size is non-zero */
 				if (new_bios[new_bios_entry].size != 0)
 					if (++new_bios_entry >= E820MAX)
-						break; 	/* no more space left for new bios entries */
+						break;	/* no more space */
 			}
 			if (current_type != 0)	{
-				new_bios[new_bios_entry].addr = change_point[chgidx]->addr;
+				new_bios[new_bios_entry].addr =
+					change_point[chgidx]->addr;
 				new_bios[new_bios_entry].type = current_type;
 				last_addr=change_point[chgidx]->addr;
 			}
@@ -601,9 +628,36 @@
 	}
 	new_nr = new_bios_entry;   /* retain count for new bios entries */
 
-	/* copy new bios mapping into original location */
-	memcpy(biosmap, new_bios, new_nr*sizeof(struct e820entry));
-	*pnr_map = new_nr;
+	/* if we found overlaps, replace bios map with an adjusted version */
+	if (overlaps_found == 1)
+	{
+		printk(KERN_INFO "Overlaps found in BIOS memory map:\n");
+		for (i = 0; i < old_nr; i++) {
+			printk(" BIOS-e820: %016Lx - %016Lx ", biosmap[i].addr,
+				biosmap[i].addr + biosmap[i].size);
+			switch (biosmap[i].type) {
+				case E820_RAM:
+					printk("(usable)\n");
+					break;
+				case E820_RESERVED:
+					printk("(reserved)\n");
+					break;
+				case E820_ACPI:
+					printk("(ACPI data)\n");
+					break;
+				case E820_NVS:
+					printk("(ACPI NVS)\n");
+					break;
+				default:
+					printk("type %lu\n", biosmap[i].type);
+					break;
+			}
+		}
+		printk(KERN_INFO "Compensating...\n");
+		/* copy new bios mapping into original location */
+		memcpy(biosmap, new_bios, new_nr*sizeof(struct e820entry));
+		*pnr_map = new_nr;
+	}
 
 	return 0;
 }

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2001-04-05  0:37 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2001-02-28  3:12 2.4.2-ac6 hangs on boot w/AMD Elan SC520 dev board Brian Moyle
2001-02-28  9:49 ` Alan Cox
2001-02-28 11:45 ` Alan Cox
2001-03-01  8:56   ` Brian Moyle
2001-03-02  1:32     ` Brian Moyle
2001-03-01 12:12   ` Pavel Machek
2001-04-05  0:36     ` Brian Moyle

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).