patch-2.3.48 linux/arch/mips64/mm/fault.c
Next file: linux/arch/mips64/mm/init.c
Previous file: linux/arch/mips64/mm/extable.c
Back to the patch index
Back to the overall index
- Lines: 185
- Date:
Thu Feb 24 22:53:35 2000
- Orig file:
v2.3.47/linux/arch/mips64/mm/fault.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.47/linux/arch/mips64/mm/fault.c linux/arch/mips64/mm/fault.c
@@ -0,0 +1,184 @@
+/* $Id: fault.c,v 1.6 2000/02/18 00:24:31 ralf Exp $
+ *
+ * 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/pgalloc.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 write, unsigned long address)
+{
+ struct vm_area_struct * vma;
+ struct task_struct *tsk = current;
+ struct mm_struct *mm = tsk->mm;
+ int si_code = SEGV_MAPERR;
+ 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, write, 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:
+ 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;
+ }
+
+ /*
+ * If for any reason at all we couldn't handle the fault,
+ * make sure we exit gracefully rather than endlessly redo
+ * the fault.
+ */
+ {
+ int fault = handle_mm_fault(tsk, vma, address, write);
+ if (fault < 0)
+ goto out_of_memory;
+ if (!fault)
+ 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)) {
+ struct siginfo si;
+ tsk->thread.cp0_badvaddr = address;
+ tsk->thread.error_code = write;
+#if 0
+ printk("do_page_fault() #2: sending SIGSEGV to %s for illegal %s\n"
+ "%08lx (epc == %08lx, ra == %08lx)\n",
+ tsk->comm,
+ write ? "write access to" : "read access from",
+ address,
+ (unsigned long) regs->cp0_epc,
+ (unsigned long) regs->regs[31]);
+#endif
+ si.si_signo = SIGSEGV;
+ si.si_code = si_code;
+ si.si_addr = (void *) address;
+ force_sig_info(SIGSEGV, &si, 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->thread.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]);
+while(1);
+ die("Oops", regs, write);
+ 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(&mm->mmap_sem);
+ printk("VM: killing process %s\n", tsk->comm);
+ if (user_mode(regs))
+ do_exit(SIGKILL);
+ goto no_context;
+
+do_sigbus:
+ up(&mm->mmap_sem);
+
+ /*
+ * Send a sigbus, regardless of whether we were in kernel
+ * or user mode.
+ */
+ tsk->thread.cp0_badvaddr = address;
+ force_sig(SIGBUS, tsk);
+
+ /* Kernel mode? Handle exceptions or die */
+ if (!user_mode(regs))
+ goto no_context;
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)