diff -Naur linux-2.4.0orig/Documentation/Configure.help linux-2.4.0exp/Documentation/Configure.help
--- linux-2.4.0orig/Documentation/Configure.help	Wed May 31 12:01:46 2000
+++ linux-2.4.0exp/Documentation/Configure.help	Wed May 31 11:29:19 2000
@@ -185,6 +185,40 @@
   on the Alpha. The only time you would ever not say Y is to say M in
   order to debug the code. Say Y unless you know what you are doing.
 
+Use extended bios calls to detect memory
+CONFIG_MEM_E820
+  The traditional Linux memory detection code for ia32 machines is
+  restricted to detecting 64mb (or 16mb!) of core.   The E820 call,
+  which is used by many operating systems, is a modern replacement
+  for it, which gives back a detailed memory map of usable memory,
+  rom, and ACPI tables.  Older machines ignore this detection method,
+  so you should be able to say Y here unless your machine has
+  problems correctly detecting memory.
+
+Merge adjacent memory regions
+CONFIG_E820_MERGE
+  The E820 bios call may return a fragmented map where many segments
+  of the map are too small to fit a Linux memory table entry onto.
+  This option merges adjacent memory sections together before
+  initializing the Linux memory tables.
+
+Reclaim ACPI table memory (DANGEROUS)
+CONFIG_E820_RECLAIM
+  If you are not using ACPI (CONFIG_ACPI) on this machine, you can
+  use this option to use ACPI tables as regular memory.  This is a
+  dangerous option -- some people have reported that attempting to
+  reclaim ACPI tables result in Linux crashing -- so only answer Y
+  if you know what you are doing.
+
+Use bios call e801 to detect memory
+CONFIG_MEM_E801
+  The E801 bios call was an earlier method for detecting memory on
+  ia32 machines.   It has fallen out of favor, and some modern
+  bioses have broken it so that it returns garbage. The kernel
+  will attempt to use this call if the e820 bios call does not
+  exist on the machine.  It's probably safe to answer Y here,
+  though 99% of the machines out there won't use this call.
+
 High Memory support
 CONFIG_NOHIGHMEM
   Linux can use up to 64 Gigabytes of physical memory on x86 systems.
diff -Naur linux-2.4.0orig/Documentation/i386/zero-page.txt linux-2.4.0exp/Documentation/i386/zero-page.txt
--- linux-2.4.0orig/Documentation/i386/zero-page.txt	Wed May 31 12:01:20 2000
+++ linux-2.4.0exp/Documentation/i386/zero-page.txt	Sat May 27 01:59:31 2000
@@ -29,7 +29,8 @@
 			( struct sys_desc_table_struct )
  0xb0 - 0x1df		Free. Add more parameters here if you really need them.
 
-0x1e0	unsigned long	ALT_MEM_K, alternative mem check, in Kb
+0x1e0	unsigned long	E801_MEM_LOW, e801 # kbytes between 1mb and 16mb
+0x1e4	unsigned long	E801_MEM_HIGH, e801 # kbytes above 16mb
 0x1e8	char		number of entries in E820MAP (below)
 0x1f1	char		size of setup.S, number of sectors
 0x1f2	unsigned short	MOUNT_ROOT_RDONLY (if !=0)
diff -Naur linux-2.4.0orig/Makefile linux-2.4.0exp/Makefile
--- linux-2.4.0orig/Makefile	Wed May 31 12:01:46 2000
+++ linux-2.4.0exp/Makefile	Wed May 31 11:29:19 2000
@@ -336,10 +336,10 @@
 	if [ -f PCMCIA_CHAR_MODULES ]; then inst_mod PCMCIA_CHAR_MODULES pcmcia; fi; \
 	if [ -f PCMCIA_SCSI_MODULES ]; then inst_mod PCMCIA_SCSI_MODULES pcmcia; fi; \
 	\
-	ls -1 -U *.o | sort > $$MODLIB/.allmods; \
+	ls -1 *.o | sort > $$MODLIB/.allmods; \
 	if [ -f $$MODLIB/net/3c59x.o ]; then \
 		mkdir -p $$MODLIB/pcmcia; \
-		ln -nfs ../net/3c59x.o $$MODLIB/pcmcia/3c575_cb.o; \
+		ln -fs ../net/3c59x.o $$MODLIB/pcmcia/3c575_cb.o; \
 		MODULES="$$MODULES 3c575_cb.o"; fi; \
 	echo $$MODULES | tr ' ' '\n' | sort | comm -23 $$MODLIB/.allmods - > $$MODLIB/.misc; \
 	if [ -s $$MODLIB/.misc ]; then inst_mod $$MODLIB/.misc misc; fi; \
diff -Naur linux-2.4.0orig/arch/i386/boot/setup.S linux-2.4.0exp/arch/i386/boot/setup.S
--- linux-2.4.0orig/arch/i386/boot/setup.S	Wed May 31 12:01:19 2000
+++ linux-2.4.0exp/arch/i386/boot/setup.S	Sat May 27 01:59:30 2000
@@ -32,6 +32,9 @@
  *
  * Transcribed from Intel (as86) -> AT&T (gas) by Chris Noe, May 1999.
  * <stiker@northlink.com>
+ *
+ * Extended memory detection made more paranoid by orc@pell.chi.il.us (jessica l.
+ * parsons) and Nathan Zook (nathan.zook@amd.com), December 1999-February 2000.
  */
 
 #define __ASSEMBLY__
@@ -255,91 +258,99 @@
 loader_panic_mess: .string "Wrong loader, giving up..."
 
 loader_ok:
-# Get memory size (extended mem, kB)
+/* MEMORY DETECTION CODE */
 
 	xorl	%eax, %eax
-	movl	%eax, (0x1e0)
-#ifndef STANDARD_MEMORY_BIOS_CALL
-	movb	%al, (E820NR)
-# Try three different memory detection schemes.  First, try
-# e820h, which lets us assemble a memory map, then try e801h,
-# which returns a 32-bit memory size, and finally 88h, which
-# returns 0-64m
-
-# method E820H:
-# the memory map from hell.  e820h returns memory classified into
-# a whole bunch of different types, and allows memory holes and
-# everything.  We scan through this memory map and build a list
-# of the first 32 memory areas, which we return at [E820MAP].
-#
+	movl	%eax, (0x1e0)		# zero e801_mem_low
+	movl	%eax, (0x1e4)		#  "   e801_mem_high
+	movb	%al, (E820NR)		#  "   e820_nr
+
+/*
+ * Try three different memory detection schemes.  First, try
+ * e820h, which lets us assemble a memory map, then try e801h,
+ * which returns a 32-bit memory size, and finally 88h, which
+ * returns 0-64m
+ */
 
-meme820:
-	movl	$0x534d4150, %edx		# ascii `SMAP'
-	xorl	%ebx, %ebx			# continuation counter
-	movw	$E820MAP, %di			# point into the whitelist
-						# so we can have the bios
-						# directly write into it.
+#ifdef CONFIG_MEM_E820
+
+/*
+ * method E820H:
+ * the memory map from hell.  e820h returns memory classified into
+ * a whole bunch of different types, and allows memory holes and
+ * everything.  We scan through this memory map and build a list
+ * of the first E820MAX memory areas, which we return at [E820MAP].
+ */
+#define SMAP	0x534d4150
 
+meme820:
+	xorl	%ebx, %ebx		# e820 continuation counter
+	movw	$E820MAP, %di		# point di at the map
 jmpe820:
-	movl	$0x0000e820, %eax		# e820, upper word zeroed
-	movl	$20, %ecx			# size of the e820rec
-	pushw	%ds				# data record.
+	pushw	%ds			# set es := ds every time around.
 	popw	%es
-	int	$0x15				# make the call
-	jc	bail820				# fall to e801 if it fails
-
-	cmpl	$0x534d4150, %eax		# check the return is `SMAP'
-	jne	bail820				# fall to e801 if it fails
-
-#	cmpl	$1, 16(%di)			# is this usable memory?
-#	jne	again820
-
-	# If this is usable memory, we save it by simply advancing %di by
-	# sizeof(e820rec).
-	#
-good820:
-	movb	(E820NR), %al			# up to 32 entries
-	cmpb	$E820MAX, %al
-	jnl	bail820
-
-	incb	(E820NR)
-	movw	%di, %ax
-	addw	$20, %ax
-	movw	%ax, %di
-again820:
-	cmpl	$0, %ebx			# check to see if
-	jne	jmpe820				# %ebx is set to EOF
-bail820:
-
-
-# method E801H:
-# memory size is in 1k chunksizes, to avoid confusing loadlin.
-# we store the 0xe801 memory size in a completely different place,
-# because it will most likely be longer than 16 bits.
-# (use 1e0 because that's what Larry Augustine uses in his
-# alternative new memory detection scheme, and it's sensible
-# to write everything into the same place.)
+	movl	$0x0000e820, %eax	# e820, upper word zeroed
+	movl	$SMAP, %edx		# assign magic numbers
+	movl	$E820LEN, %ecx		# we'd like one memory record, please.
+
+	int	$0x15			# make the call
+	jc	fin820			# fall out if it fails
+
+	cmpl	$SMAP, %eax		# did e820 properly move the magic?
+	jne	punte820
+
+	cmpw	$E820LEN, %cx		# did we get back a whole memory record?
+	jne	punte820
+
+stash820:
+	incb	(E820NR)		# up the # of records
+	movb	(E820NR), %al		# If the table fills up
+	cmpb	$E820MAX, %al		# we must flee
+	jnl	fin820
+
+	cmp	$0, %ebx		# any more records in the pipe?
+	jz	fin820			# apparently not
+
+	addw	$E820LEN, %di		# point %di at the next record
+	jmp	jmpe820			# and go around again
+
+punte820:				# if something goes wrong, don't
+	xor	%ah, %ah		# let userland look at this (corrupt?)
+	movb	%ah, (E820NR)		# e820 map.
+fin820:
+
+#endif/*CONFIG_MEM_E820*/
+
+#ifdef CONFIG_MEM_E801
+/*
+ * method E801H:
+ * e801 returns 2 memory chunks: 1 from 1mb to 16mb, and 1 from 16mb up.
+ * return the both of them, low in 1e0h, for compatability with older
+ * versions of memory detection code, and high in 1e4h.
+ */
 
 meme801:
 	movw	$0xe801, %ax
 	int	$0x15
-	jc	mem88
+	jc	fine801
 
-	andl	$0xffff, %edx			# clear sign extend
-	shll	$6, %edx			# and go from 64k to 1k chunks
-	movl	%edx, (0x1e0)			# store extended memory size
 	andl	$0xffff, %ecx			# clear sign extend
- 	addl	%ecx, (0x1e0)			# and add lower memory into
-						# total size.
+	movl	%ecx, (0x1e0)			# store 1kb between 1-16mb
+	andl	$0xffff, %edx			# clear sign extend
+	movl	%edx, (0x1e4)			# store 64kb chunks above 16mb
+fine801:
 
-# Ye Olde Traditional Methode.  Returns the memory size (up to 16mb or
-# 64mb, depending on the bios) in ax.
-mem88:
+#endif/*CONFIG_MEM_E801*/
 
-#endif
+/*
+ * Ye Olde Traditional Methode.  Returns the memory size (up to 16mb or
+ * 64mb, depending on the bios) in ax.
+ */
 	movb	$0x88, %ah
 	int	$0x15
 	movw	%ax, (2)
+
+/* END OF MEMORY DETECTION CODE */
 
 # Set the keyboard repeat rate to the max
 	movw	$0x0305, %ax
diff -Naur linux-2.4.0orig/arch/i386/config.in linux-2.4.0exp/arch/i386/config.in
--- linux-2.4.0orig/arch/i386/config.in	Wed May 31 12:01:46 2000
+++ linux-2.4.0exp/arch/i386/config.in	Wed May 31 11:29:19 2000
@@ -108,6 +108,16 @@
    define_bool CONFIG_X86_PAE y
 fi
 
+mainmenu_option next_comment
+comment 'Memory detection'
+bool 'Use extended bios calls to detect memory' CONFIG_MEM_E820
+if [ "$CONFIG_MEM_E820" = "y" ]; then
+   bool '  Merge adjacent memory regions' CONFIG_E820_MERGE
+   bool '  Reclaim ACPI table memory (DANGEROUS)' CONFIG_E820_RECLAIM
+   bool '  Also try bios call E801' CONFIG_MEM_E801
+fi
+endmenu
+
 if [ "$CONFIG_X86_FXSR" != "y" ]; then
    bool 'Math emulation' CONFIG_MATH_EMULATION
 fi
diff -Naur linux-2.4.0orig/arch/i386/defconfig linux-2.4.0exp/arch/i386/defconfig
--- linux-2.4.0orig/arch/i386/defconfig	Wed May 31 12:01:46 2000
+++ linux-2.4.0exp/arch/i386/defconfig	Wed May 31 11:29:19 2000
@@ -37,6 +37,10 @@
 # CONFIG_X86_MSR is not set
 # CONFIG_X86_CPUID is not set
 CONFIG_NOHIGHMEM=y
+CONFIG_MEM_E820=y
+CONFIG_MEM_E801=y
+CONFIG_E820_MERGE=y
+#CONFIG_MEM_RECLAIM is not set
 # CONFIG_HIGHMEM4G is not set
 # CONFIG_HIGHMEM64G is not set
 # CONFIG_MATH_EMULATION is not set
diff -Naur linux-2.4.0orig/arch/i386/kernel/Makefile linux-2.4.0exp/arch/i386/kernel/Makefile
--- linux-2.4.0orig/arch/i386/kernel/Makefile	Wed May 31 12:01:19 2000
+++ linux-2.4.0exp/arch/i386/kernel/Makefile	Sat May 27 01:59:31 2000
@@ -15,7 +15,7 @@
 O_TARGET := kernel.o
 O_OBJS   := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \
 	    ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_i386.o \
-	    pci-dma.o
+	    pci-dma.o regions.o
 OX_OBJS  := i386_ksyms.o
 MX_OBJS  :=
 
diff -Naur linux-2.4.0orig/arch/i386/kernel/regions.c linux-2.4.0exp/arch/i386/kernel/regions.c
--- linux-2.4.0orig/arch/i386/kernel/regions.c	Wed May 31 12:01:19 2000
+++ linux-2.4.0exp/arch/i386/kernel/regions.c	Wed May 31 11:40:13 2000
@@ -0,0 +1,364 @@
+/*
+ * memory region setup and pruning code
+ *
+ * Copyright (c) 2000 by Jessica L. Parsons, Nathan Zook.
+ */
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/e820.h>
+
+#define PARAM	((unsigned char *)empty_zero_page)
+
+#define EXT_MEM_K (*(unsigned short *) (PARAM+2))
+#define E801_MEM_LOW (*(unsigned long *) (PARAM+0x1e0))
+#define E801_MEM_HIGH (*(unsigned long *) (PARAM+0x1e4))
+#define E820_MAP_NR (*(char*) (PARAM+E820NR))
+#define E820_MAP    ((unsigned long *) (PARAM+E820MAP))
+
+#define GIGABYTE(x)	((x) * 1024LL * 1024LL * 1024LL)
+#define MEGABYTE(x)	((x) * 1024LL * 1024LL)
+#if FLAG_E820_DEBUG
+#  define E820DBG(x)	printk x
+#else
+#  define E820DBG(x)	
+#endif
+
+/*
+ * Do NOT EVER look at the BIOS memory size location.
+ * It does not work on many machines.
+ */
+#define LOWMEMSIZE()	(639 * 1024)
+
+
+
+static struct excluded_memory memory_hole;
+
+#define Hole	memory_hole.hole
+
+/*
+ * clear_memory_regions() wipes out the memory regions map so that it
+ * can be populated from scratch
+ */
+static void __init clear_memory_regions()
+{
+    physical_memory.nr_region = 0;
+}
+
+
+/*
+ * add_memory_region() adds a memory region, merging and reclaiming memory
+ * as needed.  it returns a bitmask of what happened.
+ */
+enum region_flags __init add_memory_region(__u64 start, __u64 end, __u32 type)
+{
+	int i, x;
+	enum region_flags flag = 0;
+
+#ifdef CONFIG_E820_RECLAIM
+	if ( type == E820_ACPI )
+		flag |= RF_RECLAIM;
+	else
+#endif
+		if (type != E820_RAM)
+			return RF_NOT_RAM;
+
+	if (start == end)
+		return flag|RF_ZERO;
+
+	if (start > end)
+		return RF_TOOLONG;
+
+#ifdef CONFIG_E820_MERGE
+	/* see if we can merge this region with an existing one */
+
+	for (x=0; x < physical_memory.nr_region; x++)
+		if (end == Region[x].start) {
+			/* glue memory region onto the start of this one */
+			Region[x].start = start;
+			break;
+		}
+		else if (Region[x].end == start) {
+			/* glue memory region onto the end of this one */
+			Region[x].end = end;
+			break;
+		}
+	if (x < physical_memory.nr_region) {
+		/* If we've merged a region, check to see if the new region
+		 * want to merge with anyone else.
+		 */
+		if (x > 0 && Region[x].start == Region[x-1].end) {
+			Region[x-1].end = Region[x].end;
+			for (i=x; i < physical_memory.nr_region; i++)
+				    Region[x] = Region[x+1];
+			physical_memory.nr_region--;
+			x--;
+		}
+		if (x < physical_memory.nr_region-1 && Region[x].end == Region[x+1].start) {
+			Region[x].end = Region[x+1].end;
+			for (i=x+1; i < physical_memory.nr_region-1; i++)
+				    Region[x] = Region[x+1];
+			physical_memory.nr_region--;
+		}
+		else
+			return flag|RF_MERGED;
+	}
+#endif
+
+	if (physical_memory.nr_region == E820MAX)
+		return flag|RF_OVERFLOW;
+
+	/* insert the new region in order */
+	for (x=0; x < physical_memory.nr_region; x++)
+		if (end < Region[x].start) {
+			/* add it in here */
+			for (i=physical_memory.nr_region; i>x; --i)
+				Region[i] = Region[i-1];
+			Region[x].start = start;
+			Region[x].end   = end;
+			physical_memory.nr_region++;
+			return flag|RF_ADDED;
+		}
+	Region[physical_memory.nr_region].start = start;
+	Region[physical_memory.nr_region].end   = end;
+	physical_memory.nr_region++;
+	return flag|RF_ADDED;
+} /* add_memory_region */
+
+
+void __init print_region_map()
+{
+    int x;
+    for (x = 0; x < physical_memory.nr_region; x++)
+	printk("MEM: %010L-%010L\n", Region[x].start, Region[x].end);
+} /* print_region_map */
+
+
+#if FLAG_E820_DEBUG && defined(CONFIG_MEM_E820)
+/*
+ * report_memory_region() decodes the return value from add_memory_region
+ */
+static void __init report_memory_region(enum region_flags code)
+{
+	if (code & RF_RECLAIM)
+		printk(" reclaimed");
+	if (code & RF_ADDED)
+		printk(" added");
+	if (code & RF_MERGED)
+		printk(" merged");
+	if (code & RF_NOT_RAM)
+		printk(" rom");
+	if (code & RF_ZERO)
+		printk(" zero-length");
+
+	if (code & RF_OVERFLOW)
+		printk(" ERROR: table overflow");
+	else if (code & RF_TOOLONG)
+		printk(" ERROR: start+size > 64 bits");
+}
+#endif
+
+
+/*
+ * prune_memory_region() checks a memory region to see if it needs to be
+ * cut to fit around memory holes.  It returns the index on the next
+ * memory region to process usually x+1, but it will be x if the memory
+ * region in question is swallowed by a memory hole.
+ */
+static int __init prune_memory_region(int x)
+{
+	int i, ct;
+
+	for (ct = 0; ct < memory_hole.nr_hole; ct++) {
+
+		if (Region[x].start >= Hole[ct].start || Region[x].end <= Hole[ct].end)
+			continue;
+
+		if (Region[x].start >= Hole[ct].start) {
+			if (Region[x].end < Hole[ct].end) {
+				E820DBG(("SMAP: %010Lx - %010Lx dropped\n",
+					    Region[x].start, Region[x].end));
+				for (i=x; i < physical_memory.nr_region; i++)
+					Region[i] = Region[i+1];
+				physical_memory.nr_region--;
+				return x;
+			}
+			else {
+				E820DBG(("SMAP: %010Lx - %010Lx trimmed to %010Lx - %010Lx\n",
+					    Region[x].start, Region[x].end,
+					    Hole[ct].start, Region[x].end));
+				Region[x].start = Hole[ct].start;
+			}
+		}
+		else if (Region[x].end < Hole[ct].end || physical_memory.nr_region >= E820PHYSMAX) {
+			if (physical_memory.nr_region >= E820PHYSMAX)
+				E820DBG(("SMAP: Memory region overflow\n"));
+			E820DBG(("SMAP: %010Lx - %010Lx trimmed to %010Lx - %010Lx\n",
+				    Region[x].start, Region[x].end,
+				    Region[x].start, Hole[ct].start));
+			Region[x].end = Hole[ct].start;
+		}
+		else {
+			E820DBG(("SMAP: %010Lx - %010Lx split to %010Lx - %010Lx, %010Lx - %010Lx\n",
+				Region[x].start, Region[x].end,
+				Region[x].start, Hole[ct].start,
+				Hole[ct].end, Region[x].end));
+			for (i=physical_memory.nr_region; i > x; --i)
+				Region[i+1] = Region[i];
+
+			Region[x+1].start = Hole[ct].end;
+			Region[x+1].end = Region[x].end;
+			Region[x].end = Hole[ct].start;
+			physical_memory.nr_region++;
+		}
+	}
+	return x+1;
+}
+
+
+/*
+ * validate_regions() validates the physical_memory map, plus lops out memory
+ * that lives in suspicious places.
+ */
+int __init validate_regions(void)
+{
+	unsigned int x;
+
+	for (x=0; x < physical_memory.nr_region; ) {
+		if (x < physical_memory.nr_region-1
+				    && Region[x].end >= Region[x+1].start) {
+			printk("SMAP: region %d (%010Lx - %010Lx) overlaps region %d (%010Lx - %010Lx)\n",
+				    x, Region[x].start,
+				       Region[x].end,
+				    x+1, Region[x+1].start,
+					 Region[x+1].end);
+			return 1;
+		}
+		if ( (x = prune_memory_region(x)) < 0)
+		    return 1;
+	}
+	return 0;
+} /* validate_regions */
+
+
+/*
+ * add_memory_hole() does what you'd think it does.
+ */
+int __init add_memory_hole(__u64 start, __u64 end)
+{
+    int idx;
+
+    if ( (idx=memory_hole.nr_hole) < NR_HOLES) {
+	Hole[idx].start = start;
+	Hole[idx].end   = end;
+	memory_hole.nr_hole ++ ;
+	return 0;
+    }
+    printk("memory hole overflow adding %010Lx-%010Lx\n", start, end);
+    return 1;
+} /* add_memory_hole */
+
+
+/*
+ * setup_detected_memory() build the initial physical_memory.region[] map
+ * from the information returned by the system bios (either an e820 memory
+ * map, a 801 low/high memory map, or the old fashioned 88 call)
+ */
+void __init setup_detected_memory(void)
+{
+	int i;
+	enum region_flags ret;
+
+	add_memory_hole(LOWMEMSIZE(), HIGH_MEMORY);
+#if FLAG_E820_4GB_ONLY
+	add_memory_hole(GIGABYTE(4), (__u64)-1);
+#endif
+	clear_memory_regions();
+
+#ifdef CONFIG_MEM_E820
+	/*
+	 * If we're lucky and live on a modern system, the setup code
+	 * will have given us a memory map that we can use to properly
+	 * set up memory.  If we aren't, we'll fake a memory map.
+	 *
+	 * We check to see that the memory map contains at least 2 elements
+	 * before we'll use it, because the detection code in setup.S may
+	 * not be perfect and most every PC known to man has two memory
+	 * regions: one from 0 to ~640k, and one from 1mb up.
+	 */
+	if ( (E820_MAP_NR > 1) && (E820_MAP_NR < E820MAX) ) {
+		/* got a memory map; copy it into a safe place.
+		 */
+		physical_memory.nr_bios = E820_MAP_NR;
+		memcpy(&(Bios), E820_MAP, E820_MAP_NR * sizeof Bios[0]);
+		for (i=0; i < E820_MAP_NR; i++) {
+			ret = add_memory_region(Bios[i].addr,
+					Bios[i].addr + Bios[i].size,
+					Bios[i].type);
+#if FLAG_E820_DEBUG
+			printk("SMAP: %010Lx - %010Lx ",
+				Bios[i].addr,
+				Bios[i].addr + Bios[i].size);
+			switch (Bios[i].type) {
+			case E820_RAM:	printk("(usable)");
+					break;
+			case E820_RESERVED:
+					printk("(reserved)");
+					break;
+			case E820_ACPI:
+					printk("(ACPI data)");
+					break;
+			case E820_NVS:
+					printk("(ACPI NVS)");
+					break;
+			default:
+					printk("(type %lu)",
+					    (unsigned long)Bios[i].type);
+					break;
+			}
+			report_memory_region(ret);
+			printk("\n");
+#endif
+			if (ret & (RF_TOOLONG|RF_OVERFLOW))
+				goto oldstyle;
+		}
+		/* after putting in the e820 map, massage it into shape
+		 * for use by the kernel.  If validate_regions returns
+		 * anything except 0, something is very ill in the state
+		 * of the bios map and we'll discard it with extreme
+		 * prejudice.
+		 */
+		if (validate_regions() == 0)
+			return /* the map is fine, so we'll use it */;
+	}
+#endif
+
+    oldstyle:
+	clear_memory_regions();
+
+#ifdef CONFIG_MEM_E801
+	if (E801_MEM_LOW > 0) {
+	    add_memory_region(0, (E801_MEM_LOW<<10)+HIGH_MEMORY, E820_RAM);
+	    add_memory_region(MEGABYTE(16), MEGABYTE(16)+(E801_MEM_HIGH<<16), E820_RAM);
+	}
+	else
+#endif
+	    add_memory_region(0, HIGH_MEMORY+(EXT_MEM_K << 10), E820_RAM);
+
+	validate_regions();
+} /* setup_detected_memory */
diff -Naur linux-2.4.0orig/arch/i386/kernel/setup.c linux-2.4.0exp/arch/i386/kernel/setup.c
--- linux-2.4.0orig/arch/i386/kernel/setup.c	Wed May 31 12:01:46 2000
+++ linux-2.4.0exp/arch/i386/kernel/setup.c	Wed May 31 12:18:51 2000
@@ -120,7 +120,7 @@
 	unsigned char table[0];
 };
 
-struct e820map e820 = { 0 };
+struct physical_memory physical_memory;
 
 unsigned char aux_device_present;
 
@@ -139,10 +139,7 @@
  */
 #define PARAM	((unsigned char *)empty_zero_page)
 #define SCREEN_INFO (*(struct screen_info *) (PARAM+0))
-#define EXT_MEM_K (*(unsigned short *) (PARAM+2))
-#define ALT_MEM_K (*(unsigned long *) (PARAM+0x1e0))
-#define E820_MAP_NR (*(char*) (PARAM+E820NR))
-#define E820_MAP    ((unsigned long *) (PARAM+E820MAP))
+
 #define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+0x40))
 #define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80))
 #define SYS_DESC_TABLE (*(struct sys_desc_table_struct*)(PARAM+0xa0))
@@ -380,9 +377,9 @@
 	}
 }
 
-unsigned long __init memparse(char *ptr, char **retptr)
+unsigned long long __init memparse(char *ptr, char **retptr)
 {
-	unsigned long ret;
+	unsigned long long ret;
 
 	ret = simple_strtoul(ptr, retptr, 0);
 
@@ -394,98 +391,21 @@
 		ret <<= 20;
 		(*retptr)++;
 	}
+	else if (**retptr == 'G' || **retptr == 'g') {
+		ret <<= 30;
+		(*retptr)++;
+	}
 	return ret;
 } /* memparse */
 
 
-void __init add_memory_region(unsigned long start,
-                                  unsigned long size, int type)
-{
-	int x = e820.nr_map;
-
-	if (x == E820MAX) {
-	    printk("Ooops! Too many entries in the memory map!\n");
-	    return;
-	}
-
-	e820.map[x].addr = start;
-	e820.map[x].size = size;
-	e820.map[x].type = type;
-	e820.nr_map++;
-} /* add_memory_region */
-
-#define E820_DEBUG	1
-
-static void __init print_e820_map(void)
-{
-	int i;
-
-	for (i = 0; i < e820.nr_map; i++) {
-		printk(" e820: %016Lx @ %016Lx ",
-			e820.map[i].size, e820.map[i].addr);
-		switch (e820.map[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", e820.map[i].type);
-				break;
-		}
-	}
-}
-
-/*
- * Do NOT EVER look at the BIOS memory size location.
- * It does not work on many machines.
+/* setup_cmdline_memory() parses the command line looking for mem= arguments,
+ *                     and builds a memory region map out of any that it
+ *                     finds.
+ * it returns 1 if mem= is valid,
+ *            0 if mem= is bogus or there is no mem= to be found.
  */
-#define LOWMEMSIZE()	(0x9f000)
-
-void __init setup_memory_region(void)
-{
-	/*
-	 * If we're lucky and live on a modern system, the setup code
-	 * will have given us a memory map that we can use to properly
-	 * set up memory.  If we aren't, we'll fake a memory map.
-	 *
-	 * We check to see that the memory map contains at least 2 elements
-	 * before we'll use it, because the detection code in setup.S may
-	 * not be perfect and most every PC known to man has two memory
-	 * regions: one from 0 to 640k, and one from 1mb up.  (The IBM
-	 * thinkpad 560x, for example, does not cooperate with the memory
-	 * detection code.)
-	 */
-	if (E820_MAP_NR > 1) {
-		/* got a memory map; copy it into a safe place.
-		 */
-		e820.nr_map = E820_MAP_NR;
-		if (e820.nr_map > E820MAX)
-			e820.nr_map = E820MAX;
-		memcpy(e820.map, E820_MAP, e820.nr_map * sizeof e820.map[0]);
-	}
-	else {
-		/* otherwise fake a memory map; one section from 0k->640k,
-		 * the next section from 1mb->appropriate_mem_k
-		 */
-		unsigned long mem_size;
-
-		mem_size = (ALT_MEM_K < EXT_MEM_K) ? EXT_MEM_K : ALT_MEM_K;
-
-		add_memory_region(0, LOWMEMSIZE(), E820_RAM);
-		add_memory_region(HIGH_MEMORY, mem_size << 10, E820_RAM);
-  	}
-	printk("BIOS-provided physical RAM map:\n");
-	print_e820_map();
-} /* setup_memory_region */
-
-
-static inline void parse_mem_cmdline (char ** cmdline_p)
+static inline int setup_cmdline_memory (char ** cmdline_p)
 {
 	char c = ' ', *to = command_line, *from = COMMAND_LINE;
 	int len = 0;
@@ -509,10 +429,6 @@
 			if (!memcmp(from+4, "nopentium", 9)) {
 				from += 9+4;
 				boot_cpu_data.x86_capability &= ~X86_FEATURE_PSE;
-			} else if (!memcmp(from+4, "exactmap", 8)) {
-				from += 8+4;
-				e820.nr_map = 0;
-				usermem = 1;
 			} else {
 				/* If the user specifies memory size, we
 				 * blow away any automatically generated
@@ -520,23 +436,16 @@
 				 */
 				unsigned long start_at, mem_size;
  
-				if (usermem == 0) {
-					/* first time in: zap the whitelist
-					 * and reinitialize it with the
-					 * standard low-memory region.
-					 */
-					e820.nr_map = 0;
-					usermem = 1;
-					add_memory_region(0, LOWMEMSIZE(), E820_RAM);
-				}
+				usermem = 1;
 				mem_size = memparse(from+4, &from);
 				if (*from == '@')
 					start_at = memparse(from+1, &from);
-				else {
-					start_at = HIGH_MEMORY;
-					mem_size -= HIGH_MEMORY;
-					usermem=0;
+				else if (*from == '-') {
+					start_at = mem_size;
+					mem_size += memparse(from+1, &from);
 				}
+				else
+					start_at = 0;
 				add_memory_region(start_at, mem_size, E820_RAM);
 			}
 		}
@@ -550,9 +459,13 @@
 	*to = '\0';
 	*cmdline_p = command_line;
 	if (usermem) {
-		printk("user-defined physical RAM map:\n");
-		print_e820_map();
+		if (validate_regions()) {
+			printk("user-defined physical RAM map:\n");
+			print_region_map();
+			return 1;
+		}
 	}
+	return 0;
 }
 
 void __init setup_arch(char **cmdline_p)
@@ -582,7 +495,8 @@
 	rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0);
 	rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0);
 #endif
-	setup_memory_region();
+	if (!setup_cmdline_memory(cmdline_p))
+		setup_detected_memory();
 
 	if (!MOUNT_ROOT_RDONLY)
 		root_mountflags &= ~MS_RDONLY;
@@ -596,12 +510,6 @@
 	data_resource.start = virt_to_bus(&_etext);
 	data_resource.end = virt_to_bus(&_edata)-1;
 
-	parse_mem_cmdline(cmdline_p);
-
-#define PFN_UP(x)	(((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
-#define PFN_DOWN(x)	((x) >> PAGE_SHIFT)
-#define PFN_PHYS(x)	((x) << PAGE_SHIFT)
-
 /*
  * 128MB for vmalloc and initrd
  */
@@ -617,21 +525,16 @@
 	start_pfn = PFN_UP(__pa(&_end));
 
 	/*
-	 * Find the highest page frame number we have available
-	 */
-	max_pfn = 0;
-	for (i = 0; i < e820.nr_map; i++) {
-		unsigned long start, end;
-		/* RAM? */
-		if (e820.map[i].type != E820_RAM)
-			continue;
-		start = PFN_UP(e820.map[i].addr);
-		end = PFN_DOWN(e820.map[i].addr + e820.map[i].size);
-		if (start >= end)
-			continue;
-		if (end > max_pfn)
-			max_pfn = end;
-	}
+  	 * Find the highest page frame number we have available
+  	 */
+  	max_pfn = 0;
+ 	for (i = 0; i < physical_memory.nr_region; i++) {
+  		unsigned long start, end;
+		start = PFN_UP(Region[i].start);
+ 		end = PFN_DOWN(Region[i].end);
+  		if ( (start < end) && (end > max_pfn) )
+  			max_pfn = end;
+  	}
 
 	/*
 	 * Determine low and high memory ranges:
@@ -674,23 +577,19 @@
 	/*
 	 * Register fully available low RAM pages with the bootmem allocator.
 	 */
-	for (i = 0; i < e820.nr_map; i++) {
+	for (i = 0; i < physical_memory.nr_region; i++) {
 		unsigned long curr_pfn, last_pfn, size;
- 		/*
-		 * Reserve usable low memory
-		 */
-		if (e820.map[i].type != E820_RAM)
-			continue;
+
 		/*
 		 * We are rounding up the start address of usable memory:
 		 */
-		curr_pfn = PFN_UP(e820.map[i].addr);
+		curr_pfn = PFN_UP(Region[i].start);
 		if (curr_pfn >= max_low_pfn)
 			continue;
 		/*
 		 * ... and at the end of the usable range downwards:
 		 */
-		last_pfn = PFN_DOWN(e820.map[i].addr + e820.map[i].size);
+		last_pfn = PFN_DOWN(Region[i].end);
 
 		if (last_pfn > max_low_pfn)
 			last_pfn = max_low_pfn;
@@ -771,30 +670,37 @@
 	 * and also for regions reported as reserved by the e820.
 	 */
 	probe_roms();
-	for (i = 0; i < e820.nr_map; i++) {
+	for (i = 0; i < physical_memory.nr_region; i++) {
+		struct resource *res;
+		res = alloc_bootmem_low(sizeof(struct resource));
+		res->start = Region[i].start;
+		res->end   = Region[i].end-1;
+		res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+		res->name  = "System RAM";
+		request_resource(&iomem_resource, res);
+		/*
+		 *  We dont't know which RAM region contains the
+		 *  kernel data, so we try it repeatedly and let
+		 *  the resource manager test it.
+		 */
+		request_resource(res, &code_resource);
+		request_resource(res, &data_resource);
+	}
+	for (i = 0; i < physical_memory.nr_bios; i++) {
 		struct resource *res;
-		if (e820.map[i].addr + e820.map[i].size > 0x100000000ULL)
+		if (Bios[i].type == E820_RAM)
 			continue;
+
 		res = alloc_bootmem_low(sizeof(struct resource));
-		switch (e820.map[i].type) {
-		case E820_RAM:	res->name = "System RAM"; break;
+		switch (Bios[i].type) {
 		case E820_ACPI:	res->name = "ACPI Tables"; break;
 		case E820_NVS:	res->name = "ACPI Non-volatile Storage"; break;
 		default:	res->name = "reserved";
 		}
-		res->start = e820.map[i].addr;
-		res->end = res->start + e820.map[i].size - 1;
+		res->start = Bios[i].addr;
+		res->end = Bios[i].addr + Bios[i].size - 1;
 		res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
 		request_resource(&iomem_resource, res);
-		if (e820.map[i].type == E820_RAM) {
-			/*
-			 *  We dont't know which RAM region contains kernel data,
-			 *  so we try it repeatedly and let the resource manager
-			 *  test it.
-			 */
-			request_resource(res, &code_resource);
-			request_resource(res, &data_resource);
-		}
 	}
 	request_resource(&iomem_resource, &vram_resource);
 
diff -Naur linux-2.4.0orig/arch/i386/mm/init.c linux-2.4.0exp/arch/i386/mm/init.c
--- linux-2.4.0orig/arch/i386/mm/init.c	Wed May 31 12:01:19 2000
+++ linux-2.4.0exp/arch/i386/mm/init.c	Sat May 27 01:59:31 2000
@@ -538,21 +538,11 @@
 {
 	int i;
 
-	for (i = 0; i < e820.nr_map; i++) {
-		unsigned long addr, end;
-
-		if (e820.map[i].type != E820_RAM)	/* not usable memory */
-			continue;
-		/*
-		 *	!!!FIXME!!! Some BIOSen report areas as RAM that
-		 *	are not. Notably the 640->1Mb area. We need a sanity
-		 *	check here.
-		 */
-		addr = (e820.map[i].addr+PAGE_SIZE-1) >> PAGE_SHIFT;
-		end = (e820.map[i].addr+e820.map[i].size) >> PAGE_SHIFT;
-		if  ((pagenr >= addr) && (pagenr < end))
+	for (i = 0; i < physical_memory.nr_region; i++)
+		if (pagenr >= PFN_UP(physical_memory.region[i].start)
+		    && pagenr < PFN_DOWN(physical_memory.region[i].end) )
 			return 1;
-	}
+
 	return 0;
 }
 
diff -Naur linux-2.4.0orig/drivers/char/Config.in linux-2.4.0exp/drivers/char/Config.in
--- linux-2.4.0orig/drivers/char/Config.in	Sat May 27 01:58:50 2000
+++ linux-2.4.0exp/drivers/char/Config.in	Wed May 31 11:45:26 2000
@@ -220,7 +220,7 @@
    fi
    dep_tristate '  CPiA Video For Linux' CONFIG_VIDEO_CPIA $CONFIG_VIDEO_DEV
    if [ "$CONFIG_VIDEO_CPIA" != "n" ]; then
-     if [ "CONFIG_PARPORT_1284" != "n" ]; then
+     if [ "$CONFIG_PARPORT_1284" != "n" ]; then
        dep_tristate '    CPiA Parallel Port Lowlevel Support' CONFIG_VIDEO_CPIA_PP $CONFIG_VIDEO_CPIA $CONFIG_PARPORT
      fi
      if [ "$CONFIG_USB" != "n" ]; then
diff -Naur linux-2.4.0orig/drivers/ide/Config.in linux-2.4.0exp/drivers/ide/Config.in
--- linux-2.4.0orig/drivers/ide/Config.in	Wed May 31 12:01:47 2000
+++ linux-2.4.0exp/drivers/ide/Config.in	Wed May 31 11:42:40 2000
@@ -140,9 +140,9 @@
 fi
 
 if [ "$CONFIG_BLK_DEV_TIVO" = "y" ]; then
-  define CONFIG_DMA_NONPCI y
+  define_bool CONFIG_DMA_NONPCI y
 else
-  define CONFIG_DMA_NONPCI n
+  define_bool CONFIG_DMA_NONPCI n
 fi
 if [ "$CONFIG_IDE_CHIPSETS" = "y" -o \
      "$CONFIG_BLK_DEV_AEC62XX" = "y" -o \
diff -Naur linux-2.4.0orig/include/asm-i386/e820.h linux-2.4.0exp/include/asm-i386/e820.h
--- linux-2.4.0orig/include/asm-i386/e820.h	Wed May 31 12:01:19 2000
+++ linux-2.4.0exp/include/asm-i386/e820.h	Wed May 31 11:41:06 2000
@@ -5,16 +5,19 @@
  * In a nutshell, arch/i386/boot/setup.S populates a scratch table
  * in the empty_zero_block that contains a list of usable address/size
  * duples.   In arch/i386/kernel/setup.c, this information is
- * transferred into the e820map, and in arch/i386/mm/init.c, that
- * new information is used to mark pages reserved or not.
+ * transferred into the bios[], then converted into a list of valid
+ * memory regions in region[], and that new information is used in
+ * arch/i386/mm/init.c to mark pages available or not.
  *
  */
 #ifndef __E820_HEADER
 #define __E820_HEADER
 
-#define E820MAP	0x2d0		/* our map */
-#define E820MAX	32		/* number of entries in E820MAP */
-#define E820NR	0x1e8		/* # entries in E820MAP */
+#define E820MAP		0x2d0	/* our map */
+#define E820MAX		32	/* number of entries in E820MAP */
+#define E820PHYSMAX	64	/* number of entries in physical_memory.region */
+#define E820NR		0x1e8	/* # entries in E820MAP */
+#define E820LEN		20	/* size of an e820 entry */
 
 #define E820_RAM	1
 #define E820_RESERVED	2
@@ -23,18 +26,90 @@
 
 #define HIGH_MEMORY	(1024*1024)
 
+/* HACK: These macros map between page numbers and physical addresses.
+ * They used to be in arch/i386/kernel/setup.c, but have been moved here
+ * so that they can also be used in arch/i386/mm/init.c.
+ */
+#define PFN_UP(x)	(((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
+#define PFN_DOWN(x)	((x) >> PAGE_SHIFT)
+#define PFN_PHYS(x)	((x) << PAGE_SHIFT)
+
+/*
+ * FLAG_E820_DEBUG enables bunches of debug messages.
+ */
+#define FLAG_E820_DEBUG	1
+
+/* FLAG_E820_RECLAIM tells add_memory_region/check_memory_region to
+ * attempt to use E820 reclaim memory sections.  To have reclaim work,
+ * you need to configure it on, _plus_ CONFIG_ACPI needs to be turned off.
+ */
+#if defined(CONFIG_E820_RECLAIM) && !defined(CONFIG_ACPI)
+#  define FLAG_E820_RECLAIM	1
+#else
+#  define FLAG_E820_RECLAIM	0
+#endif
+
+/* FLAG_E820_4GB_ONLY tells the memory region adder to truncate memory
+ * segments to the first 4 gb of memory.
+ */
+#define	FLAG_E820_4GB_ONLY	1
+
+
 #ifndef __ASSEMBLY__
 
-struct e820map {
-    int nr_map;
-    struct {
-	long long addr;		/* start of memory segment */
-	long long size;		/* size of memory segment */
-	long type;		/* type of memory segment */
-    } map[E820MAX];
-};
+struct e820 {
+    __u64 addr;	/* addr,size,type are returned by e820 bios call */
+    __u64 size;
+    __u32 type;
+} __attribute__ ((__packed__));
+
+struct physical_region {
+    __u64 start;
+    __u64 end;
+} ;
+
+struct physical_memory {
+    int nr_bios;				/* bios[] holds memory region */
+    int nr_region;				/* region[] holds valid mem */
+    struct e820 bios[E820MAX];			/* returned by the bios */
+    struct physical_region region[E820PHYSMAX];	/* that we want to allocate */
+} ;
+
+#define NR_HOLES	10
+
+struct excluded_memory {
+    int nr_hole;
+    struct __hole {
+	__u64 start;
+	__u64 end;
+	__u32 mask;
+    } hole[NR_HOLES];
+} ;
 
-extern struct e820map e820;
-#endif/*!__ASSEMBLY__*/
+extern struct physical_memory physical_memory;
+
+/* Typing shortcuts.
+ */
+#define Region  physical_memory.region
+#define Bios    physical_memory.bios                                           
 
+/*
+ * setup functions
+ */
+enum region_flags { RF_RECLAIM =0x01,
+                    RF_NOT_RAM =0x02,
+		    RF_OVERFLOW=0x04,
+		    RF_ZERO    =0x08,
+		    RF_MERGED  =0x10,
+		    RF_ADDED   =0x20,
+		    RF_TOOLONG =0x80 } ;
+
+enum region_flags add_memory_region(__u64, __u64, __u32);
+int add_memory_hole(__u64, __u64);
+int validate_regions(void);
+void print_region_map(void);
+void setup_detected_memory(void);
+
+
+#endif/*!__ASSEMBLY__*/
 #endif/*__E820_HEADER*/
diff -Naur linux-2.4.0orig/scripts/Menuconfig linux-2.4.0exp/scripts/Menuconfig
--- linux-2.4.0orig/scripts/Menuconfig	Wed May 31 12:01:20 2000
+++ linux-2.4.0exp/scripts/Menuconfig	Sat May 27 01:59:31 2000
@@ -545,7 +545,7 @@
 
 			# Semantics of + and ? in GNU expr changed, so
 			# we avoid them:
-			if expr "$answer" : '0$\|-[1-9][0-9]*$\|[1-9][0-9]*$' >/dev/null
+			if expr "$answer" : '0$' '|' "$answer" : '[1-9][0-9]*$' '|' "$answer" : '-[1-9][0-9]*$' >/dev/null
 			then
 				eval $2="$answer"
 			else
