sh: Support thread fault code encoding.
authorPaul Mundt <lethal@linux-sh.org>
Mon, 14 May 2012 05:57:28 +0000 (14:57 +0900)
committerPaul Mundt <lethal@linux-sh.org>
Mon, 14 May 2012 05:57:28 +0000 (14:57 +0900)
This provides a simple interface modelled after sparc64/m32r to encode
the error code in the upper byte of thread_info for finer-grained
handling in the page fault path.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>
arch/sh/include/asm/thread_info.h
arch/sh/kernel/cpu/sh3/entry.S
arch/sh/mm/fault_32.c

index 20ee40af16e9b5fae4b6bf7e4f29ba27c6fe698c..25a13e534ffe093e8bf09c68b14b46538bdbee0f 100644 (file)
  *  - Incorporating suggestions made by Linus Torvalds and Dave Miller
  */
 #ifdef __KERNEL__
+
 #include <asm/page.h>
 
+/*
+ * Page fault error code bits
+ */
+#define FAULT_CODE_WRITE       (1 << 0)        /* write access */
+#define FAULT_CODE_INITIAL     (1 << 1)        /* initial page write */
+#define FAULT_CODE_ITLB                (1 << 2)        /* ITLB miss */
+#define FAULT_CODE_PROT                (1 << 3)        /* protection fault */
+#define FAULT_CODE_USER                (1 << 4)        /* user-mode access */
+
 #ifndef __ASSEMBLY__
 #include <asm/processor.h>
 
@@ -107,10 +117,13 @@ extern void init_thread_xstate(void);
 #endif /* __ASSEMBLY__ */
 
 /*
- * thread information flags
- * - these are process state flags that various assembly files may need to access
- * - pending work-to-be-done flags are in LSW
- * - other flags in MSW
+ * Thread information flags
+ *
+ * - Limited to 24 bits, upper byte used for fault code encoding.
+ *
+ * - _TIF_ALLWORK_MASK and _TIF_WORK_MASK need to fit within 2 bytes, or
+ *   we blow the tst immediate size constraints and need to fix up
+ *   arch/sh/kernel/entry-common.S.
  */
 #define TIF_SYSCALL_TRACE      0       /* syscall trace active */
 #define TIF_SIGPENDING         1       /* signal pending */
@@ -133,12 +146,6 @@ extern void init_thread_xstate(void);
 #define _TIF_SYSCALL_TRACEPOINT        (1 << TIF_SYSCALL_TRACEPOINT)
 #define _TIF_POLLING_NRFLAG    (1 << TIF_POLLING_NRFLAG)
 
-/*
- * _TIF_ALLWORK_MASK and _TIF_WORK_MASK need to fit within 2 bytes, or we
- * blow the tst immediate size constraints and need to fix up
- * arch/sh/kernel/entry-common.S.
- */
-
 /* work to do in syscall trace */
 #define _TIF_WORK_SYSCALL_MASK (_TIF_SYSCALL_TRACE | _TIF_SINGLESTEP | \
                                 _TIF_SYSCALL_AUDIT | _TIF_SECCOMP    | \
@@ -165,6 +172,7 @@ extern void init_thread_xstate(void);
 #define TS_USEDFPU             0x0002  /* FPU used by this task this quantum */
 
 #ifndef __ASSEMBLY__
+
 #define HAVE_SET_RESTORE_SIGMASK       1
 static inline void set_restore_sigmask(void)
 {
@@ -172,6 +180,24 @@ static inline void set_restore_sigmask(void)
        ti->status |= TS_RESTORE_SIGMASK;
        set_bit(TIF_SIGPENDING, (unsigned long *)&ti->flags);
 }
+
+#define TI_FLAG_FAULT_CODE_SHIFT       24
+
+/*
+ * Additional thread flag encoding
+ */
+static inline void set_thread_fault_code(unsigned int val)
+{
+       struct thread_info *ti = current_thread_info();
+       ti->flags = (ti->flags & (~0 >> (32 - TI_FLAG_FAULT_CODE_SHIFT)))
+               | (val << TI_FLAG_FAULT_CODE_SHIFT);
+}
+
+static inline unsigned int get_thread_fault_code(void)
+{
+       struct thread_info *ti = current_thread_info();
+       return ti->flags >> TI_FLAG_FAULT_CODE_SHIFT;
+}
 #endif /* !__ASSEMBLY__ */
 
 #endif /* __KERNEL__ */
index f6a389c996cbbb16ce9b8cf5daaf45b34db16188..262db6ec067ba0716b3511834ffd7f41eab2fde6 100644 (file)
@@ -2,7 +2,7 @@
  * arch/sh/kernel/cpu/sh3/entry.S
  *
  *  Copyright (C) 1999, 2000, 2002  Niibe Yutaka
- *  Copyright (C) 2003 - 2006  Paul Mundt
+ *  Copyright (C) 2003 - 2012  Paul Mundt
  *
  * 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
@@ -17,6 +17,7 @@
 #include <cpu/mmu_context.h>
 #include <asm/page.h>
 #include <asm/cache.h>
+#include <asm/thread_info.h>
 
 ! NOTE:
 ! GNU as (as of 2.9.1) changes bf/s into bt/s and bra, when the address
@@ -114,22 +115,22 @@ ENTRY(tlb_miss_load)
        .align  2
 ENTRY(tlb_miss_store)
        bra     call_handle_tlbmiss
-        mov    #1, r5
+        mov    #FAULT_CODE_WRITE, r5
 
        .align  2
 ENTRY(initial_page_write)
        bra     call_handle_tlbmiss
-        mov    #2, r5
+        mov    #FAULT_CODE_INITIAL, r5
 
        .align  2
 ENTRY(tlb_protection_violation_load)
        bra     call_do_page_fault
-        mov    #0, r5
+        mov    #FAULT_CODE_PROT, r5
 
        .align  2
 ENTRY(tlb_protection_violation_store)
        bra     call_do_page_fault
-        mov    #1, r5
+        mov    #(FAULT_CODE_PROT | FAULT_CODE_WRITE), r5
 
 call_handle_tlbmiss:
        mov.l   1f, r0
index 889e83b5ff22801396e8426388d2203bf484223a..a469b95e88fba8af16dc1d572c63b4e88aa7fbb4 100644 (file)
@@ -211,7 +211,7 @@ show_fault_oops(struct pt_regs *regs, unsigned long address)
 }
 
 static noinline void
-no_context(struct pt_regs *regs, unsigned long writeaccess,
+no_context(struct pt_regs *regs, unsigned long error_code,
           unsigned long address)
 {
        /* Are we prepared to handle this kernel fault?  */
@@ -229,13 +229,13 @@ no_context(struct pt_regs *regs, unsigned long writeaccess,
 
        show_fault_oops(regs, address);
 
-       die("Oops", regs, writeaccess);
+       die("Oops", regs, error_code);
        bust_spinlocks(0);
        do_exit(SIGKILL);
 }
 
 static void
-__bad_area_nosemaphore(struct pt_regs *regs, unsigned long writeaccess,
+__bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,
                       unsigned long address, int si_code)
 {
        struct task_struct *tsk = current;
@@ -252,18 +252,18 @@ __bad_area_nosemaphore(struct pt_regs *regs, unsigned long writeaccess,
                return;
        }
 
-       no_context(regs, writeaccess, address);
+       no_context(regs, error_code, address);
 }
 
 static noinline void
-bad_area_nosemaphore(struct pt_regs *regs, unsigned long writeaccess,
+bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,
                     unsigned long address)
 {
-       __bad_area_nosemaphore(regs, writeaccess, address, SEGV_MAPERR);
+       __bad_area_nosemaphore(regs, error_code, address, SEGV_MAPERR);
 }
 
 static void
-__bad_area(struct pt_regs *regs, unsigned long writeaccess,
+__bad_area(struct pt_regs *regs, unsigned long error_code,
           unsigned long address, int si_code)
 {
        struct mm_struct *mm = current->mm;
@@ -274,20 +274,20 @@ __bad_area(struct pt_regs *regs, unsigned long writeaccess,
         */
        up_read(&mm->mmap_sem);
 
-       __bad_area_nosemaphore(regs, writeaccess, address, si_code);
+       __bad_area_nosemaphore(regs, error_code, address, si_code);
 }
 
 static noinline void
-bad_area(struct pt_regs *regs, unsigned long writeaccess, unsigned long address)
+bad_area(struct pt_regs *regs, unsigned long error_code, unsigned long address)
 {
-       __bad_area(regs, writeaccess, address, SEGV_MAPERR);
+       __bad_area(regs, error_code, address, SEGV_MAPERR);
 }
 
 static noinline void
-bad_area_access_error(struct pt_regs *regs, unsigned long writeaccess,
+bad_area_access_error(struct pt_regs *regs, unsigned long error_code,
                      unsigned long address)
 {
-       __bad_area(regs, writeaccess, address, SEGV_ACCERR);
+       __bad_area(regs, error_code, address, SEGV_ACCERR);
 }
 
 static void out_of_memory(void)
@@ -302,7 +302,7 @@ static void out_of_memory(void)
 }
 
 static void
-do_sigbus(struct pt_regs *regs, unsigned long writeaccess, unsigned long address)
+do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address)
 {
        struct task_struct *tsk = current;
        struct mm_struct *mm = tsk->mm;
@@ -311,13 +311,13 @@ do_sigbus(struct pt_regs *regs, unsigned long writeaccess, unsigned long address
 
        /* Kernel mode? Handle exceptions or die: */
        if (!user_mode(regs))
-               no_context(regs, writeaccess, address);
+               no_context(regs, error_code, address);
 
        force_sig_info_fault(SIGBUS, BUS_ADRERR, address, tsk);
 }
 
 static noinline int
-mm_fault_error(struct pt_regs *regs, unsigned long writeaccess,
+mm_fault_error(struct pt_regs *regs, unsigned long error_code,
               unsigned long address, unsigned int fault)
 {
        /*
@@ -328,7 +328,7 @@ mm_fault_error(struct pt_regs *regs, unsigned long writeaccess,
                if (!(fault & VM_FAULT_RETRY))
                        up_read(&current->mm->mmap_sem);
                if (!user_mode(regs))
-                       no_context(regs, writeaccess, address);
+                       no_context(regs, error_code, address);
                return 1;
        }
 
@@ -339,14 +339,14 @@ mm_fault_error(struct pt_regs *regs, unsigned long writeaccess,
                /* Kernel mode? Handle exceptions or die: */
                if (!user_mode(regs)) {
                        up_read(&current->mm->mmap_sem);
-                       no_context(regs, writeaccess, address);
+                       no_context(regs, error_code, address);
                        return 1;
                }
 
                out_of_memory();
        } else {
                if (fault & VM_FAULT_SIGBUS)
-                       do_sigbus(regs, writeaccess, address);
+                       do_sigbus(regs, error_code, address);
                else
                        BUG();
        }
@@ -381,7 +381,7 @@ static int fault_in_kernel_space(unsigned long address)
  * routines.
  */
 asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
-                                       unsigned long writeaccess,
+                                       unsigned long error_code,
                                        unsigned long address)
 {
        unsigned long vec;
@@ -389,8 +389,9 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
        struct mm_struct *mm;
        struct vm_area_struct * vma;
        int fault;
+       int write = error_code & FAULT_CODE_WRITE;
        unsigned int flags = (FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE |
-                             (writeaccess ? FAULT_FLAG_WRITE : 0));
+                             (write ? FAULT_FLAG_WRITE : 0));
 
        tsk = current;
        mm = tsk->mm;
@@ -411,7 +412,7 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
                if (notify_page_fault(regs, vec))
                        return;
 
-               bad_area_nosemaphore(regs, writeaccess, address);
+               bad_area_nosemaphore(regs, error_code, address);
                return;
        }
 
@@ -429,7 +430,7 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
         * in an atomic region then we must not take the fault:
         */
        if (unlikely(in_atomic() || !mm)) {
-               bad_area_nosemaphore(regs, writeaccess, address);
+               bad_area_nosemaphore(regs, error_code, address);
                return;
        }
 
@@ -438,17 +439,17 @@ retry:
 
        vma = find_vma(mm, address);
        if (unlikely(!vma)) {
-               bad_area(regs, writeaccess, address);
+               bad_area(regs, error_code, address);
                return;
        }
        if (likely(vma->vm_start <= address))
                goto good_area;
        if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) {
-               bad_area(regs, writeaccess, address);
+               bad_area(regs, error_code, address);
                return;
        }
        if (unlikely(expand_stack(vma, address))) {
-               bad_area(regs, writeaccess, address);
+               bad_area(regs, error_code, address);
                return;
        }
 
@@ -457,11 +458,13 @@ retry:
         * we can handle it..
         */
 good_area:
-       if (unlikely(access_error(writeaccess, vma))) {
-               bad_area_access_error(regs, writeaccess, address);
+       if (unlikely(access_error(error_code, vma))) {
+               bad_area_access_error(regs, error_code, address);
                return;
        }
 
+       set_thread_fault_code(error_code);
+
        /*
         * If for any reason at all we couldn't handle the fault,
         * make sure we exit gracefully rather than endlessly redo
@@ -470,7 +473,7 @@ good_area:
        fault = handle_mm_fault(mm, vma, address, flags);
 
        if (unlikely(fault & (VM_FAULT_RETRY | VM_FAULT_ERROR)))
-               if (mm_fault_error(regs, writeaccess, address, fault))
+               if (mm_fault_error(regs, error_code, address, fault))
                        return;
 
        if (flags & FAULT_FLAG_ALLOW_RETRY) {
@@ -502,7 +505,7 @@ good_area:
  * Called with interrupts disabled.
  */
 asmlinkage int __kprobes
-handle_tlbmiss(struct pt_regs *regs, unsigned long writeaccess,
+handle_tlbmiss(struct pt_regs *regs, unsigned long error_code,
               unsigned long address)
 {
        pgd_t *pgd;
@@ -535,10 +538,10 @@ handle_tlbmiss(struct pt_regs *regs, unsigned long writeaccess,
        entry = *pte;
        if (unlikely(pte_none(entry) || pte_not_present(entry)))
                return 1;
-       if (unlikely(writeaccess && !pte_write(entry)))
+       if (unlikely(error_code && !pte_write(entry)))
                return 1;
 
-       if (writeaccess)
+       if (error_code)
                entry = pte_mkdirty(entry);
        entry = pte_mkyoung(entry);
 
@@ -550,10 +553,11 @@ handle_tlbmiss(struct pt_regs *regs, unsigned long writeaccess,
         * the case of an initial page write exception, so we need to
         * flush it in order to avoid potential TLB entry duplication.
         */
-       if (writeaccess == 2)
+       if (error_code == FAULT_CODE_INITIAL)
                local_flush_tlb_one(get_asid(), address & PAGE_MASK);
 #endif
 
+       set_thread_fault_code(error_code);
        update_mmu_cache(NULL, address, pte);
 
        return 0;