Anyone running 2.0.28(or later?) who wants to test some memory code?

david parsons (o.r.c@p.e.l.l.p.o.r.t.l.a.n.d.o.r.u.s)
15 Jun 1999 00:26:12 -0700


I've been tweaking my new.new.mem memory detection code so that it will
pay a little more attention to the gory details of what bios 15, E820
does, and I'd like to have some people let the smoke out of their machines
for the glory of more byzantine code. This is a 2.0.28 patch; a 2.2.x
patch will follow as soon as I set up a machine to do real work with a
2.2.x kernel.

What this code does is pays attention to memory holes, by having setup.S
return a list of the first 32 memory areas that E820 returns, and by
having init.c use that list to reserve pages that don't fall into valid
memory areas (like the infamous 15mb memory hole that some ISA video
cards really want.) As it stands, it's a fairly small patch and it
doesn't blow up on the first 10 machines I've thrown it at, but I'm
certainly not going to submit it to Linux until it works on more than my
pile of development machines.

____
david parsons \bi/ It's 2.0.28 because that's what my development machine
\/ is running, memory holes and all.

------[cut here]---------
--- linux-2.0.28-orig/arch/i386/boot/setup.S Sat Mar 30 10:58:57 1996
+++ linux/arch/i386/boot/setup.S Mon Jun 14 22:36:08 1999
@@ -26,6 +26,10 @@
!
! Video handling moved to video.S by Martin Mares, March 1996
! <mj@k332.feld.cvut.cz>
+!
+! Extended memory detection scheme retwiddled by orc@pell.chi.il.us (david
+! parsons) to avoid loadlin confusion, July 1997
+!

! NOTE! These had better be the same as in bootsect.s!
#define __ASSEMBLY__
@@ -232,6 +236,105 @@
loader_ok:
! Get memory size (extended mem, kB)

+#ifndef STANDARD_MEMORY_BIOS_CALL
+ xor ecx,ecx ! use `configured memory', and pre-zero
+ xor edx,edx ! the registers in case the bios lies
+ mov [0x1e0], ecx ! to us about the call being supported.
+ mov [0x1e4], ecx
+ mov [0x1e8], ecx
+ movb [0x220], dh ! zero e820 memory detect list
+
+! 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 [0x220]
+! (byte [0x220] is the number of areas found, long [0x224] is a list
+! of address32/size32 duples.
+! 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
+ lea di, e820rec ! es:di points at the
+ ! data record.
+jmpe820:
+ mov eax, #0x0000e820 ! e820, upper word zeroed
+ mov ecx, #20 ! size of the e820rec
+
+ 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 e820rec+0x10, #1 ! is this usable memory?
+ jne again820
+
+ok820:
+ ! usable memory: add it to the whitelist
+ !
+ movb al, [0x220]
+ cmp al, #0x19
+ je again820
+ inc al
+ mov [0x220], al
+ and ax, #0xff ! clear sign extend
+ shl ax, 3 ! double dword alignment
+ mov si, ax
+ mov ecx, e820rec+8 ! pick up low dword of memsize
+ mov eax, e820rec+0 ! pick up low dword of address
+ mov 0x21c(si), eax ! store address in whitelist
+ mov 0x220(si), ecx ! store size in whitelist
+
+ mov ecx, e820rec+0+4
+ add eax, e820rec+8
+ adc ecx, e820rec+8+4
+ mov [0x1e4], eax ! set the e820 memsize to the end of
+ mov [0x1e8], ecx ! this memory block
+
+again820:
+ cmp ebx, #0 ! check to see if ebx is
+ jne jmpe820 ! set to EOF
+
+
+
+! 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.)
+
+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:
+
+#endif
mov ah,#0x88
int 0x15
mov [2],ax
@@ -700,6 +803,13 @@
gdt_48:
.word 0x800 ! gdt limit=2048, 256 GDT entries
.word 512+gdt,0x9 ! gdt base = 0X9xxxx
+
+! 0xe820 memory map block
+!
+e820rec:
+ .word 0,0,0,0 ! base address
+ .word 0,0,0,0 ! length in bytes
+ .word 0,0 ! type of address range

!
! Include video setup & detection code
--- linux-2.0.28-orig/arch/i386/kernel/setup.c Fri Sep 20 07:00:34 1996
+++ linux/arch/i386/kernel/setup.c Mon Jun 14 22:39:46 1999
@@ -82,6 +82,10 @@
*/
#define PARAM empty_zero_page
#define EXT_MEM_K (*(unsigned short *) (PARAM+2))
+#ifndef STANDARD_MEMORY_BIOS_CALL
+#define E801_MEM_K (*(unsigned long *) (PARAM+0x1e0))
+#define E820_MEM (*(unsigned long *) (PARAM+0x1e4))
+#endif
#ifdef CONFIG_APM
#define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+64))
#endif
@@ -95,6 +99,14 @@
#define KERNEL_START (*(unsigned long *) (PARAM+0x214))
#define INITRD_START (*(unsigned long *) (PARAM+0x218))
#define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c))
+#define MEMORY_LIST (*(unsigned char*) (PARAM+0x220))
+/* memory E820:
+ * PARAM+0x220 -> PARAM+0x2a0 are eaten by the memory list:
+ * (byte) PARAM+0x220 is the number of entries in the memory list,
+ * (long*) PARAM+0x224 is the memory list proper.
+ *
+ * look in arch/i386/mm/init.c for the code that actually uses this.
+ */
#define COMMAND_LINE ((char *) (PARAM+2048))
#define COMMAND_LINE_SIZE 256

@@ -109,6 +121,7 @@
unsigned long * memory_start_p, unsigned long * memory_end_p)
{
unsigned long memory_start, memory_end;
+ unsigned long memory_e801_end;
char c = ' ', *to = command_line, *from = COMMAND_LINE;
int len = 0;
static unsigned char smptrap=0;
@@ -127,6 +140,20 @@
#endif
aux_device_present = AUX_DEVICE_INFO;
memory_end = (1<<20) + (EXT_MEM_K<<10);
+#ifndef STANDARD_MEMORY_BIOS_CALL
+ memory_e801_end = (1<<20) + (E801_MEM_K<<10);
+
+#ifdef VERBOSITY_SAYS_IT_ALL
+ printk("Memory: 088: %12ld\n", memory_end);
+ printk("Memory: e801: %12ld\n", memory_e801_end);
+ printk("Memory: e820: %12ld\n", E820_MEM);
+#endif
+ if ((memory_e801_end > memory_end) || (E820_MEM > memory_end))
+ if (memory_e801_end > E820_MEM)
+ memory_end = memory_e801_end;
+ else
+ memory_end = E820_MEM;
+#endif
memory_end &= PAGE_MASK;
#ifdef CONFIG_BLK_DEV_RAM
rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK;
--- linux-2.0.28-orig/arch/i386/mm/init.c Thu Nov 14 05:20:09 1996
+++ linux/arch/i386/mm/init.c Mon Jun 14 23:15:15 1999
@@ -204,6 +204,7 @@
return free_area_init(start_mem, end_mem);
}

+#define CONFIG_FANCY_MEMORY
void mem_init(unsigned long start_mem, unsigned long end_mem)
{
unsigned long start_low_mem = PAGE_SIZE;
@@ -212,13 +213,31 @@
int datapages = 0;
unsigned long tmp;
extern int _etext;
+#ifdef CONFIG_FANCY_MEMORY
+ unsigned long *mem;
+ unsigned int nr_mem; /* ((char*)zero_page)[0x220] */
+ unsigned long mem_start = ~0;
+ unsigned long mem_size = 0;
+
+ nr_mem = ((char*)empty_zero_page)[0x220];
+ mem = (long*)(((char*)empty_zero_page)+0x224);
+
+ if (nr_mem-- > 0) {
+ /* grab the first memory segment */
+ mem_start = *(mem++);
+ mem_size = *(mem++);
+ printk("mm: %ldK at %08lx\n", mem_size>>10, mem_start);
+ }
+ else {
+ /* no memory map, so all memory is good */
+ mem_start = 0;
+ mem_size = ~0;
+ }
+#endif

end_mem &= PAGE_MASK;
high_memory = end_mem;

- /* clear the zero-page */
- memset(empty_zero_page, 0, PAGE_SIZE);
-
/* mark usable pages in the mem_map[] */
start_low_mem = PAGE_ALIGN(start_low_mem);

@@ -241,15 +260,39 @@
start_low_mem += PAGE_SIZE;
}

- while (start_mem < high_memory) {
+ for( ; start_mem < high_memory; start_mem += PAGE_SIZE) {
+#ifdef CONFIG_FANCY_MEMORY
+ again:
+ if (start_mem > mem_start)
+ if (start_mem < mem_start+mem_size) {
+ clear_bit(PG_reserved, &mem_map[MAP_NR(start_mem)].flags);
+ continue; /* good memory heah */
+ }
+ else if (nr_mem-- > 0) {
+ mem_start = *(mem++);
+ mem_size = *(mem++);
+ printk("mm: %ldK at %08lx\n", mem_size>>10, mem_start);
+ /* check again in case there are two memory
+ * regions adjacent to each other
+ */
+ goto again;
+ }
+ /*set_bit(PG_error, &mem_map[MAP_NR(start_mem)].flags);*/
+ set_bit(PG_reserved, &mem_map[MAP_NR(start_mem)].flags);
+#else
clear_bit(PG_reserved, &mem_map[MAP_NR(start_mem)].flags);
- start_mem += PAGE_SIZE;
+#endif
}
+
+ /* clear the zero-page */
+ memset(empty_zero_page, 0, PAGE_SIZE);
+
+
for (tmp = 0 ; tmp < high_memory ; tmp += PAGE_SIZE) {
if (tmp >= MAX_DMA_ADDRESS)
clear_bit(PG_DMA, &mem_map[MAP_NR(tmp)].flags);
if (PageReserved(mem_map+MAP_NR(tmp))) {
- if (tmp >= 0xA0000 && tmp < 0x100000)
+ if ((tmp >= 0xA0000 && tmp < 0x100000) || PageReserved(mem_map+MAP_NR(tmp)))
reservedpages++;
else if (tmp < (unsigned long) &_etext)
codepages++;
--- linux-2.0.28-orig/mm/vmalloc.c Mon Aug 5 00:13:55 1996
+++ linux/mm/vmalloc.c Mon Jun 14 23:16:32 1999
@@ -307,7 +307,8 @@
struct vm_struct * area;

if (MAP_NR(offset) < MAP_NR(high_memory))
- return NULL;
+ if (PageReserved(mem_map+MAP_NR(offset)) == 0)
+ return NULL;
if (offset & ~PAGE_MASK)
return NULL;
size = PAGE_ALIGN(size);
------[cut here]---------

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