summaryrefslogtreecommitdiffstats
path: root/arch/mips/mm/fault.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/mm/fault.c')
-rw-r--r--arch/mips/mm/fault.c73
1 files changed, 50 insertions, 23 deletions
diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c
index 9256025d9..f1462eff9 100644
--- a/arch/mips/mm/fault.c
+++ b/arch/mips/mm/fault.c
@@ -1,10 +1,8 @@
/*
* arch/mips/mm/fault.c
*
- * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
- * Ported to MIPS by Ralf Baechle
+ * Copyright (C) 1995, 1996 by Ralf Baechle
*/
-#include <linux/config.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/head.h>
@@ -16,45 +14,47 @@
#include <linux/mman.h>
#include <linux/mm.h>
+#include <asm/cache.h>
#include <asm/system.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/pgtable.h>
extern void die_if_kernel(char *, struct pt_regs *, long);
/*
+ * 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)
+do_page_fault(struct pt_regs *regs, unsigned long writeaccess,
+ unsigned long address)
{
struct vm_area_struct * vma;
- unsigned long address;
-
- /* get the address */
- __asm__(".set\tmips3\n\t"
- "dmfc0\t%0,$8\n\t"
- ".set\tmips0"
- : "=r" (address));
+ struct task_struct *tsk = current;
+ struct mm_struct *mm = tsk->mm;
+ unsigned long fixup;
#if 0
- printk("do_page_fault() #1: %s %08lx (epc == %08lx)\n",
+ printk("do_page_fault() #1: %s %08lx (epc == %08lx, ra == %08lx)\n",
writeaccess ? "writeaccess to" : "readaccess from",
- address, regs->cp0_epc);
+ address, regs->cp0_epc, regs->regs[31]);
#endif
- vma = find_vma(current, address);
+ 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 (vma->vm_end - address > current->rlim[RLIMIT_STACK].rlim_cur)
+ if (expand_stack(vma, address))
goto bad_area;
- vma->vm_offset -= vma->vm_start - (address & PAGE_MASK);
- vma->vm_start = (address & PAGE_MASK);
/*
* Ok, we have a good vm_area for this memory access, so
* we can handle it..
@@ -68,6 +68,8 @@ good_area:
goto bad_area;
}
handle_mm_fault(vma, address, writeaccess);
+ up(&mm->mmap_sem);
+
return;
/*
@@ -75,18 +77,43 @@ good_area:
* Fix it, but check if it's kernel or user first..
*/
bad_area:
+ up(&mm->mmap_sem);
+ /* Did we have an exception handler installed? */
+
+ fixup = search_exception_table(regs->cp0_epc);
+ if (fixup) {
+ long new_epc;
+ new_epc = fixup_exception(dpf_reg, fixup, regs->cp0_epc);
+ printk("Taking exception at %lx (%lx)\n",
+ regs->cp0_epc, new_epc);
+ regs->cp0_epc = new_epc;
+ return;
+ }
if (user_mode(regs)) {
- current->tss.cp0_badvaddr = address;
- current->tss.error_code = writeaccess;
- send_sig(SIGSEGV, current, 1);
+ tsk->tss.cp0_badvaddr = address;
+ tsk->tss.error_code = writeaccess;
+#if 1
+ 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;
}
/*
* 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",
- address);
+ printk(KERN_ALERT "Unable to handle kernel paging request at virtual "
+#ifdef __mips64
+ "address %08lx, epc == %08Lx\n", address, regs->cp0_epc);
+#else
+ "address %08lx, epc == %016lx\n", address, regs->cp0_epc);
+#endif
die_if_kernel("Oops", regs, writeaccess);
do_exit(SIGKILL);
}