/* * linux/arch/i386/mm/init.c * * Copyright (C) 1995 Linus Torvalds */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern void scsi_mem_init(unsigned long); extern void sound_mem_init(void); extern void die_if_kernel(char *,struct pt_regs *,long); extern void show_net_buffers(void); /* * BAD_PAGE is the page that is used for page faults when linux * is out-of-memory. Older versions of linux just did a * do_exit(), but using this instead means there is less risk * for a process dying in kernel mode, possibly leaving a inode * unused etc.. * * BAD_PAGETABLE is the accompanying page-table: it is initialized * to point to BAD_PAGE entries. * * ZERO_PAGE is a special page that is used for zero-initialized * data and COW. */ pte_t * __bad_pagetable(void) { extern char empty_bad_page_table[PAGE_SIZE]; __asm__ __volatile__("cld ; rep ; stosl": :"a" (pte_val(BAD_PAGE)), "D" ((long) empty_bad_page_table), "c" (PAGE_SIZE/4) :"di","cx"); return (pte_t *) empty_bad_page_table; } pte_t __bad_page(void) { extern char empty_bad_page[PAGE_SIZE]; __asm__ __volatile__("cld ; rep ; stosl": :"a" (0), "D" ((long) empty_bad_page), "c" (PAGE_SIZE/4) :"di","cx"); return pte_mkdirty(mk_pte((unsigned long) empty_bad_page, PAGE_SHARED)); } void show_mem(void) { int i,free = 0,total = 0,reserved = 0; int shared = 0; printk("Mem-info:\n"); show_free_areas(); printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); i = high_memory >> PAGE_SHIFT; while (i-- > 0) { total++; if (mem_map[i] & MAP_PAGE_RESERVED) reserved++; else if (!mem_map[i]) free++; else shared += mem_map[i]-1; } printk("%d pages of RAM\n",total); printk("%d free pages\n",free); printk("%d reserved pages\n",reserved); printk("%d pages shared\n",shared); show_buffers(); #ifdef CONFIG_NET show_net_buffers(); #endif } extern unsigned long free_area_init(unsigned long, unsigned long); /* * paging_init() sets up the page tables - note that the first 4MB are * already mapped by head.S. * * This routines also unmaps the page at virtual kernel address 0, so * that we can trap those pesky NULL-reference errors in the kernel. */ unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) { pgd_t * pg_dir; pte_t * pg_table; unsigned long tmp; unsigned long address; /* * Physical page 0 is special; it's not touched by Linux since BIOS * and SMM (for laptops with [34]86/SL chips) may need it. It is read * and write protected to detect null pointer references in the * kernel. */ #if 0 memset((void *) 0, 0, PAGE_SIZE); #endif start_mem = PAGE_ALIGN(start_mem); address = 0; pg_dir = swapper_pg_dir; while (address < end_mem) { /* map the memory at virtual addr 0xC0000000 */ pg_table = (pte_t *) (PAGE_MASK & pgd_val(pg_dir[768])); if (!pg_table) { pg_table = (pte_t *) start_mem; start_mem += PAGE_SIZE; } /* also map it temporarily at 0x0000000 for init */ pgd_val(pg_dir[0]) = _PAGE_TABLE | (unsigned long) pg_table; pgd_val(pg_dir[768]) = _PAGE_TABLE | (unsigned long) pg_table; pg_dir++; for (tmp = 0 ; tmp < PTRS_PER_PTE ; tmp++,pg_table++) { if (address < end_mem) *pg_table = mk_pte(address, PAGE_SHARED); else pte_clear(pg_table); address += PAGE_SIZE; } } invalidate(); return free_area_init(start_mem, end_mem); } void mem_init(unsigned long start_mem, unsigned long end_mem) { unsigned long start_low_mem = PAGE_SIZE; int codepages = 0; int reservedpages = 0; int datapages = 0; unsigned long tmp; extern int etext; 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); 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.. */ while (start_low_mem < 0x9f000) { mem_map[MAP_NR(start_low_mem)] = 0; start_low_mem += PAGE_SIZE; } while (start_mem < high_memory) { mem_map[MAP_NR(start_mem)] = 0; start_mem += PAGE_SIZE; } #ifdef CONFIG_SCSI scsi_mem_init(high_memory); #endif #ifdef CONFIG_SOUND sound_mem_init(); #endif for (tmp = 0 ; tmp < high_memory ; tmp += PAGE_SIZE) { if (mem_map[MAP_NR(tmp)]) { if (tmp >= 0xA0000 && tmp < 0x100000) reservedpages++; else if (tmp < (unsigned long) &etext) codepages++; else datapages++; continue; } mem_map[MAP_NR(tmp)] = 1; free_page(tmp); } tmp = nr_free_pages << PAGE_SHIFT; printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data)\n", tmp >> 10, high_memory >> 10, codepages << (PAGE_SHIFT-10), reservedpages << (PAGE_SHIFT-10), datapages << (PAGE_SHIFT-10)); /* test if the WP bit is honoured in supervisor mode */ wp_works_ok = -1; pg0[0] = pte_val(mk_pte(0, PAGE_READONLY)); invalidate(); __asm__ __volatile__("movb 0,%%al ; movb %%al,0": : :"ax", "memory"); pg0[0] = 0; invalidate(); if (wp_works_ok < 0) wp_works_ok = 0; #ifdef CONFIG_TEST_VERIFY_AREA wp_works_ok = 0; #endif return; } void si_meminfo(struct sysinfo *val) { int i; i = high_memory >> PAGE_SHIFT; val->totalram = 0; val->sharedram = 0; val->freeram = nr_free_pages << PAGE_SHIFT; val->bufferram = buffermem; while (i-- > 0) { if (mem_map[i] & MAP_PAGE_RESERVED) continue; val->totalram++; if (!mem_map[i]) continue; val->sharedram += mem_map[i]-1; } val->totalram <<= PAGE_SHIFT; val->sharedram <<= PAGE_SHIFT; return; }