diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1995-11-14 08:00:00 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1995-11-14 08:00:00 +0000 |
commit | e7c2a72e2680827d6a733931273a93461c0d8d1b (patch) | |
tree | c9abeda78ef7504062bb2e816bcf3e3c9d680112 /arch/mips/mm/init.c | |
parent | ec6044459060a8c9ce7f64405c465d141898548c (diff) |
Import of Linux/MIPS 1.3.0
Diffstat (limited to 'arch/mips/mm/init.c')
-rw-r--r-- | arch/mips/mm/init.c | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c new file mode 100644 index 000000000..37912e2d0 --- /dev/null +++ b/arch/mips/mm/init.c @@ -0,0 +1,299 @@ +/* + * arch/mips/mm/init.c + * + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * Ported to MIPS by Ralf Baechle + */ +#include <linux/config.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/head.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> + +#include <asm/cachectl.h> +#include <asm/vector.h> +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/pgtable.h> + +extern void deskstation_tyne_dma_init(void); +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); + +extern char empty_zero_page[PAGE_SIZE]; + +/* + * 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]; + unsigned long page; + unsigned long dummy1, dummy2; + + page = ((unsigned long)empty_bad_page_table) + (PT_OFFSET - PAGE_OFFSET); +#ifdef __R4000__ + /* + * Use 64bit code even for Linux/MIPS 32bit on R4000 + */ + __asm__ __volatile__( + ".set\tnoreorder\n" + ".set\tnoat\n\t" + ".set\tmips3\n\t" + "dsll32\t$1,%2,0\n\t" + "dsrl32\t%2,$1,0\n\t" + "or\t%2,$1\n" + "1:\tsd\t%2,(%0)\n\t" + "subu\t%1,1\n\t" + "bnez\t%1,1b\n\t" + "addiu\t%0,8\n\t" + ".set\tmips0\n\t" + ".set\tat\n" + ".set\treorder" + :"=r" (dummy1), + "=r" (dummy2) + :"r" (pte_val(BAD_PAGE)), + "0" (page), + "1" (PAGE_SIZE/8)); +#else + __asm__ __volatile__( + ".set\tnoreorder\n" + "1:\tsw\t%2,(%0)\n\t" + "subu\t%1,1\n\t" + "bnez\t%1,1b\n\t" + "addiu\t%0,4\n\t" + ".set\treorder" + :"=r" (dummy1), + "=r" (dummy2) + :"r" (pte_val(BAD_PAGE)), + "0" (page), + "1" (PAGE_SIZE/4)); +#endif + + return (pte_t *)page; +} + +static inline void +__zeropage(unsigned long page) +{ + unsigned long dummy1, dummy2; + +#ifdef __R4000__ + /* + * Use 64bit code even for Linux/MIPS 32bit on R4000 + */ + __asm__ __volatile__( + ".set\tnoreorder\n" + ".set\tnoat\n\t" + ".set\tmips3\n" + "1:\tsd\t$0,(%0)\n\t" + "subu\t%1,1\n\t" + "bnez\t%1,1b\n\t" + "addiu\t%0,8\n\t" + ".set\tmips0\n\t" + ".set\tat\n" + ".set\treorder" + :"=r" (dummy1), + "=r" (dummy2) + :"0" (page), + "1" (PAGE_SIZE/8)); +#else + __asm__ __volatile__( + ".set\tnoreorder\n" + "1:\tsw\t$0,(%0)\n\t" + "subu\t%1,1\n\t" + "bnez\t%1,1b\n\t" + "addiu\t%0,4\n\t" + ".set\treorder" + :"=r" (dummy1), + "=r" (dummy2) + :"0" (page), + "1" (PAGE_SIZE/4)); +#endif +} + +static inline void +zeropage(unsigned long page) +{ + sys_cacheflush((void *)page, PAGE_SIZE, BCACHE); + sync_mem(); + __zeropage(page + (PT_OFFSET - PAGE_OFFSET)); +} + +pte_t __bad_page(void) +{ + extern char empty_bad_page[PAGE_SIZE]; + unsigned long page = (unsigned long)empty_bad_page; + + zeropage(page); + return pte_mkdirty(mk_pte(page, PAGE_SHARED)); +} + +unsigned long __zero_page(void) +{ + unsigned long page = (unsigned long) empty_zero_page; + + zeropage(page); + return page; +} + +/* + * This is horribly inefficient ... + */ +void __copy_page(unsigned long from, unsigned long to) +{ + /* + * Now copy page from uncached KSEG1 to KSEG0. The copy destination + * is in KSEG0 so that we keep stupid L2 caches happy. + */ + if(from == (unsigned long) empty_zero_page) + { + /* + * The page copied most is the COW empty_zero_page. Since we + * know it's contents we can avoid the writeback reading of + * the page. Speeds up the standard case alot. + */ + __zeropage(to); + } + else + { + /* + * Force writeback of old page to memory. We don't know the + * virtual address, so we have to flush the entire cache ... + */ + sys_cacheflush(0, ~0, DCACHE); + sync_mem(); + memcpy((void *) to, + (void *) (from + (PT_OFFSET - PAGE_OFFSET)), PAGE_SIZE); + } + /* + * Now writeback the page again if colour has changed. + * Actually this does a Hit_Writeback, but due to an artifact in + * the R4xx0 implementation this should be slightly faster. + * Then sweep chipset controlled secondary caches and the ICACHE. + */ + if (page_colour(from) != page_colour(to)) + sys_cacheflush(0, ~0, DCACHE); + sys_cacheflush(0, ~0, ICACHE); +} + +void show_mem(void) +{ + int i,free = 0,total = 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_OFFSET) >> PAGE_SHIFT; + while (i-- > 0) { + total++; + 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 pages shared\n", shared); + show_buffers(); +#ifdef CONFIG_NET + show_net_buffers(); +#endif +} + +extern unsigned long free_area_init(unsigned long, unsigned long); + +unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) +{ + pgd_init((unsigned long)swapper_pg_dir - (PT_OFFSET - PAGE_OFFSET)); + return free_area_init(start_mem, end_mem); +} + +void mem_init(unsigned long start_mem, unsigned long end_mem) +{ + int codepages = 0; + int datapages = 0; + unsigned long tmp; + extern int _etext; + + end_mem &= PAGE_MASK; + high_memory = end_mem; + + /* mark usable pages in the mem_map[] */ + start_mem = PAGE_ALIGN(start_mem); + + tmp = start_mem; + while (tmp < high_memory) { + mem_map[MAP_NR(tmp)] = 0; + tmp += PAGE_SIZE; + } +#ifdef CONFIG_DESKSTATION_TYNE + deskstation_tyne_dma_init(); +#endif +#ifdef CONFIG_SCSI + scsi_mem_init(high_memory); +#endif +#ifdef CONFIG_SOUND + sound_mem_init(); +#endif + for (tmp = PAGE_OFFSET ; tmp < high_memory ; tmp += PAGE_SIZE) { + if (mem_map[MAP_NR(tmp)]) { + if (tmp < (unsigned long) &_etext) + codepages++; + else if (tmp < start_mem) + 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 data)\n", + tmp >> 10, + (high_memory - PAGE_OFFSET) >> 10, + codepages << (PAGE_SHIFT-10), + datapages << (PAGE_SHIFT-10)); + + 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; +} |