VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[linux-2.6.git] / arch / sparc64 / mm / fault.c
index 348f206..49c3dd2 100644 (file)
@@ -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;
 }