Re: [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)
27 Aug 1999 10:55:44 -0700


In article <linux.kernel.7q13ak$c4k@pell.pell.portland.or.us>,
david parsons <o.r.c@p.e.l.l.p.o.r.t.l.a.n.d.o.r.u.s> wrote:
>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.

Whoops. This patch was against 2.3.15pre3, which differs from
2.3.15 in one small detail (an odd addition to a comment in
the middle of the memory setup code in mm/init.c). Here's a
slightly newer one that works on two machine I have at my
disposal (a laptop I use at work, and the workstation I have
at home); If you'd like less of a thrill, try it out:

____
david parsons \bi/ Kernel panics can be very thrilling, doncha know.
\/

--- 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*/

-
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/