diff options
Diffstat (limited to 'arch/cris/mm/fault.c')
| -rw-r--r-- | arch/cris/mm/fault.c | 252 |
1 files changed, 89 insertions, 163 deletions
diff --git a/arch/cris/mm/fault.c b/arch/cris/mm/fault.c index 934c51078cc..1790f22e71a 100644 --- a/arch/cris/mm/fault.c +++ b/arch/cris/mm/fault.c @@ -1,141 +1,19 @@ /* - * linux/arch/cris/mm/fault.c - * - * Copyright (C) 2000, 2001 Axis Communications AB - * - * Authors: Bjorn Wesen - * - * $Log: fault.c,v $ - * Revision 1.20 2005/03/04 08:16:18 starvik - * Merge of Linux 2.6.11. - * - * Revision 1.19 2005/01/14 10:07:59 starvik - * Fixed warning. - * - * Revision 1.18 2005/01/12 08:10:14 starvik - * Readded the change of frametype when handling kernel page fault fixup - * for v10. This is necessary to avoid that the CPU remakes the faulting - * access. - * - * Revision 1.17 2005/01/11 13:53:05 starvik - * Use raw_printk. - * - * Revision 1.16 2004/12/17 11:39:41 starvik - * SMP support. - * - * Revision 1.15 2004/11/23 18:36:18 starvik - * Stack is now non-executable. - * Signal handler trampolines are placed in a reserved page mapped into all - * processes. - * - * Revision 1.14 2004/11/23 07:10:21 starvik - * Moved find_fixup_code to generic code. - * - * Revision 1.13 2004/11/23 07:00:54 starvik - * Actually use the execute permission bit in the MMU. This makes it possible - * to prevent e.g. attacks where executable code is put on the stack. - * - * Revision 1.12 2004/09/29 06:16:04 starvik - * Use instruction_pointer - * - * Revision 1.11 2004/05/14 07:58:05 starvik - * Merge of changes from 2.4 - * - * Revision 1.10 2003/10/27 14:51:24 starvik - * Removed debugcode - * - * Revision 1.9 2003/10/27 14:50:42 starvik - * Changed do_page_fault signature - * - * Revision 1.8 2003/07/04 13:02:48 tobiasa - * Moved code snippet from arch/cris/mm/fault.c that searches for fixup code - * to seperate function in arch-specific files. - * - * Revision 1.7 2003/01/22 06:48:38 starvik - * Fixed warnings issued by GCC 3.2.1 - * - * Revision 1.6 2003/01/09 14:42:52 starvik - * Merge of Linux 2.5.55 - * - * Revision 1.5 2002/12/11 14:44:48 starvik - * Extracted v10 (ETRAX 100LX) specific stuff to arch/cris/arch-v10/mm - * - * Revision 1.4 2002/11/13 15:10:28 starvik - * pte_offset has been renamed to pte_offset_kernel - * - * Revision 1.3 2002/11/05 06:45:13 starvik - * Merge of Linux 2.5.45 - * - * Revision 1.2 2001/12/18 13:35:22 bjornw - * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). - * - * Revision 1.20 2001/11/22 13:34:06 bjornw - * * Bug workaround (LX TR89): force a rerun of the whole of an interrupted - * unaligned write, because the second half of the write will be corrupted - * otherwise. Affected unaligned writes spanning not-yet mapped pages. - * * Optimization: use the wr_rd bit in R_MMU_CAUSE to know whether a miss - * was due to a read or a write (before we didn't know this until the next - * restart of the interrupted instruction, thus wasting one fault-irq) - * - * Revision 1.19 2001/11/12 19:02:10 pkj - * Fixed compiler warnings. - * - * Revision 1.18 2001/07/18 22:14:32 bjornw - * Enable interrupts in the bulk of do_page_fault - * - * Revision 1.17 2001/07/18 13:07:23 bjornw - * * Detect non-existant PTE's in vmalloc pmd synchronization - * * Remove comment about fast-paths for VMALLOC_START etc, because all that - * was totally bogus anyway it turned out :) - * * Fix detection of vmalloc-area synchronization - * * Add some comments - * - * Revision 1.16 2001/06/13 00:06:08 bjornw - * current_pgd should be volatile - * - * Revision 1.15 2001/06/13 00:02:23 bjornw - * Use a separate variable to store the current pgd to avoid races in schedule - * - * Revision 1.14 2001/05/16 17:41:07 hp - * Last comment tweak further tweaked. - * - * Revision 1.13 2001/05/15 00:58:44 hp - * Expand a bit on the comment why we compare address >= TASK_SIZE rather - * than >= VMALLOC_START. - * - * Revision 1.12 2001/04/04 10:51:14 bjornw - * mmap_sem is grabbed for reading - * - * Revision 1.11 2001/03/23 07:36:07 starvik - * Corrected according to review remarks - * - * Revision 1.10 2001/03/21 16:10:11 bjornw - * CRIS_FRAME_FIXUP not needed anymore, use FRAME_NORMAL - * - * Revision 1.9 2001/03/05 13:22:20 bjornw - * Spell-fix and fix in vmalloc_fault handling - * - * Revision 1.8 2000/11/22 14:45:31 bjornw - * * 2.4.0-test10 removed the set_pgdir instantaneous kernel global mapping - * into all processes. Instead we fill in the missing PTE entries on demand. - * - * Revision 1.7 2000/11/21 16:39:09 bjornw - * fixup switches frametype - * - * Revision 1.6 2000/11/17 16:54:08 bjornw - * More detailed siginfo reporting - * + * arch/cris/mm/fault.c * + * Copyright (C) 2000-2010 Axis Communications AB */ #include <linux/mm.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/wait.h> #include <asm/uaccess.h> +#include <arch/system.h> extern int find_fixup_code(struct pt_regs *); extern void die_if_kernel(const char *, struct pt_regs *, long); -extern int raw_printk(const char *fmt, ...); +extern void show_registers(struct pt_regs *regs); /* debug of low-level TLB reload */ #undef DEBUG @@ -151,7 +29,7 @@ extern int raw_printk(const char *fmt, ...); /* current active page directory */ -volatile DEFINE_PER_CPU(pgd_t *,current_pgd); +DEFINE_PER_CPU(pgd_t *, current_pgd); unsigned long cris_signal_return_page; /* @@ -164,8 +42,8 @@ unsigned long cris_signal_return_page; * address. * * error_code: - * bit 0 == 0 means no page found, 1 means protection fault - * bit 1 == 0 means read, 1 means write + * bit 0 == 0 means no page found, 1 means protection fault + * bit 1 == 0 means read, 1 means write * * If this routine detects a bad access, it returns 1, otherwise it * returns 0. @@ -179,10 +57,13 @@ do_page_fault(unsigned long address, struct pt_regs *regs, struct mm_struct *mm; struct vm_area_struct * vma; siginfo_t info; + int fault; + unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; - D(printk("Page fault for %lX on %X at %lX, prot %d write %d\n", - address, smp_processor_id(), instruction_pointer(regs), - protection, writeaccess)); + D(printk(KERN_DEBUG + "Page fault for %lX on %X at %lX, prot %d write %d\n", + address, smp_processor_id(), instruction_pointer(regs), + protection, writeaccess)); tsk = current; @@ -228,13 +109,16 @@ do_page_fault(unsigned long address, struct pt_regs *regs, info.si_code = SEGV_MAPERR; /* - * If we're in an interrupt or have no user - * context, we must not take the fault.. + * If we're in an interrupt or "atomic" operation or have no + * user context, we must not take the fault. */ - if (in_interrupt() || !mm) + if (in_atomic() || !mm) goto no_context; + if (user_mode(regs)) + flags |= FAULT_FLAG_USER; +retry: down_read(&mm->mmap_sem); vma = find_vma(mm, address); if (!vma) @@ -272,6 +156,7 @@ do_page_fault(unsigned long address, struct pt_regs *regs, } else if (writeaccess == 1) { if (!(vma->vm_flags & VM_WRITE)) goto bad_area; + flags |= FAULT_FLAG_WRITE; } else { if (!(vma->vm_flags & (VM_READ | VM_EXEC))) goto bad_area; @@ -283,17 +168,36 @@ do_page_fault(unsigned long address, struct pt_regs *regs, * the fault. */ - switch (handle_mm_fault(mm, vma, address, writeaccess & 1)) { - case VM_FAULT_MINOR: - tsk->min_flt++; - break; - case VM_FAULT_MAJOR: - tsk->maj_flt++; - break; - case VM_FAULT_SIGBUS: - goto do_sigbus; - default: - goto out_of_memory; + fault = handle_mm_fault(mm, vma, address, flags); + + if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) + return; + + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + goto do_sigbus; + BUG(); + } + + if (flags & FAULT_FLAG_ALLOW_RETRY) { + if (fault & VM_FAULT_MAJOR) + tsk->maj_flt++; + else + tsk->min_flt++; + if (fault & VM_FAULT_RETRY) { + flags &= ~FAULT_FLAG_ALLOW_RETRY; + flags |= FAULT_FLAG_TRIED; + + /* + * No need to up_read(&mm->mmap_sem) as we would + * have already released it in __lock_page_or_retry + * in mm/filemap.c. + */ + + goto retry; + } } up_read(&mm->mmap_sem); @@ -313,11 +217,25 @@ do_page_fault(unsigned long address, struct pt_regs *regs, /* User mode accesses just cause a SIGSEGV */ if (user_mode(regs)) { + printk(KERN_NOTICE "%s (pid %d) segfaults for page " + "address %08lx at pc %08lx\n", + tsk->comm, tsk->pid, + address, instruction_pointer(regs)); + + /* With DPG on, we've already dumped registers above. */ + DPG(if (0)) + show_registers(regs); + +#ifdef CONFIG_NO_SEGFAULT_TERMINATION + DECLARE_WAIT_QUEUE_HEAD(wq); + wait_event_interruptible(wq, 0 == 1); +#else info.si_signo = SIGSEGV; info.si_errno = 0; /* info.si_code has been set above */ info.si_addr = (void *)address; force_sig_info(SIGSEGV, &info, tsk); +#endif return; } @@ -325,8 +243,8 @@ do_page_fault(unsigned long address, struct pt_regs *regs, /* Are we prepared to handle this kernel fault? * - * (The kernel has valid exception-points in the source - * when it acesses user-memory. When it fails in one + * (The kernel has valid exception-points in the source + * when it accesses user-memory. When it fails in one * of those points, we find it in a table and do a jump * to some fixup code that loads an appropriate error * code) @@ -340,13 +258,18 @@ do_page_fault(unsigned long address, struct pt_regs *regs, * terminate things with extreme prejudice. */ - if ((unsigned long) (address) < PAGE_SIZE) - raw_printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); - else - raw_printk(KERN_ALERT "Unable to handle kernel access"); - raw_printk(" at virtual address %08lx\n",address); - - die_if_kernel("Oops", regs, (writeaccess << 1) | protection); + if (!oops_in_progress) { + oops_in_progress = 1; + if ((unsigned long) (address) < PAGE_SIZE) + printk(KERN_ALERT "Unable to handle kernel NULL " + "pointer dereference"); + else + printk(KERN_ALERT "Unable to handle kernel access" + " at virtual address %08lx\n", address); + + die_if_kernel("Oops", regs, (writeaccess << 1) | protection); + oops_in_progress = 0; + } do_exit(SIGKILL); @@ -357,10 +280,10 @@ do_page_fault(unsigned long address, struct pt_regs *regs, out_of_memory: up_read(&mm->mmap_sem); - printk("VM: killing process %s\n", tsk->comm); - if (user_mode(regs)) - do_exit(SIGKILL); - goto no_context; + if (!user_mode(regs)) + goto no_context; + pagefault_out_of_memory(); + return; do_sigbus: up_read(&mm->mmap_sem); @@ -405,8 +328,8 @@ vmalloc_fault: /* Since we're two-level, we don't need to do both * set_pgd and set_pmd (they do the same thing). If * we go three-level at some point, do the right thing - * with pgd_present and set_pgd here. - * + * with pgd_present and set_pgd here. + * * Also, since the vmalloc area is global, we don't * need to copy individual PTE's, it is enough to * copy the pgd pointer into the pte page of the @@ -446,8 +369,11 @@ int find_fixup_code(struct pt_regs *regs) { const struct exception_table_entry *fixup; + /* in case of delay slot fault (v32) */ + unsigned long ip = (instruction_pointer(regs) & ~0x1); - if ((fixup = search_exception_tables(instruction_pointer(regs))) != 0) { + fixup = search_exception_tables(ip); + if (fixup != 0) { /* Adjust the instruction pointer in the stackframe. */ instruction_pointer(regs) = fixup->fixup; arch_fixup(regs); |
