X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;ds=inline;f=arch%2Fsparc64%2Fmm%2Ffault.c;h=49c3dd29a5b69ed39ed1b7fe73c3a22b0221c689;hb=9bf4aaab3e101692164d49b7ca357651eb691cb6;hp=348f206f0663944b911e8edb52463b04e3f707a4;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c index 348f206f0..49c3dd29a 100644 --- a/arch/sparc64/mm/fault.c +++ b/arch/sparc64/mm/fault.c @@ -207,14 +207,21 @@ outret: return insn; } -static void do_fault_siginfo(int code, int sig, unsigned long address) +extern unsigned long compute_effective_address(struct pt_regs *, unsigned int, unsigned int); + +static void do_fault_siginfo(int code, int sig, struct pt_regs *regs, + unsigned int insn, int fault_code) { siginfo_t info; info.si_code = code; info.si_signo = sig; info.si_errno = 0; - info.si_addr = (void *) address; + if (fault_code & FAULT_CODE_ITLB) + info.si_addr = (void __user *) regs->tpc; + else + info.si_addr = (void __user *) + compute_effective_address(regs, insn, 0); info.si_trapno = 0; force_sig_info(sig, &info, current); } @@ -250,7 +257,7 @@ static void do_kernel_fault(struct pt_regs *regs, int si_code, int fault_code, * in that case. */ - if (!(fault_code & FAULT_CODE_WRITE) && + if (!(fault_code & (FAULT_CODE_WRITE|FAULT_CODE_ITLB)) && (insn & 0xc0800000) == 0xc0800000) { if (insn & 0x2000) asi = (regs->tstate >> 24); @@ -295,7 +302,7 @@ static void do_kernel_fault(struct pt_regs *regs, int si_code, int fault_code, /* The si_code was set to make clear whether * this was a SEGV_MAPERR or SEGV_ACCERR fault. */ - do_fault_siginfo(si_code, SIGSEGV, address); + do_fault_siginfo(si_code, SIGSEGV, regs, insn, fault_code); return; } @@ -401,6 +408,16 @@ continue_fault: */ good_area: si_code = SEGV_ACCERR; + + /* If we took a ITLB miss on a non-executable page, catch + * that here. + */ + if ((fault_code & FAULT_CODE_ITLB) && !(vma->vm_flags & VM_EXEC)) { + BUG_ON(address != regs->tpc); + BUG_ON(regs->tstate & TSTATE_PRIV); + goto bad_area; + } + if (fault_code & FAULT_CODE_WRITE) { if (!(vma->vm_flags & VM_WRITE)) goto bad_area; @@ -411,7 +428,8 @@ good_area: if (tlb_type == spitfire && (vma->vm_flags & VM_EXEC) != 0 && vma->vm_file != NULL) - set_thread_flag(TIF_BLKCOMMIT); + set_thread_fault_code(fault_code | + FAULT_CODE_BLKCOMMIT); } else { /* Allow reads even for write-only mappings */ if (!(vma->vm_flags & (VM_READ | VM_EXEC))) @@ -471,7 +489,7 @@ do_sigbus: * Send a sigbus, regardless of whether we were in kernel * or user mode. */ - do_fault_siginfo(BUS_ADRERR, SIGBUS, address); + do_fault_siginfo(BUS_ADRERR, SIGBUS, regs, insn, fault_code); /* Kernel mode? Handle exceptions or die */ if (regs->tstate & TSTATE_PRIV) @@ -480,6 +498,5 @@ do_sigbus: fault_done: /* These values are no longer needed, clear them. */ set_thread_fault_code(0); - clear_thread_flag(TIF_BLKCOMMIT); current_thread_info()->fault_address = 0; }