--- 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 #include #include +#include ! 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 , August 1999 + * + * Memory region support + * Jessica L. Parsons , July-August 1999 */ /* @@ -51,6 +54,7 @@ #include #include #include +#include /* * 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 , overriding the bios size. + * "mem=XXX[KkmM]@XXX[KkmM]" defines a memory region from + * to +, 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 #include #include +#include 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*/