summaryrefslogtreecommitdiffstats
path: root/arch/mips64/mm/fault.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips64/mm/fault.c')
-rw-r--r--arch/mips64/mm/fault.c165
1 files changed, 165 insertions, 0 deletions
diff --git a/arch/mips64/mm/fault.c b/arch/mips64/mm/fault.c
new file mode 100644
index 000000000..1e34de13c
--- /dev/null
+++ b/arch/mips64/mm/fault.c
@@ -0,0 +1,165 @@
+/* $Id$
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1995, 1996, 1997, 1998, 1999 by Ralf Baechle
+ * Copyright (C) 1999 by Silicon Graphics
+ */
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.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 <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/version.h>
+
+#include <asm/hardirq.h>
+#include <asm/pgtable.h>
+#include <asm/mmu_context.h>
+#include <asm/softirq.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#define development_version (LINUX_VERSION_CODE & 0x100)
+
+extern void die(char *, struct pt_regs *, unsigned long write);
+
+unsigned long asid_cache;
+
+/*
+ * Macro for exception fixup code to access integer registers.
+ */
+#define dpf_reg(r) (regs->regs[r])
+
+/*
+ * This routine handles page faults. It determines the address,
+ * and the problem, and then passes it off to one of the appropriate
+ * routines.
+ */
+asmlinkage void
+do_page_fault(struct pt_regs *regs, unsigned long writeaccess,
+ unsigned long address)
+{
+ struct vm_area_struct * vma;
+ struct task_struct *tsk = current;
+ struct mm_struct *mm = tsk->mm;
+ unsigned long fixup;
+
+ /*
+ * If we're in an interrupt or have no user
+ * context, we must not take the fault..
+ */
+ if (in_interrupt() || mm == &init_mm)
+ goto no_context;
+#if 0
+ printk("[%s:%d:%08lx:%ld:%08lx]\n", current->comm, current->pid,
+ address, writeaccess, regs->cp0_epc);
+#endif
+ down(&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;
+/*
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
+ */
+good_area:
+ if (writeaccess) {
+ if (!(vma->vm_flags & VM_WRITE))
+ goto bad_area;
+ } else {
+ 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.
+ */
+ if (!handle_mm_fault(tsk, vma, address, writeaccess))
+ goto do_sigbus;
+
+ up(&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(&mm->mmap_sem);
+
+ if (user_mode(regs)) {
+ tsk->tss.cp0_badvaddr = address;
+ tsk->tss.error_code = writeaccess;
+#if 0
+ printk("do_page_fault() #2: sending SIGSEGV to %s for illegal %s\n"
+ "%08lx (epc == %08lx, ra == %08lx)\n",
+ tsk->comm,
+ writeaccess ? "writeaccess to" : "readaccess from",
+ address,
+ (unsigned long) regs->cp0_epc,
+ (unsigned long) regs->regs[31]);
+#endif
+ force_sig(SIGSEGV, tsk);
+ return;
+ }
+
+no_context:
+ /* Are we prepared to handle this kernel fault? */
+ fixup = search_exception_table(regs->cp0_epc);
+ if (fixup) {
+ long new_epc;
+
+ tsk->tss.cp0_baduaddr = address;
+ new_epc = fixup_exception(dpf_reg, fixup, regs->cp0_epc);
+ if (development_version)
+ printk(KERN_DEBUG "%s: Exception at [<%lx>] (%lx)\n",
+ tsk->comm, regs->cp0_epc, new_epc);
+ regs->cp0_epc = new_epc;
+ return;
+ }
+
+ /*
+ * Oops. The kernel tried to access some bad page. We'll have to
+ * terminate things with extreme prejudice.
+ */
+ printk(KERN_ALERT "Unable to handle kernel paging request at virtual "
+ "address %08lx, epc == %08lx, ra == %08lx\n",
+ address, regs->cp0_epc, regs->regs[31]);
+ die("Oops", regs, writeaccess);
+ 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.
+ */
+do_sigbus:
+ up(&mm->mmap_sem);
+
+ /*
+ * Send a sigbus, regardless of whether we were in kernel
+ * or user mode.
+ */
+ tsk->tss.cp0_badvaddr = address;
+ force_sig(SIGBUS, tsk);
+
+ /* Kernel mode? Handle exceptions or die */
+ if (!user_mode(regs))
+ goto no_context;
+}