X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Farm%2Fmm%2Ffault.c;h=0e784791bd85baf1b6700cced3335cc5d7a33206;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=e25b4fd8412cb4f0c4f0c51c09ea30bc57a9c50d;hpb=f7f1b0f1e2fbadeab12d24236000e778aa9b1ead;p=linux-2.6.git diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index e25b4fd84..0e784791b 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -8,7 +8,6 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#include #include #include #include @@ -132,10 +131,11 @@ __do_user_fault(struct task_struct *tsk, unsigned long addr, force_sig_info(sig, &si, tsk); } -void -do_bad_area(struct task_struct *tsk, struct mm_struct *mm, unsigned long addr, - unsigned int fsr, struct pt_regs *regs) +void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { + struct task_struct *tsk = current; + struct mm_struct *mm = tsk->active_mm; + /* * If we are in kernel mode at this point, we * have no context to handle this fault with. @@ -171,7 +171,7 @@ good_area: if (fsr & (1 << 11)) /* write? */ mask = VM_WRITE; else - mask = VM_READ|VM_EXEC; + mask = VM_READ|VM_EXEC|VM_WRITE; fault = VM_FAULT_BADACCESS; if (!(vma->vm_flags & mask)) @@ -198,7 +198,7 @@ survive: return fault; } - if (tsk->pid != 1) + if (!is_init(tsk)) goto out; /* @@ -230,17 +230,27 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) * If we're in an interrupt or have no user * context, we must not take the fault.. */ - if (in_interrupt() || !mm) + if (in_atomic() || !mm) goto no_context; - down_read(&mm->mmap_sem); + /* + * As per x86, we may deadlock here. However, since the kernel only + * validly references user space from well defined areas of the code, + * we can bug out early if this is from code which shouldn't. + */ + if (!down_read_trylock(&mm->mmap_sem)) { + if (!user_mode(regs) && !search_exception_tables(regs->ARM_pc)) + goto no_context; + down_read(&mm->mmap_sem); + } + fault = __do_page_fault(mm, addr, fsr, tsk); up_read(&mm->mmap_sem); /* - * Handle the "normal" case first + * Handle the "normal" case first - VM_FAULT_MAJOR / VM_FAULT_MINOR */ - if (fault > 0) + if (fault >= VM_FAULT_MINOR) return 0; /* @@ -257,11 +267,12 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) * happened to us that made us unable to handle * the page fault gracefully. */ - printk("VM: killing process %s\n", tsk->comm); + printk("VM: killing process %s(%d:#%u)\n", + tsk->comm, tsk->pid, tsk->xid); do_exit(SIGKILL); return 0; - case 0: + case VM_FAULT_SIGBUS: /* * We had some memory, but were unable to * successfully fix up this page fault. @@ -310,7 +321,6 @@ static int do_translation_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { - struct task_struct *tsk; unsigned int index; pgd_t *pgd, *pgd_k; pmd_t *pmd, *pmd_k; @@ -342,9 +352,7 @@ do_translation_fault(unsigned long addr, unsigned int fsr, return 0; bad_area: - tsk = current; - - do_bad_area(tsk, tsk->active_mm, addr, fsr, regs); + do_bad_area(addr, fsr, regs); return 0; } @@ -355,8 +363,7 @@ bad_area: static int do_sect_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { - struct task_struct *tsk = current; - do_bad_area(tsk, tsk->active_mm, addr, fsr, regs); + do_bad_area(addr, fsr, regs); return 0; } @@ -372,49 +379,50 @@ do_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs) static struct fsr_info { int (*fn)(unsigned long addr, unsigned int fsr, struct pt_regs *regs); int sig; + int code; const char *name; } fsr_info[] = { /* * The following are the standard ARMv3 and ARMv4 aborts. ARMv5 * defines these to be "precise" aborts. */ - { do_bad, SIGSEGV, "vector exception" }, - { do_bad, SIGILL, "alignment exception" }, - { do_bad, SIGKILL, "terminal exception" }, - { do_bad, SIGILL, "alignment exception" }, - { do_bad, SIGBUS, "external abort on linefetch" }, - { do_translation_fault, SIGSEGV, "section translation fault" }, - { do_bad, SIGBUS, "external abort on linefetch" }, - { do_page_fault, SIGSEGV, "page translation fault" }, - { do_bad, SIGBUS, "external abort on non-linefetch" }, - { do_bad, SIGSEGV, "section domain fault" }, - { do_bad, SIGBUS, "external abort on non-linefetch" }, - { do_bad, SIGSEGV, "page domain fault" }, - { do_bad, SIGBUS, "external abort on translation" }, - { do_sect_fault, SIGSEGV, "section permission fault" }, - { do_bad, SIGBUS, "external abort on translation" }, - { do_page_fault, SIGSEGV, "page permission fault" }, + { do_bad, SIGSEGV, 0, "vector exception" }, + { do_bad, SIGILL, BUS_ADRALN, "alignment exception" }, + { do_bad, SIGKILL, 0, "terminal exception" }, + { do_bad, SIGILL, BUS_ADRALN, "alignment exception" }, + { do_bad, SIGBUS, 0, "external abort on linefetch" }, + { do_translation_fault, SIGSEGV, SEGV_MAPERR, "section translation fault" }, + { do_bad, SIGBUS, 0, "external abort on linefetch" }, + { do_page_fault, SIGSEGV, SEGV_MAPERR, "page translation fault" }, + { do_bad, SIGBUS, 0, "external abort on non-linefetch" }, + { do_bad, SIGSEGV, SEGV_ACCERR, "section domain fault" }, + { do_bad, SIGBUS, 0, "external abort on non-linefetch" }, + { do_bad, SIGSEGV, SEGV_ACCERR, "page domain fault" }, + { do_bad, SIGBUS, 0, "external abort on translation" }, + { do_sect_fault, SIGSEGV, SEGV_ACCERR, "section permission fault" }, + { do_bad, SIGBUS, 0, "external abort on translation" }, + { do_page_fault, SIGSEGV, SEGV_ACCERR, "page permission fault" }, /* * The following are "imprecise" aborts, which are signalled by bit * 10 of the FSR, and may not be recoverable. These are only * supported if the CPU abort handler supports bit 10. */ - { do_bad, SIGBUS, "unknown 16" }, - { do_bad, SIGBUS, "unknown 17" }, - { do_bad, SIGBUS, "unknown 18" }, - { do_bad, SIGBUS, "unknown 19" }, - { do_bad, SIGBUS, "lock abort" }, /* xscale */ - { do_bad, SIGBUS, "unknown 21" }, - { do_bad, SIGBUS, "imprecise external abort" }, /* xscale */ - { do_bad, SIGBUS, "unknown 23" }, - { do_bad, SIGBUS, "dcache parity error" }, /* xscale */ - { do_bad, SIGBUS, "unknown 25" }, - { do_bad, SIGBUS, "unknown 26" }, - { do_bad, SIGBUS, "unknown 27" }, - { do_bad, SIGBUS, "unknown 28" }, - { do_bad, SIGBUS, "unknown 29" }, - { do_bad, SIGBUS, "unknown 30" }, - { do_bad, SIGBUS, "unknown 31" } + { do_bad, SIGBUS, 0, "unknown 16" }, + { do_bad, SIGBUS, 0, "unknown 17" }, + { do_bad, SIGBUS, 0, "unknown 18" }, + { do_bad, SIGBUS, 0, "unknown 19" }, + { do_bad, SIGBUS, 0, "lock abort" }, /* xscale */ + { do_bad, SIGBUS, 0, "unknown 21" }, + { do_bad, SIGBUS, BUS_OBJERR, "imprecise external abort" }, /* xscale */ + { do_bad, SIGBUS, 0, "unknown 23" }, + { do_bad, SIGBUS, 0, "dcache parity error" }, /* xscale */ + { do_bad, SIGBUS, 0, "unknown 25" }, + { do_bad, SIGBUS, 0, "unknown 26" }, + { do_bad, SIGBUS, 0, "unknown 27" }, + { do_bad, SIGBUS, 0, "unknown 28" }, + { do_bad, SIGBUS, 0, "unknown 29" }, + { do_bad, SIGBUS, 0, "unknown 30" }, + { do_bad, SIGBUS, 0, "unknown 31" } }; void __init @@ -435,15 +443,19 @@ asmlinkage void do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { const struct fsr_info *inf = fsr_info + (fsr & 15) + ((fsr & (1 << 10)) >> 6); + struct siginfo info; if (!inf->fn(addr, fsr, regs)) return; printk(KERN_ALERT "Unhandled fault: %s (0x%03x) at 0x%08lx\n", inf->name, fsr, addr); - force_sig(inf->sig, current); - show_pte(current->mm, addr); - die_if_kernel("Oops", regs, 0); + + info.si_signo = inf->sig; + info.si_errno = 0; + info.si_code = inf->code; + info.si_addr = (void __user *)addr; + notify_die("", regs, &info, fsr, 0); } asmlinkage void