Re: [PATCH] fancy memory detection for 2.3.14 on ia32

david parsons (o.r.c@p.e.l.l.p.o.r.t.l.a.n.d.o.r.u.s)
19 Aug 1999 09:43:37 -0700


In article <linux.kernel.7phb4r$gmn@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:

[nothing]

Okay, let me try again, this time with a message body:

This is my fancy memory detection patch, ported from 2.3.13 to 2.3.14
(not much of a port; the 2.3.13 patch applies without any complaints
on 2.3.13.) For those who are unaware, this patch uses, if available,
bios call E820 to return a memory map which the kernel then uses to paint
around memory holes.

I'm pretty certain this patch works. A version of it for kernel 2.0.28
is being used on 30 or so machines running Mastodon without any hilarious
sideeffects, and I've poked the bioses on all of my modern machines to
include memory holes.

diff -Naur linux-2.3.14-orig/arch/i386/boot/setup.S linux-2.3.14/arch/i386/boot/setup.S
--- linux-2.3.14-orig/arch/i386/boot/setup.S Mon Jul 5 20:04:47 1999
+++ linux-2.3.14/arch/i386/boot/setup.S Wed Aug 18 23:54:04 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,100 @@
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 dword ptr [E820MEM], eax
+ mov dword ptr [E820NR], eax

- 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 available memory areas, which we return at [0xC20].
+! We also make a stab at the memory size by the somewhat gross approach
+! of setting memsize to the address+size of the LAST available memory
+! we picked up. This assumes that e820 returns memory regions in strictly
+! increasing order; if a bios returns those memory regions in any other
+! order, we will be in for a world of pain.
+!
+
+meme820:
+ mov edx, #0x534d4150 ! ascii `SMAP'
+ xor ebx, ebx ! continuation counter
+
+jmpe820:
+ mov eax, #0x0000e820 ! e820, upper word zeroed
+ mov ecx, #20 ! size of the e820rec
+
+ mov dword ptr [E820+16], #42
+ mov di, #E820 ! es:di points at the
+ push ds ! data record.
+ pop es
+ int 0x15 ! make the call
+ jc meme801 ! fall to e801 if it fails
+
+ cmp eax, #0x534d4150 ! check the return is `SMAP'
+ jne meme801 ! fall to e801 if it fails
+
+ cmp dword ptr [E820+16], #1 ! is this usable memory?
+ jne again820
+
+ ! usable memory: add it to the whitelist
+ !
+good820:
+ mov al, byte ptr [E820NR]
+ cmp al, #E820MAX
+ jnl again820
+ inc byte ptr [E820NR]
+ and ax, #0xff ! clear sign extend
+ shl ax, 3 ! double dword alignment
+ mov si, ax
+ mov ecx, dword ptr [E820+8] ! pick up low dword of memsize
+ mov eax, dword ptr [E820+0] ! pick up low dword of address
+ mov E820MAP(si), eax ! store address in whitelist
+ mov E820MAP+4(si), ecx ! store size in whitelist
+
+ add eax, ecx ! set end_mem to address + memsize
+ mov [E820MEM], eax
+
+again820:
+ cmp ebx, #0 ! check to see if ebx is
+ jne jmpe820 ! set to EOF
+

-! Memory size is in 1 k chunksizes, to avoid confusing loadlin.
-! We store the 0xe801 memory size in a completely different place,
+
+! 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:

-oldstylemem:
- pop ebx
-#else
- mov dword ptr [0x1e0], #0
+ 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:
+
#endif
mov ah,#0x88
int 0x15
diff -Naur linux-2.3.14-orig/arch/i386/kernel/setup.c linux-2.3.14/arch/i386/kernel/setup.c
--- linux-2.3.14-orig/arch/i386/kernel/setup.c Wed Aug 18 11:25:12 1999
+++ linux-2.3.14/arch/i386/kernel/setup.c Wed Aug 18 23:54:04 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,9 @@
#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_MEM (*(unsigned long *) (PARAM+E820MEM))
+#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))
@@ -371,9 +377,27 @@
#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); */
+ unsigned long *mem = E820_MAP;
+
if (memory_alt_end > memory_end)
memory_end = memory_alt_end;
+
+ if (E820_MEM > memory_end)
+ memory_end = E820_MEM;
+
+ if ((e820.nr_map = E820_MAP_NR) > 0) {
+ int x;
+
+ if (e820.nr_map > E820MAX)
+ e820.nr_map = 0;
+ else for (x = 0; x < e820.nr_map; x++) {
+ e820.map[x].addr = *(mem++);
+ e820.map[x].size = *(mem++);
+ /*printk("E820: region %d, %lu bytes @ %08lx\n",
+ x, e820.map[x].size, e820.map[x].addr);*/
+ }
+ }
+
}
#endif

diff -Naur linux-2.3.14-orig/arch/i386/mm/init.c linux-2.3.14/arch/i386/mm/init.c
--- linux-2.3.14-orig/arch/i386/mm/init.c Wed Aug 4 22:53:14 1999
+++ linux-2.3.14/arch/i386/mm/init.c Wed Aug 18 23:54:04 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;

@@ -363,6 +364,19 @@
unsigned long tmp;
unsigned long endbase;

+ unsigned long start_seg = 0;
+ unsigned long size_seg = ~0;
+ int nr_map = 0;
+
+ if (nr_map < e820.nr_map) {
+ start_seg = e820.map[nr_map].addr + PAGE_OFFSET;
+ size_seg = e820.map[nr_map].size;
+ printk("region %d: %lu bytes @ %08lx\n",
+ nr_map, size_seg, start_seg - PAGE_OFFSET);
+ nr_map++;
+ }
+
+
end_mem &= PAGE_MASK;
high_memory = (void *) end_mem;
max_mapnr = num_physpages = MAP_NR(end_mem);
@@ -398,7 +412,23 @@
}

while (start_mem < end_mem) {
- clear_bit(PG_reserved, &mem_map[MAP_NR(start_mem)].flags);
+ if (start_mem > start_seg) {
+ if (start_mem < start_seg + size_seg)
+ clear_bit(PG_reserved,
+ &mem_map[MAP_NR(start_mem)].flags);
+ else if (nr_map < e820.nr_map) {
+ start_seg = e820.map[nr_map].addr + PAGE_OFFSET;
+ size_seg = e820.map[nr_map].size;
+ printk("region %d: %lu bytes @ %08lx\n",
+ nr_map, size_seg, start_seg - PAGE_OFFSET);
+ nr_map++;
+ continue; /* be paranoid in case the */
+ /* e820 code hasn't collapsed */
+ /* adjacent usable memory */
+ }
+ else
+ break; /* no more valid memory */
+ }
start_mem += PAGE_SIZE;
}
for (tmp = PAGE_OFFSET ; tmp < end_mem ; tmp += PAGE_SIZE) {
diff -Naur linux-2.3.14-orig/include/asm-i386/e820.h linux-2.3.14/include/asm-i386/e820.h
--- linux-2.3.14-orig/include/asm-i386/e820.h Wed Dec 31 16:00:00 1969
+++ linux-2.3.14/include/asm-i386/e820.h Wed Aug 18 23:54:04 1999
@@ -0,0 +1,34 @@
+/*
+ * 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 E820 0x2d0 /* scratch area */
+#define E820MAP 0x300 /* our map */
+#define E820MAX 32 /* number of entries in E820MAP */
+#define E820MEM 0x1e4 /* memtop from e820 calls */
+#define E820NR 0x1e8 /* # entries in E820MAP */
+
+#ifndef __ASSEMBLY__
+
+struct e820map {
+ int nr_map;
+ struct {
+ unsigned long addr;
+ unsigned long size;
+ } map[E820MAX];
+};
+
+extern struct e820map e820;
+#endif/*!__ASSEMBLY__*/
+
+#endif/*__E820_HEADER*/

____
david parsons \bi/ Now to figure out how to make ISA video cards get along
\/ with this memory hole. First I need to get an ISA video
card....

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