aboutsummaryrefslogtreecommitdiff
path: root/arch/sparc/mm
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/sparc/mm
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'arch/sparc/mm')
-rw-r--r--arch/sparc/mm/Makefile23
-rw-r--r--arch/sparc/mm/btfixup.c336
-rw-r--r--arch/sparc/mm/extable.c77
-rw-r--r--arch/sparc/mm/fault.c596
-rw-r--r--arch/sparc/mm/generic.c154
-rw-r--r--arch/sparc/mm/highmem.c120
-rw-r--r--arch/sparc/mm/hypersparc.S413
-rw-r--r--arch/sparc/mm/init.c515
-rw-r--r--arch/sparc/mm/io-unit.c318
-rw-r--r--arch/sparc/mm/iommu.c475
-rw-r--r--arch/sparc/mm/loadmmu.c46
-rw-r--r--arch/sparc/mm/nosrmmu.c59
-rw-r--r--arch/sparc/mm/nosun4c.c77
-rw-r--r--arch/sparc/mm/srmmu.c2274
-rw-r--r--arch/sparc/mm/sun4c.c2276
-rw-r--r--arch/sparc/mm/swift.S256
-rw-r--r--arch/sparc/mm/tsunami.S133
-rw-r--r--arch/sparc/mm/viking.S284
18 files changed, 8432 insertions, 0 deletions
diff --git a/arch/sparc/mm/Makefile b/arch/sparc/mm/Makefile
new file mode 100644
index 00000000000..16eeba4b991
--- /dev/null
+++ b/arch/sparc/mm/Makefile
@@ -0,0 +1,23 @@
+# $Id: Makefile,v 1.38 2000/12/15 00:41:22 davem Exp $
+# Makefile for the linux Sparc-specific parts of the memory manager.
+#
+
+EXTRA_AFLAGS := -ansi
+
+obj-y := fault.o init.o loadmmu.o generic.o extable.o btfixup.o
+
+ifeq ($(CONFIG_SUN4),y)
+obj-y += nosrmmu.o
+else
+obj-y += srmmu.o iommu.o io-unit.o hypersparc.o viking.o tsunami.o swift.o
+endif
+
+ifdef CONFIG_HIGHMEM
+obj-y += highmem.o
+endif
+
+ifdef CONFIG_SMP
+obj-y += nosun4c.o
+else
+obj-y += sun4c.o
+endif
diff --git a/arch/sparc/mm/btfixup.c b/arch/sparc/mm/btfixup.c
new file mode 100644
index 00000000000..f147a44c978
--- /dev/null
+++ b/arch/sparc/mm/btfixup.c
@@ -0,0 +1,336 @@
+/* $Id: btfixup.c,v 1.10 2000/05/09 17:40:13 davem Exp $
+ * btfixup.c: Boot time code fixup and relocator, so that
+ * we can get rid of most indirect calls to achieve single
+ * image sun4c and srmmu kernel.
+ *
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <asm/btfixup.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/oplib.h>
+#include <asm/system.h>
+#include <asm/cacheflush.h>
+
+#define BTFIXUP_OPTIMIZE_NOP
+#define BTFIXUP_OPTIMIZE_OTHER
+
+extern char *srmmu_name;
+static char version[] __initdata = "Boot time fixup v1.6. 4/Mar/98 Jakub Jelinek (jj@ultra.linux.cz). Patching kernel for ";
+#ifdef CONFIG_SUN4
+static char str_sun4c[] __initdata = "sun4\n";
+#else
+static char str_sun4c[] __initdata = "sun4c\n";
+#endif
+static char str_srmmu[] __initdata = "srmmu[%s]/";
+static char str_iommu[] __initdata = "iommu\n";
+static char str_iounit[] __initdata = "io-unit\n";
+
+static int visited __initdata = 0;
+extern unsigned int ___btfixup_start[], ___btfixup_end[], __init_begin[], __init_end[], __init_text_end[];
+extern unsigned int _stext[], _end[], __start___ksymtab[], __stop___ksymtab[];
+static char wrong_f[] __initdata = "Trying to set f fixup %p to invalid function %08x\n";
+static char wrong_b[] __initdata = "Trying to set b fixup %p to invalid function %08x\n";
+static char wrong_s[] __initdata = "Trying to set s fixup %p to invalid value %08x\n";
+static char wrong_h[] __initdata = "Trying to set h fixup %p to invalid value %08x\n";
+static char wrong_a[] __initdata = "Trying to set a fixup %p to invalid value %08x\n";
+static char wrong[] __initdata = "Wrong address for %c fixup %p\n";
+static char insn_f[] __initdata = "Fixup f %p refers to weird instructions at %p[%08x,%08x]\n";
+static char insn_b[] __initdata = "Fixup b %p doesn't refer to a SETHI at %p[%08x]\n";
+static char insn_s[] __initdata = "Fixup s %p doesn't refer to an OR at %p[%08x]\n";
+static char insn_h[] __initdata = "Fixup h %p doesn't refer to a SETHI at %p[%08x]\n";
+static char insn_a[] __initdata = "Fixup a %p doesn't refer to a SETHI nor OR at %p[%08x]\n";
+static char insn_i[] __initdata = "Fixup i %p doesn't refer to a valid instruction at %p[%08x]\n";
+static char fca_und[] __initdata = "flush_cache_all undefined in btfixup()\n";
+static char wrong_setaddr[] __initdata = "Garbled CALL/INT patch at %p[%08x,%08x,%08x]=%08x\n";
+
+#ifdef BTFIXUP_OPTIMIZE_OTHER
+static void __init set_addr(unsigned int *addr, unsigned int q1, int fmangled, unsigned int value)
+{
+ if (!fmangled)
+ *addr = value;
+ else {
+ unsigned int *q = (unsigned int *)q1;
+ if (*addr == 0x01000000) {
+ /* Noped */
+ *q = value;
+ } else if (addr[-1] == *q) {
+ /* Moved */
+ addr[-1] = value;
+ *q = value;
+ } else {
+ prom_printf(wrong_setaddr, addr-1, addr[-1], *addr, *q, value);
+ prom_halt();
+ }
+ }
+}
+#else
+static __inline__ void set_addr(unsigned int *addr, unsigned int q1, int fmangled, unsigned int value)
+{
+ *addr = value;
+}
+#endif
+
+void __init btfixup(void)
+{
+ unsigned int *p, *q;
+ int type, count;
+ unsigned insn;
+ unsigned *addr;
+ int fmangled = 0;
+ void (*flush_cacheall)(void);
+
+ if (!visited) {
+ visited++;
+ printk(version);
+ if (ARCH_SUN4C_SUN4)
+ printk(str_sun4c);
+ else {
+ printk(str_srmmu, srmmu_name);
+ if (sparc_cpu_model == sun4d)
+ printk(str_iounit);
+ else
+ printk(str_iommu);
+ }
+ }
+ for (p = ___btfixup_start; p < ___btfixup_end; ) {
+ count = p[2];
+ q = p + 3;
+ switch (type = *(unsigned char *)p) {
+ case 'f':
+ count = p[3];
+ q = p + 4;
+ if (((p[0] & 1) || p[1])
+ && ((p[1] & 3) || (unsigned *)(p[1]) < _stext || (unsigned *)(p[1]) >= _end)) {
+ prom_printf(wrong_f, p, p[1]);
+ prom_halt();
+ }
+ break;
+ case 'b':
+ if (p[1] < (unsigned long)__init_begin || p[1] >= (unsigned long)__init_text_end || (p[1] & 3)) {
+ prom_printf(wrong_b, p, p[1]);
+ prom_halt();
+ }
+ break;
+ case 's':
+ if (p[1] + 0x1000 >= 0x2000) {
+ prom_printf(wrong_s, p, p[1]);
+ prom_halt();
+ }
+ break;
+ case 'h':
+ if (p[1] & 0x3ff) {
+ prom_printf(wrong_h, p, p[1]);
+ prom_halt();
+ }
+ break;
+ case 'a':
+ if (p[1] + 0x1000 >= 0x2000 && (p[1] & 0x3ff)) {
+ prom_printf(wrong_a, p, p[1]);
+ prom_halt();
+ }
+ break;
+ }
+ if (p[0] & 1) {
+ p[0] &= ~1;
+ while (count) {
+ fmangled = 0;
+ addr = (unsigned *)*q;
+ if (addr < _stext || addr >= _end) {
+ prom_printf(wrong, type, p);
+ prom_halt();
+ }
+ insn = *addr;
+#ifdef BTFIXUP_OPTIMIZE_OTHER
+ if (type != 'f' && q[1]) {
+ insn = *(unsigned int *)q[1];
+ if (!insn || insn == 1)
+ insn = *addr;
+ else
+ fmangled = 1;
+ }
+#endif
+ switch (type) {
+ case 'f': /* CALL */
+ if (addr >= __start___ksymtab && addr < __stop___ksymtab) {
+ *addr = p[1];
+ break;
+ } else if (!q[1]) {
+ if ((insn & 0xc1c00000) == 0x01000000) { /* SETHI */
+ *addr = (insn & 0xffc00000) | (p[1] >> 10); break;
+ } else if ((insn & 0xc1f82000) == 0x80102000) { /* OR X, %LO(i), Y */
+ *addr = (insn & 0xffffe000) | (p[1] & 0x3ff); break;
+ } else if ((insn & 0xc0000000) != 0x40000000) { /* !CALL */
+ bad_f:
+ prom_printf(insn_f, p, addr, insn, addr[1]);
+ prom_halt();
+ }
+ } else if (q[1] != 1)
+ addr[1] = q[1];
+ if (p[2] == BTFIXUPCALL_NORM) {
+ norm_f:
+ *addr = 0x40000000 | ((p[1] - (unsigned)addr) >> 2);
+ q[1] = 0;
+ break;
+ }
+#ifndef BTFIXUP_OPTIMIZE_NOP
+ goto norm_f;
+#else
+ if (!(addr[1] & 0x80000000)) {
+ if ((addr[1] & 0xc1c00000) != 0x01000000) /* !SETHI */
+ goto bad_f; /* CALL, Bicc, FBfcc, CBccc are weird in delay slot, aren't they? */
+ } else {
+ if ((addr[1] & 0x01800000) == 0x01800000) {
+ if ((addr[1] & 0x01f80000) == 0x01e80000) {
+ /* RESTORE */
+ goto norm_f; /* It is dangerous to patch that */
+ }
+ goto bad_f;
+ }
+ if ((addr[1] & 0xffffe003) == 0x9e03e000) {
+ /* ADD %O7, XX, %o7 */
+ int displac = (addr[1] << 19);
+
+ displac = (displac >> 21) + 2;
+ *addr = (0x10800000) + (displac & 0x3fffff);
+ q[1] = addr[1];
+ addr[1] = p[2];
+ break;
+ }
+ if ((addr[1] & 0x201f) == 0x200f || (addr[1] & 0x7c000) == 0x3c000)
+ goto norm_f; /* Someone is playing bad tricks with us: rs1 or rs2 is o7 */
+ if ((addr[1] & 0x3e000000) == 0x1e000000)
+ goto norm_f; /* rd is %o7. We'd better take care. */
+ }
+ if (p[2] == BTFIXUPCALL_NOP) {
+ *addr = 0x01000000;
+ q[1] = 1;
+ break;
+ }
+#ifndef BTFIXUP_OPTIMIZE_OTHER
+ goto norm_f;
+#else
+ if (addr[1] == 0x01000000) { /* NOP in the delay slot */
+ q[1] = addr[1];
+ *addr = p[2];
+ break;
+ }
+ if ((addr[1] & 0xc0000000) != 0xc0000000) {
+ /* Not a memory operation */
+ if ((addr[1] & 0x30000000) == 0x10000000) {
+ /* Ok, non-memory op with rd %oX */
+ if ((addr[1] & 0x3e000000) == 0x1c000000)
+ goto bad_f; /* Aiee. Someone is playing strange %sp tricks */
+ if ((addr[1] & 0x3e000000) > 0x12000000 ||
+ ((addr[1] & 0x3e000000) == 0x12000000 &&
+ p[2] != BTFIXUPCALL_STO1O0 && p[2] != BTFIXUPCALL_SWAPO0O1) ||
+ ((p[2] & 0xffffe000) == BTFIXUPCALL_RETINT(0))) {
+ /* Nobody uses the result. We can nop it out. */
+ *addr = p[2];
+ q[1] = addr[1];
+ addr[1] = 0x01000000;
+ break;
+ }
+ if ((addr[1] & 0xf1ffffe0) == 0x90100000) {
+ /* MOV %reg, %Ox */
+ if ((addr[1] & 0x3e000000) == 0x10000000 &&
+ (p[2] & 0x7c000) == 0x20000) {
+ /* Ok, it is call xx; mov reg, %o0 and call optimizes
+ to doing something on %o0. Patch the patch. */
+ *addr = (p[2] & ~0x7c000) | ((addr[1] & 0x1f) << 14);
+ q[1] = addr[1];
+ addr[1] = 0x01000000;
+ break;
+ }
+ if ((addr[1] & 0x3e000000) == 0x12000000 &&
+ p[2] == BTFIXUPCALL_STO1O0) {
+ *addr = (p[2] & ~0x3e000000) | ((addr[1] & 0x1f) << 25);
+ q[1] = addr[1];
+ addr[1] = 0x01000000;
+ break;
+ }
+ }
+ }
+ }
+ *addr = addr[1];
+ q[1] = addr[1];
+ addr[1] = p[2];
+ break;
+#endif /* BTFIXUP_OPTIMIZE_OTHER */
+#endif /* BTFIXUP_OPTIMIZE_NOP */
+ case 'b': /* BLACKBOX */
+ /* Has to be sethi i, xx */
+ if ((insn & 0xc1c00000) != 0x01000000) {
+ prom_printf(insn_b, p, addr, insn);
+ prom_halt();
+ } else {
+ void (*do_fixup)(unsigned *);
+
+ do_fixup = (void (*)(unsigned *))p[1];
+ do_fixup(addr);
+ }
+ break;
+ case 's': /* SIMM13 */
+ /* Has to be or %g0, i, xx */
+ if ((insn & 0xc1ffe000) != 0x80102000) {
+ prom_printf(insn_s, p, addr, insn);
+ prom_halt();
+ }
+ set_addr(addr, q[1], fmangled, (insn & 0xffffe000) | (p[1] & 0x1fff));
+ break;
+ case 'h': /* SETHI */
+ /* Has to be sethi i, xx */
+ if ((insn & 0xc1c00000) != 0x01000000) {
+ prom_printf(insn_h, p, addr, insn);
+ prom_halt();
+ }
+ set_addr(addr, q[1], fmangled, (insn & 0xffc00000) | (p[1] >> 10));
+ break;
+ case 'a': /* HALF */
+ /* Has to be sethi i, xx or or %g0, i, xx */
+ if ((insn & 0xc1c00000) != 0x01000000 &&
+ (insn & 0xc1ffe000) != 0x80102000) {
+ prom_printf(insn_a, p, addr, insn);
+ prom_halt();
+ }
+ if (p[1] & 0x3ff)
+ set_addr(addr, q[1], fmangled,
+ (insn & 0x3e000000) | 0x80102000 | (p[1] & 0x1fff));
+ else
+ set_addr(addr, q[1], fmangled,
+ (insn & 0x3e000000) | 0x01000000 | (p[1] >> 10));
+ break;
+ case 'i': /* INT */
+ if ((insn & 0xc1c00000) == 0x01000000) /* %HI */
+ set_addr(addr, q[1], fmangled, (insn & 0xffc00000) | (p[1] >> 10));
+ else if ((insn & 0x80002000) == 0x80002000 &&
+ (insn & 0x01800000) != 0x01800000) /* %LO */
+ set_addr(addr, q[1], fmangled, (insn & 0xffffe000) | (p[1] & 0x3ff));
+ else {
+ prom_printf(insn_i, p, addr, insn);
+ prom_halt();
+ }
+ break;
+ }
+ count -= 2;
+ q += 2;
+ }
+ } else
+ p = q + count;
+ }
+#ifdef CONFIG_SMP
+ flush_cacheall = (void (*)(void))BTFIXUPVAL_CALL(local_flush_cache_all);
+#else
+ flush_cacheall = (void (*)(void))BTFIXUPVAL_CALL(flush_cache_all);
+#endif
+ if (!flush_cacheall) {
+ prom_printf(fca_und);
+ prom_halt();
+ }
+ (*flush_cacheall)();
+}
diff --git a/arch/sparc/mm/extable.c b/arch/sparc/mm/extable.c
new file mode 100644
index 00000000000..c9845c71f42
--- /dev/null
+++ b/arch/sparc/mm/extable.c
@@ -0,0 +1,77 @@
+/*
+ * linux/arch/sparc/mm/extable.c
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+
+void sort_extable(struct exception_table_entry *start,
+ struct exception_table_entry *finish)
+{
+}
+
+/* Caller knows they are in a range if ret->fixup == 0 */
+const struct exception_table_entry *
+search_extable(const struct exception_table_entry *start,
+ const struct exception_table_entry *last,
+ unsigned long value)
+{
+ const struct exception_table_entry *walk;
+
+ /* Single insn entries are encoded as:
+ * word 1: insn address
+ * word 2: fixup code address
+ *
+ * Range entries are encoded as:
+ * word 1: first insn address
+ * word 2: 0
+ * word 3: last insn address + 4 bytes
+ * word 4: fixup code address
+ *
+ * See asm/uaccess.h for more details.
+ */
+
+ /* 1. Try to find an exact match. */
+ for (walk = start; walk <= last; walk++) {
+ if (walk->fixup == 0) {
+ /* A range entry, skip both parts. */
+ walk++;
+ continue;
+ }
+
+ if (walk->insn == value)
+ return walk;
+ }
+
+ /* 2. Try to find a range match. */
+ for (walk = start; walk <= (last - 1); walk++) {
+ if (walk->fixup)
+ continue;
+
+ if (walk[0].insn <= value && walk[1].insn > value)
+ return walk;
+
+ walk++;
+ }
+
+ return NULL;
+}
+
+/* Special extable search, which handles ranges. Returns fixup */
+unsigned long search_extables_range(unsigned long addr, unsigned long *g2)
+{
+ const struct exception_table_entry *entry;
+
+ entry = search_exception_tables(addr);
+ if (!entry)
+ return 0;
+
+ /* Inside range? Fix g2 and return correct fixup */
+ if (!entry->fixup) {
+ *g2 = (addr - entry->insn) / 4;
+ return (entry + 1)->fixup;
+ }
+
+ return entry->fixup;
+}
diff --git a/arch/sparc/mm/fault.c b/arch/sparc/mm/fault.c
new file mode 100644
index 00000000000..37f4107bae6
--- /dev/null
+++ b/arch/sparc/mm/fault.c
@@ -0,0 +1,596 @@
+/* $Id: fault.c,v 1.122 2001/11/17 07:19:26 davem Exp $
+ * fault.c: Page fault handlers for the Sparc.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <asm/head.h>
+
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/threads.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/memreg.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/smp.h>
+#include <asm/traps.h>
+#include <asm/kdebug.h>
+#include <asm/uaccess.h>
+
+#define ELEMENTS(arr) (sizeof (arr)/sizeof (arr[0]))
+
+extern int prom_node_root;
+
+/* At boot time we determine these two values necessary for setting
+ * up the segment maps and page table entries (pte's).
+ */
+
+int num_segmaps, num_contexts;
+int invalid_segment;
+
+/* various Virtual Address Cache parameters we find at boot time... */
+
+int vac_size, vac_linesize, vac_do_hw_vac_flushes;
+int vac_entries_per_context, vac_entries_per_segment;
+int vac_entries_per_page;
+
+/* Nice, simple, prom library does all the sweating for us. ;) */
+int prom_probe_memory (void)
+{
+ register struct linux_mlist_v0 *mlist;
+ register unsigned long bytes, base_paddr, tally;
+ register int i;
+
+ i = 0;
+ mlist= *prom_meminfo()->v0_available;
+ bytes = tally = mlist->num_bytes;
+ base_paddr = (unsigned long) mlist->start_adr;
+
+ sp_banks[0].base_addr = base_paddr;
+ sp_banks[0].num_bytes = bytes;
+
+ while (mlist->theres_more != (void *) 0){
+ i++;
+ mlist = mlist->theres_more;
+ bytes = mlist->num_bytes;
+ tally += bytes;
+ if (i > SPARC_PHYS_BANKS-1) {
+ printk ("The machine has more banks than "
+ "this kernel can support\n"
+ "Increase the SPARC_PHYS_BANKS "
+ "setting (currently %d)\n",
+ SPARC_PHYS_BANKS);
+ i = SPARC_PHYS_BANKS-1;
+ break;
+ }
+
+ sp_banks[i].base_addr = (unsigned long) mlist->start_adr;
+ sp_banks[i].num_bytes = mlist->num_bytes;
+ }
+
+ i++;
+ sp_banks[i].base_addr = 0xdeadbeef;
+ sp_banks[i].num_bytes = 0;
+
+ /* Now mask all bank sizes on a page boundary, it is all we can
+ * use anyways.
+ */
+ for(i=0; sp_banks[i].num_bytes != 0; i++)
+ sp_banks[i].num_bytes &= PAGE_MASK;
+
+ return tally;
+}
+
+/* Traverse the memory lists in the prom to see how much physical we
+ * have.
+ */
+unsigned long
+probe_memory(void)
+{
+ int total;
+
+ total = prom_probe_memory();
+
+ /* Oh man, much nicer, keep the dirt in promlib. */
+ return total;
+}
+
+extern void sun4c_complete_all_stores(void);
+
+/* Whee, a level 15 NMI interrupt memory error. Let's have fun... */
+asmlinkage void sparc_lvl15_nmi(struct pt_regs *regs, unsigned long serr,
+ unsigned long svaddr, unsigned long aerr,
+ unsigned long avaddr)
+{
+ sun4c_complete_all_stores();
+ printk("FAULT: NMI received\n");
+ printk("SREGS: Synchronous Error %08lx\n", serr);
+ printk(" Synchronous Vaddr %08lx\n", svaddr);
+ printk(" Asynchronous Error %08lx\n", aerr);
+ printk(" Asynchronous Vaddr %08lx\n", avaddr);
+ if (sun4c_memerr_reg)
+ printk(" Memory Parity Error %08lx\n", *sun4c_memerr_reg);
+ printk("REGISTER DUMP:\n");
+ show_regs(regs);
+ prom_halt();
+}
+
+static void unhandled_fault(unsigned long, struct task_struct *,
+ struct pt_regs *) __attribute__ ((noreturn));
+
+static void unhandled_fault(unsigned long address, struct task_struct *tsk,
+ struct pt_regs *regs)
+{
+ if((unsigned long) address < PAGE_SIZE) {
+ printk(KERN_ALERT
+ "Unable to handle kernel NULL pointer dereference\n");
+ } else {
+ printk(KERN_ALERT "Unable to handle kernel paging request "
+ "at virtual address %08lx\n", address);
+ }
+ printk(KERN_ALERT "tsk->{mm,active_mm}->context = %08lx\n",
+ (tsk->mm ? tsk->mm->context : tsk->active_mm->context));
+ printk(KERN_ALERT "tsk->{mm,active_mm}->pgd = %08lx\n",
+ (tsk->mm ? (unsigned long) tsk->mm->pgd :
+ (unsigned long) tsk->active_mm->pgd));
+ die_if_kernel("Oops", regs);
+}
+
+asmlinkage int lookup_fault(unsigned long pc, unsigned long ret_pc,
+ unsigned long address)
+{
+ struct pt_regs regs;
+ unsigned long g2;
+ unsigned int insn;
+ int i;
+
+ i = search_extables_range(ret_pc, &g2);
+ switch (i) {
+ case 3:
+ /* load & store will be handled by fixup */
+ return 3;
+
+ case 1:
+ /* store will be handled by fixup, load will bump out */
+ /* for _to_ macros */
+ insn = *((unsigned int *) pc);
+ if ((insn >> 21) & 1)
+ return 1;
+ break;
+
+ case 2:
+ /* load will be handled by fixup, store will bump out */
+ /* for _from_ macros */
+ insn = *((unsigned int *) pc);
+ if (!((insn >> 21) & 1) || ((insn>>19)&0x3f) == 15)
+ return 2;
+ break;
+
+ default:
+ break;
+ };
+
+ memset(&regs, 0, sizeof (regs));
+ regs.pc = pc;
+ regs.npc = pc + 4;
+ __asm__ __volatile__(
+ "rd %%psr, %0\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n" : "=r" (regs.psr));
+ unhandled_fault(address, current, &regs);
+
+ /* Not reached */
+ return 0;
+}
+
+extern unsigned long safe_compute_effective_address(struct pt_regs *,
+ unsigned int);
+
+static unsigned long compute_si_addr(struct pt_regs *regs, int text_fault)
+{
+ unsigned int insn;
+
+ if (text_fault)
+ return regs->pc;
+
+ if (regs->psr & PSR_PS) {
+ insn = *(unsigned int *) regs->pc;
+ } else {
+ __get_user(insn, (unsigned int *) regs->pc);
+ }
+
+ return safe_compute_effective_address(regs, insn);
+}
+
+asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write,
+ unsigned long address)
+{
+ struct vm_area_struct *vma;
+ struct task_struct *tsk = current;
+ struct mm_struct *mm = tsk->mm;
+ unsigned int fixup;
+ unsigned long g2;
+ siginfo_t info;
+ int from_user = !(regs->psr & PSR_PS);
+
+ if(text_fault)
+ address = regs->pc;
+
+ /*
+ * We fault-in kernel-space virtual memory on-demand. The
+ * 'reference' page table is init_mm.pgd.
+ *
+ * NOTE! We MUST NOT take any locks for this case. We may
+ * be in an interrupt or a critical region, and should
+ * only copy the information from the master page table,
+ * nothing more.
+ */
+ if (!ARCH_SUN4C_SUN4 && address >= TASK_SIZE)
+ goto vmalloc_fault;
+
+ info.si_code = SEGV_MAPERR;
+
+ /*
+ * If we're in an interrupt or have no user
+ * context, we must not take the fault..
+ */
+ if (in_atomic() || !mm)
+ goto no_context;
+
+ down_read(&mm->mmap_sem);
+
+ /*
+ * The kernel referencing a bad kernel pointer can lock up
+ * a sun4c machine completely, so we must attempt recovery.
+ */
+ if(!from_user && address >= PAGE_OFFSET)
+ goto bad_area;
+
+ vma = find_vma(mm, address);
+ if(!vma)
+ goto bad_area;
+ if(vma->vm_start <= address)
+ goto good_area;
+ if(!(vma->vm_flags & VM_GROWSDOWN))
+ goto bad_area;
+ if(expand_stack(vma, address))
+ goto bad_area;
+ /*
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
+ */
+good_area:
+ info.si_code = SEGV_ACCERR;
+ if(write) {
+ if(!(vma->vm_flags & VM_WRITE))
+ goto bad_area;
+ } else {
+ /* Allow reads even for write-only mappings */
+ if(!(vma->vm_flags & (VM_READ | VM_EXEC)))
+ goto bad_area;
+ }
+
+ /*
+ * If for any reason at all we couldn't handle the fault,
+ * make sure we exit gracefully rather than endlessly redo
+ * the fault.
+ */
+ switch (handle_mm_fault(mm, vma, address, write)) {
+ case VM_FAULT_SIGBUS:
+ goto do_sigbus;
+ case VM_FAULT_OOM:
+ goto out_of_memory;
+ case VM_FAULT_MAJOR:
+ current->maj_flt++;
+ break;
+ case VM_FAULT_MINOR:
+ default:
+ current->min_flt++;
+ break;
+ }
+ up_read(&mm->mmap_sem);
+ return;
+
+ /*
+ * Something tried to access memory that isn't in our memory map..
+ * Fix it, but check if it's kernel or user first..
+ */
+bad_area:
+ up_read(&mm->mmap_sem);
+
+bad_area_nosemaphore:
+ /* User mode accesses just cause a SIGSEGV */
+ if(from_user) {
+#if 0
+ printk("Fault whee %s [%d]: segfaults at %08lx pc=%08lx\n",
+ tsk->comm, tsk->pid, address, regs->pc);
+#endif
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ /* info.si_code set above to make clear whether
+ this was a SEGV_MAPERR or SEGV_ACCERR fault. */
+ info.si_addr = (void __user *)compute_si_addr(regs, text_fault);
+ info.si_trapno = 0;
+ force_sig_info (SIGSEGV, &info, tsk);
+ return;
+ }
+
+ /* Is this in ex_table? */
+no_context:
+ g2 = regs->u_regs[UREG_G2];
+ if (!from_user && (fixup = search_extables_range(regs->pc, &g2))) {
+ if (fixup > 10) { /* Values below are reserved for other things */
+ extern const unsigned __memset_start[];
+ extern const unsigned __memset_end[];
+ extern const unsigned __csum_partial_copy_start[];
+ extern const unsigned __csum_partial_copy_end[];
+
+#ifdef DEBUG_EXCEPTIONS
+ printk("Exception: PC<%08lx> faddr<%08lx>\n", regs->pc, address);
+ printk("EX_TABLE: insn<%08lx> fixup<%08x> g2<%08lx>\n",
+ regs->pc, fixup, g2);
+#endif
+ if ((regs->pc >= (unsigned long)__memset_start &&
+ regs->pc < (unsigned long)__memset_end) ||
+ (regs->pc >= (unsigned long)__csum_partial_copy_start &&
+ regs->pc < (unsigned long)__csum_partial_copy_end)) {
+ regs->u_regs[UREG_I4] = address;
+ regs->u_regs[UREG_I5] = regs->pc;
+ }
+ regs->u_regs[UREG_G2] = g2;
+ regs->pc = fixup;
+ regs->npc = regs->pc + 4;
+ return;
+ }
+ }
+
+ unhandled_fault (address, tsk, regs);
+ do_exit(SIGKILL);
+
+/*
+ * We ran out of memory, or some other thing happened to us that made
+ * us unable to handle the page fault gracefully.
+ */
+out_of_memory:
+ up_read(&mm->mmap_sem);
+ printk("VM: killing process %s\n", tsk->comm);
+ if (from_user)
+ do_exit(SIGKILL);
+ goto no_context;
+
+do_sigbus:
+ up_read(&mm->mmap_sem);
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = BUS_ADRERR;
+ info.si_addr = (void __user *) compute_si_addr(regs, text_fault);
+ info.si_trapno = 0;
+ force_sig_info (SIGBUS, &info, tsk);
+ if (!from_user)
+ goto no_context;
+
+vmalloc_fault:
+ {
+ /*
+ * Synchronize this task's top level page-table
+ * with the 'reference' page table.
+ */
+ int offset = pgd_index(address);
+ pgd_t *pgd, *pgd_k;
+ pmd_t *pmd, *pmd_k;
+
+ pgd = tsk->active_mm->pgd + offset;
+ pgd_k = init_mm.pgd + offset;
+
+ if (!pgd_present(*pgd)) {
+ if (!pgd_present(*pgd_k))
+ goto bad_area_nosemaphore;
+ pgd_val(*pgd) = pgd_val(*pgd_k);
+ return;
+ }
+
+ pmd = pmd_offset(pgd, address);
+ pmd_k = pmd_offset(pgd_k, address);
+
+ if (pmd_present(*pmd) || !pmd_present(*pmd_k))
+ goto bad_area_nosemaphore;
+ *pmd = *pmd_k;
+ return;
+ }
+}
+
+asmlinkage void do_sun4c_fault(struct pt_regs *regs, int text_fault, int write,
+ unsigned long address)
+{
+ extern void sun4c_update_mmu_cache(struct vm_area_struct *,
+ unsigned long,pte_t);
+ extern pte_t *sun4c_pte_offset_kernel(pmd_t *,unsigned long);
+ struct task_struct *tsk = current;
+ struct mm_struct *mm = tsk->mm;
+ pgd_t *pgdp;
+ pte_t *ptep;
+
+ if (text_fault) {
+ address = regs->pc;
+ } else if (!write &&
+ !(regs->psr & PSR_PS)) {
+ unsigned int insn, __user *ip;
+
+ ip = (unsigned int __user *)regs->pc;
+ if (!get_user(insn, ip)) {
+ if ((insn & 0xc1680000) == 0xc0680000)
+ write = 1;
+ }
+ }
+
+ if (!mm) {
+ /* We are oopsing. */
+ do_sparc_fault(regs, text_fault, write, address);
+ BUG(); /* P3 Oops already, you bitch */
+ }
+
+ pgdp = pgd_offset(mm, address);
+ ptep = sun4c_pte_offset_kernel((pmd_t *) pgdp, address);
+
+ if (pgd_val(*pgdp)) {
+ if (write) {
+ if ((pte_val(*ptep) & (_SUN4C_PAGE_WRITE|_SUN4C_PAGE_PRESENT))
+ == (_SUN4C_PAGE_WRITE|_SUN4C_PAGE_PRESENT)) {
+ unsigned long flags;
+
+ *ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_ACCESSED |
+ _SUN4C_PAGE_MODIFIED |
+ _SUN4C_PAGE_VALID |
+ _SUN4C_PAGE_DIRTY);
+
+ local_irq_save(flags);
+ if (sun4c_get_segmap(address) != invalid_segment) {
+ sun4c_put_pte(address, pte_val(*ptep));
+ local_irq_restore(flags);
+ return;
+ }
+ local_irq_restore(flags);
+ }
+ } else {
+ if ((pte_val(*ptep) & (_SUN4C_PAGE_READ|_SUN4C_PAGE_PRESENT))
+ == (_SUN4C_PAGE_READ|_SUN4C_PAGE_PRESENT)) {
+ unsigned long flags;
+
+ *ptep = __pte(pte_val(*ptep) | _SUN4C_PAGE_ACCESSED |
+ _SUN4C_PAGE_VALID);
+
+ local_irq_save(flags);
+ if (sun4c_get_segmap(address) != invalid_segment) {
+ sun4c_put_pte(address, pte_val(*ptep));
+ local_irq_restore(flags);
+ return;
+ }
+ local_irq_restore(flags);
+ }
+ }
+ }
+
+ /* This conditional is 'interesting'. */
+ if (pgd_val(*pgdp) && !(write && !(pte_val(*ptep) & _SUN4C_PAGE_WRITE))
+ && (pte_val(*ptep) & _SUN4C_PAGE_VALID))
+ /* Note: It is safe to not grab the MMAP semaphore here because
+ * we know that update_mmu_cache() will not sleep for
+ * any reason (at least not in the current implementation)
+ * and therefore there is no danger of another thread getting
+ * on the CPU and doing a shrink_mmap() on this vma.
+ */
+ sun4c_update_mmu_cache (find_vma(current->mm, address), address,
+ *ptep);
+ else
+ do_sparc_fault(regs, text_fault, write, address);
+}
+
+/* This always deals with user addresses. */
+inline void force_user_fault(unsigned long address, int write)
+{
+ struct vm_area_struct *vma;
+ struct task_struct *tsk = current;
+ struct mm_struct *mm = tsk->mm;
+ siginfo_t info;
+
+ info.si_code = SEGV_MAPERR;
+
+#if 0
+ printk("wf<pid=%d,wr=%d,addr=%08lx>\n",
+ tsk->pid, write, address);
+#endif
+ down_read(&mm->mmap_sem);
+ vma = find_vma(mm, address);
+ if(!vma)
+ goto bad_area;
+ if(vma->vm_start <= address)
+ goto good_area;
+ if(!(vma->vm_flags & VM_GROWSDOWN))
+ goto bad_area;
+ if(expand_stack(vma, address))
+ goto bad_area;
+good_area:
+ info.si_code = SEGV_ACCERR;
+ if(write) {
+ if(!(vma->vm_flags & VM_WRITE))
+ goto bad_area;
+ } else {
+ if(!(vma->vm_flags & (VM_READ | VM_EXEC)))
+ goto bad_area;
+ }
+ switch (handle_mm_fault(mm, vma, address, write)) {
+ case VM_FAULT_SIGBUS:
+ case VM_FAULT_OOM:
+ goto do_sigbus;
+ }
+ up_read(&mm->mmap_sem);
+ return;
+bad_area:
+ up_read(&mm->mmap_sem);
+#if 0
+ printk("Window whee %s [%d]: segfaults at %08lx\n",
+ tsk->comm,