summaryrefslogtreecommitdiffstats
path: root/arch/mips/mm/init.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1995-11-14 08:00:00 +0000
committer <ralf@linux-mips.org>1995-11-14 08:00:00 +0000
commite7c2a72e2680827d6a733931273a93461c0d8d1b (patch)
treec9abeda78ef7504062bb2e816bcf3e3c9d680112 /arch/mips/mm/init.c
parentec6044459060a8c9ce7f64405c465d141898548c (diff)
Import of Linux/MIPS 1.3.0
Diffstat (limited to 'arch/mips/mm/init.c')
-rw-r--r--arch/mips/mm/init.c299
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;
+}