[PATCH] 2.3.15 fancy memory detection patch, reworked

david parsons (o.r.c@p.e.l.l.p.o.r.t.l.a.n.d.o.r.u.s)
25 Aug 1999 08:49:40 -0700


This is the latest and greatest of my fancy memory detection patch,
reworked after review by Linux (``I hate this patch'' :-) It uses
the whitelist as the only method of setting up memory, and tweaks
the old style detection methods, plus mem=, into fake whitelists
instead of the old method.

It performs the critical task of reducing ia8 assembly code, plus as
a little something extra actually appears to run on the one machine
I've tested it on (I'm moving my office to make room for spawn, so
the machine farm is currently 20 feet and one floor from the nearest
power). Could anyone who wants a thrill try it out and let me know
how it does before I resubmit it to Linus?

____
david parsons \bi/ One machine isn't the typical 20, alas.
\/

--- linux-2.3.15-orig/arch/i386/boot/setup.S Wed Aug 25 00:04:21 1999
+++ linux-2.3.15/arch/i386/boot/setup.S Wed Aug 25 08:03:09 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 00:04:21 1999
+++ linux-2.3.15/arch/i386/kernel/setup.c Wed Aug 25 07:59:03 1999
@@ -51,6 +51,7 @@
#include <asm/cobalt.h>
#include <asm/msr.h>
#include <asm/desc.h>
+#include <asm/e820.h>

/*
* Machine setup..
@@ -84,6 +85,8 @@
unsigned char table[0];
};

+struct e820map e820 = { 0 };
+
unsigned char aux_device_present;

#ifdef CONFIG_BLK_DEV_RAM
@@ -103,6 +106,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 +350,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("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 +440,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 +468,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 +479,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);
+ }
+ mem_size = memparse(from+4, &from);
+ if (*from == '@')
+ start_at = memparse(from+1,&from);
+ else {
+ start_at = HIGH_MEMORY;
+ mem_size -= HIGH_MEMORY;
}
- if (memory_end > ram_resources[1].end)
- ram_resources[1].end = memory_end-1;
+ add_memory_region(start_at, mem_size, 1);
}
}
c = *(from++);
@@ -436,6 +514,9 @@
*to = '\0';
*cmdline_p = command_line;

+ 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);
@@ -446,6 +527,12 @@

#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].addr + e820.map[i].size > memory_end)
+ memory_end = e820.map[i].addr + e820.map[i].size;
+
+ memory_end &= PAGE_MASK;

if (memory_end > MAXMEM)
{
--- linux-2.3.15-orig/arch/i386/mm/init.c Wed Aug 4 22:53:14 1999
+++ linux-2.3.15/arch/i386/mm/init.c Wed Aug 25 00:09:51 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;

end_mem &= PAGE_MASK;
high_memory = (void *) end_mem;
@@ -385,22 +387,42 @@
#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.
+ /* walk the whitelist, unreserving good memory
*/
- endbase = PAGE_OFFSET + ((*(unsigned short *)__va(0x413) * 1024) & PAGE_MASK);
- while (start_low_mem < endbase) {
- clear_bit(PG_reserved, &mem_map[MAP_NR(start_low_mem)].flags);
- start_low_mem += PAGE_SIZE;
- }
+ for (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 < (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;
+ 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);
@@ -423,8 +445,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);
}
@@ -438,6 +459,7 @@

if (boot_cpu_data.wp_works_ok < 0)
test_wp_bit();
+
}

void free_initmem(void)
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 Wed Aug 25 00:09:51 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*/
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 Wed Aug 25 00:09:50 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 - 0x550 E820MAP

0x800 string, 2K max COMMAND_LINE, the kernel commandline as
copied using CL_OFFSET.

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/