--- linux-2.3.15-orig/arch/i386/boot/setup.S	Thu Aug 19 10:54:13 1999
+++ linux-2.3.15/arch/i386/boot/setup.S	Thu Aug 26 12:50:59 1999
@@ -37,6 +37,7 @@
 #include <linux/version.h>
 #include <linux/compile.h>
 #include <asm/boot.h>
+#include <asm/e820.h>
 
 ! Signature words to ensure LILO loaded us right
 #define SIG1	0xAA55
@@ -59,7 +60,7 @@
 
 entry start
 start:
-	jmp	start_of_setup
+	jmp	trampoline
 ! ------------------------ start of header --------------------------------
 !
 ! SETUP-header, must start at CS:2 (old 0x9020:2)
@@ -119,6 +120,8 @@
 heap_end_ptr:	.word	modelist+1024	! space from here (exclusive) down to
 				! end of setup code can be used by setup
 				! for local heap purposes.
+trampoline:	call	start_of_setup
+		.space	1024
 ! ------------------------ end of header ----------------------------------
 
 start_of_setup:
@@ -245,37 +248,91 @@
 loader_ok:
 ! Get memory size (extended mem, kB)
 
+	xor	eax, eax
+	mov	dword ptr [0x1e0], eax
 #ifndef STANDARD_MEMORY_BIOS_CALL
-	push	ebx
 
-        xor     ebx,ebx		! preload new memory slot with 0k
-        mov	[0x1e0], ebx
+	mov	byte ptr [E820NR], al
 
-        mov     ax,#0xe801
-	int     0x15
-	jc      oldstylemem
+! 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].
+! 
+
+meme820:
+	mov	edx, #0x534d4150		! ascii `SMAP'
+	xor	ebx, ebx			! continuation counter
+
+	mov	di, #E820MAP			! point into the whitelist
+						! so we can have the bios
+						! directly write into it.
+
+jmpe820:
+	mov	eax, #0x0000e820		! e820, upper word zeroed
+	mov	ecx, #20			! size of the e820rec
+
+	push	ds				! data record.
+	pop	es
+	int	0x15				! make the call
+	jc	bail820				! fall to e801 if it fails
+
+	cmp	eax, #0x534d4150		! check the return is `SMAP'
+	jne	bail820				! fall to e801 if it fails
 
-! Memory size is in 1 k chunksizes, to avoid confusing loadlin.
-! We store the 0xe801 memory size in a completely different place,
+!	cmp	dword ptr [16+di], #1		! is this usable memory?
+!	jne	again820
+	
+	! If this is usable memory, we save it by simply advancing di by
+	! sizeof(e820rec).
+	!
+good820:
+	mov	al, byte ptr [E820NR]	! up to 32 good entries, that is
+	cmp	al, #E820MAX
+	jnl	bail820
+	inc	byte ptr [E820NR]
+	mov	ax, di
+	add	ax, #20
+	mov	di, ax
+
+again820:
+	cmp	ebx, #0			! check to see if ebx is
+	jne	jmpe820			! 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.)
 
-	and     ebx, #0xffff    ! clear sign extend
-	shl     ebx, 6          ! and go from 64k to 1k chunks
-	mov     [0x1e0],ebx     ! store extended memory size
-
-	and     eax, #0xffff    ! clear sign extend
- 	add     [0x1e0],eax     ! and add lower memory into total size.
-  
-	! and fall into the old memory detection code to populate the
-	! compatibility slot.
+meme801:
+
+        mov     ax,#0xe801
+	int     0x15
+	jc	mem88
+
+	and     edx, #0xffff    ! clear sign extend
+	shl     edx, 6          ! and go from 64k to 1k chunks
+	mov     [0x1e0],edx     ! store extended memory size
+
+	and     ecx, #0xffff    ! clear sign extend
+	add     [0x1e0],ecx     ! and add lower memory into total size.
+
+! Ye Olde Traditional Methode.  Returns the memory size (up to 16mb or
+! 64mb, depending on the bios) in ax.
+mem88:
 
-oldstylemem:
-	pop	ebx
-#else
-	mov     dword ptr [0x1e0], #0
 #endif
 	mov	ah,#0x88
 	int	0x15
--- linux-2.3.15-orig/arch/i386/kernel/setup.c	Wed Aug 25 15:49:00 1999
+++ linux-2.3.15/arch/i386/kernel/setup.c	Fri Aug 27 10:43:21 1999
@@ -17,6 +17,9 @@
  *
  *  IDT Winchip tweaks, misc clean ups.
  *	Dave Jones <dave@powertweak.com>, August 1999
+ *
+ *  Memory region support
+ *	David Parsons <orc@pell.chi.il.us>, July-August 1999
  */
 
 /*
@@ -51,6 +54,7 @@
 #include <asm/cobalt.h>
 #include <asm/msr.h>
 #include <asm/desc.h>
+#include <asm/e820.h>
 
 /*
  * Machine setup..
@@ -84,6 +88,8 @@
 	unsigned char table[0];
 };
 
+struct e820map e820 = { 0 };
+
 unsigned char aux_device_present;
 
 #ifdef CONFIG_BLK_DEV_RAM
@@ -103,6 +109,8 @@
 #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))
@@ -345,12 +353,80 @@
 	}
 }
 
+__initfunc(unsigned long memparse(char *ptr, char **retptr))
+{
+	unsigned long ret;
+
+	ret = simple_strtoul(ptr, retptr, 0);
+
+	if (**retptr == 'K' || **retptr == 'k') {
+		ret <<= 10;
+		(*retptr)++;
+	}
+	else if (**retptr == 'M' || **retptr == 'm') {
+		ret <<= 20;
+		(*retptr)++;
+	}
+	return ret;
+} /* memparse */
+
+
+__initfunc(void 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 LOWMEMSIZE()	((*(unsigned short *)__va(0x413)) * 1024)
+
+
+__initfunc(void 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.
+	 */
+	if (E820_MAP_NR) {
+		/* 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(), 1);
+		add_memory_region(HIGH_MEMORY, mem_size << 10, 1);
+  	}
+} /* setup_memory_region */
+
+
 void __init setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p)
 {
 	unsigned long memory_start, memory_end;
 	char c = ' ', *to = command_line, *from = COMMAND_LINE;
 	int len = 0;
 	int i;
+	int usermem=0;
 
 #ifdef CONFIG_VISWS
 	visws_get_board_type_and_rev();
@@ -367,24 +443,14 @@
 		BIOS_revision = SYS_DESC_TABLE.table[2];
 	}
 	aux_device_present = AUX_DEVICE_INFO;
-	memory_end = (1<<20) + (EXT_MEM_K<<10);
-#ifndef STANDARD_MEMORY_BIOS_CALL
-	{
-		unsigned long memory_alt_end = (1<<20) + (ALT_MEM_K<<10);
-		/* printk(KERN_DEBUG "Memory sizing: %08x %08x\n", memory_end, memory_alt_end); */
-		if (memory_alt_end > memory_end)
-			memory_end = memory_alt_end;
-	}
-#endif
-
-	ram_resources[1].end = memory_end-1;
 
-	memory_end &= PAGE_MASK;
 #ifdef CONFIG_BLK_DEV_RAM
 	rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK;
 	rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0);
 	rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0);
 #endif
+	setup_memory_region();
+
 	if (!MOUNT_ROOT_RDONLY)
 		root_mountflags &= ~MS_RDONLY;
 	memory_start = (unsigned long) &_end;
@@ -405,8 +471,10 @@
 	for (;;) {
 		/*
 		 * "mem=nopentium" disables the 4MB page tables.
-		 * "mem=XXX[kKmM]" overrides the BIOS-reported
-		 * memory size
+		 * "mem=XXX[kKmM]" defines a memory region from HIGH_MEM
+		 * to <mem>, overriding the bios size.
+		 * "mem=XXX[KkmM]@XXX[KkmM]" defines a memory region from
+		 * <start> to <start>+<mem>, overriding the bios size.
 		 */
 		if (c == ' ' && *(const unsigned long *)from == *(const unsigned long *)"mem=") {
 			if (to != command_line) to--;
@@ -414,16 +482,29 @@
 				from += 9+4;
 				boot_cpu_data.x86_capability &= ~X86_FEATURE_PSE;
 			} else {
-				memory_end = simple_strtoul(from+4, &from, 0);
-				if ( *from == 'K' || *from == 'k' ) {
-					memory_end = memory_end << 10;
-					from++;
-				} else if ( *from == 'M' || *from == 'm' ) {
-					memory_end = memory_end << 20;
-					from++;
+				/* If the user specifies memory size, we
+				 * blow away any automatically generated
+				 * size
+				 */
+				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(), 1);
 				}
-				if (memory_end > ram_resources[1].end)
-					ram_resources[1].end = memory_end-1;
+				mem_size = memparse(from+4, &from);
+				if (*from == '@')
+					start_at = memparse(from+1,&from);
+				else {
+					start_at = HIGH_MEMORY;
+					mem_size -= HIGH_MEMORY;
+				}
+				add_memory_region(start_at, mem_size, 1);
 			}
 		}
 		c = *(from++);
@@ -436,23 +517,33 @@
 	*to = '\0';
 	*cmdline_p = command_line;
 
-	/* Request the standard RAM and ROM resources - they eat up PCI memory space */
-	request_resource(&iomem_resource, ram_resources+0);
-	request_resource(&iomem_resource, ram_resources+1);
-	request_resource(&iomem_resource, ram_resources+2);
-	request_resource(ram_resources+1, &code_resource);
-	request_resource(ram_resources+1, &data_resource);
-	probe_roms();
 
 #define VMALLOC_RESERVE	(128 << 20)	/* 128MB for vmalloc and initrd */
 #define MAXMEM	((unsigned long)(-PAGE_OFFSET-VMALLOC_RESERVE))
 
+	for (memory_end=i=0; i < e820.nr_map; i++)
+	    if ((e820.map[i].type == 1) && ((unsigned long)(e820.map[i].addr + e820.map[i].size) > memory_end))
+		memory_end = (unsigned long)(e820.map[i].addr + e820.map[i].size);
+
 	if (memory_end > MAXMEM)
 	{
 		memory_end = MAXMEM;
 		printk(KERN_WARNING "Warning only %ldMB will be used.\n",
 			MAXMEM>>20);
 	}
+	memory_end &= PAGE_MASK;
+
+	ram_resources[1].end = memory_end-1;
+
+	/* Request the standard RAM and ROM resources -
+	 * they eat up PCI memory space
+	 */
+	request_resource(&iomem_resource, ram_resources+0);
+	request_resource(&iomem_resource, ram_resources+1);
+	request_resource(&iomem_resource, ram_resources+2);
+	request_resource(ram_resources+1, &code_resource);
+	request_resource(ram_resources+1, &data_resource);
+	probe_roms();
 
 	memory_end += PAGE_OFFSET;
 	*memory_start_p = memory_start;
--- linux-2.3.15-orig/arch/i386/mm/init.c	Wed Aug 25 14:54:07 1999
+++ linux-2.3.15/arch/i386/mm/init.c	Fri Aug 27 10:42:48 1999
@@ -27,6 +27,7 @@
 #include <asm/pgtable.h>
 #include <asm/dma.h>
 #include <asm/fixmap.h>
+#include <asm/e820.h>
 
 static unsigned long totalram = 0;
 
@@ -361,7 +362,8 @@
 	int datapages = 0;
 	int initpages = 0;
 	unsigned long tmp;
-	unsigned long endbase;
+	unsigned long addr;
+	int i, avail;
 
 	end_mem &= PAGE_MASK;
 	high_memory = (void *) end_mem;
@@ -385,23 +387,43 @@
 #endif
 	start_mem = PAGE_ALIGN(start_mem);
 
-	/*
-	 * IBM messed up *AGAIN* in their thinkpad: 0xA0000 -> 0x9F000.
-	 * They seem to have done something stupid with the floppy
-	 * controller as well..
-	 * The amount of available base memory is in WORD 40:13. Except
-	 * when it isn't.
+	/* walk the whitelist, unreserving good memory
 	 */
-	endbase = PAGE_OFFSET + 0x9f000;
-	while (start_low_mem < endbase) {
-		clear_bit(PG_reserved, &mem_map[MAP_NR(start_low_mem)].flags);
-		start_low_mem += PAGE_SIZE;
-	}
+	for (avail = i = 0; i < e820.nr_map; i++) {
+		if (e820.map[i].type != 1)	/* not usable memory */
+			continue;
+		printk("memory region: %luk @ %08lx\n",
+				    ((long)(e820.map[i].size)) / 1024,
+				    (long)(e820.map[i].addr) );
+		for (addr=PAGE_ALIGN(((long)(e820.map[i].addr)))+PAGE_OFFSET,
+								     tmp = 0;
+				     tmp < (unsigned long)(e820.map[i].size);
+							    tmp += PAGE_SIZE,
+							   addr += PAGE_SIZE) {
+
+			/* this little bit of grossness is for dealing
+			 * with memory borrowing for system bookkeeping
+			 * (smp stacks, zero page, kernel code, etc)
+			 * without having to go back and edit the e820
+			 * map to compensate.
+			 *
+			 * if we're in low memory (<1024k), we need to
+			 * avoid the smp stack and zero page.
+			 * if we're in high memory, we need to avoid
+			 * the kernel code.
+			 * in any case, we don't want to hack mem_map
+			 * entries above end_mem.
+			 */
+			if ( (addr < start_low_mem)
+			  || (addr >= HIGH_MEMORY && addr <= start_mem)
+			  || (addr > end_mem) )
+				continue;
 
-	while (start_mem < end_mem) {
-		clear_bit(PG_reserved, &mem_map[MAP_NR(start_mem)].flags);
-		start_mem += PAGE_SIZE;
+			avail++;
+			clear_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags);
+		}
 	}
+
 	for (tmp = PAGE_OFFSET ; tmp < end_mem ; tmp += PAGE_SIZE) {
 		if (tmp >= MAX_DMA_ADDRESS)
 			clear_bit(PG_DMA, &mem_map[MAP_NR(tmp)].flags);
@@ -424,8 +446,7 @@
 		set_page_count(mem_map+MAP_NR(tmp), 1);
 		totalram += PAGE_SIZE;
 #ifdef CONFIG_BLK_DEV_INITRD
-		if (!initrd_start || (tmp < initrd_start || tmp >=
-		    initrd_end))
+		if (!initrd_start || (tmp < initrd_start || tmp >= initrd_end))
 #endif
 			free_page(tmp);
 	}
@@ -439,6 +460,7 @@
 
 	if (boot_cpu_data.wp_works_ok < 0)
 		test_wp_bit();
+
 }
 
 void free_initmem(void)
diff -Naur linux-2.3.15-orig/Documentation/i386/zero-page.txt linux-2.3.15/Documentation/i386/zero-page.txt
--- linux-2.3.15-orig/Documentation/i386/zero-page.txt	Mon Oct  5 12:59:25 1998
+++ linux-2.3.15/Documentation/i386/zero-page.txt	Thu Aug 26 13:04:10 1999
@@ -30,6 +30,7 @@
  0xb0 - 0x1df		Free. Add more parameters here if you really need them.
 
 0x1e0	unsigned long	ALT_MEM_K, alternative mem check, in Kb
+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)
 0x1f4	unsigned short	size of compressed kernel-part in the
@@ -64,7 +65,7 @@
 0x21c	unsigned long	INITRD_SIZE, size in bytes of ramdisk image
 0x220	4 bytes		(setup.S)
 0x224	unsigned short	setup.S heap end pointer
-0x226 - 0x7ff		setup.S code.
+0x2d0 - 0x600		E820MAP
 
 0x800	string, 2K max	COMMAND_LINE, the kernel commandline as
 			copied using CL_OFFSET.
diff -Naur linux-2.3.15-orig/include/asm-i386/e820.h linux-2.3.15/include/asm-i386/e820.h
--- linux-2.3.15-orig/include/asm-i386/e820.h	Wed Dec 31 16:00:00 1969
+++ linux-2.3.15/include/asm-i386/e820.h	Thu Aug 26 12:51:01 1999
@@ -0,0 +1,35 @@
+/*
+ * structures and definitions for the int 15, ax=e820 memory map
+ * scheme.
+ *
+ * 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.
+ *
+ */
+#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 HIGH_MEMORY	(1024*1024)
+
+#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];
+};
+
+extern struct e820map e820;
+#endif/*!__ASSEMBLY__*/
+
+#endif/*__E820_HEADER*/
