OSDN Git Service

arm64: Make USER_DS an inclusive limit
[android-x86/kernel.git] / arch / arm64 / mm / fault.c
index 0f87883..4df70c9 100644 (file)
 #include <asm/pgtable.h>
 #include <asm/tlbflush.h>
 
-static const char *fault_name(unsigned int esr);
+struct fault_info {
+       int     (*fn)(unsigned long addr, unsigned int esr,
+                     struct pt_regs *regs);
+       int     sig;
+       int     code;
+       const char *name;
+};
+
+static const struct fault_info fault_info[];
+
+static inline const struct fault_info *esr_to_fault_info(unsigned int esr)
+{
+       return fault_info + (esr & 63);
+}
 
 #ifdef CONFIG_KPROBES
 static inline int notify_page_fault(struct pt_regs *regs, unsigned int esr)
@@ -88,21 +101,21 @@ void show_pte(struct mm_struct *mm, unsigned long addr)
                        break;
 
                pud = pud_offset(pgd, addr);
-               printk(", *pud=%016llx", pud_val(*pud));
+               pr_cont(", *pud=%016llx", pud_val(*pud));
                if (pud_none(*pud) || pud_bad(*pud))
                        break;
 
                pmd = pmd_offset(pud, addr);
-               printk(", *pmd=%016llx", pmd_val(*pmd));
+               pr_cont(", *pmd=%016llx", pmd_val(*pmd));
                if (pmd_none(*pmd) || pmd_bad(*pmd))
                        break;
 
                pte = pte_offset_map(pmd, addr);
-               printk(", *pte=%016llx", pte_val(*pte));
+               pr_cont(", *pte=%016llx", pte_val(*pte));
                pte_unmap(pte);
        } while(0);
 
-       printk("\n");
+       pr_cont("\n");
 }
 
 #ifdef CONFIG_ARM64_HW_AFDBM
@@ -196,10 +209,12 @@ static void __do_user_fault(struct task_struct *tsk, unsigned long addr,
                            struct pt_regs *regs)
 {
        struct siginfo si;
+       const struct fault_info *inf;
 
        if (unhandled_signal(tsk, sig) && show_unhandled_signals_ratelimited()) {
+               inf = esr_to_fault_info(esr);
                pr_info("%s[%d]: unhandled %s (%d) at 0x%08lx, esr 0x%03x\n",
-                       tsk->comm, task_pid_nr(tsk), fault_name(esr), sig,
+                       tsk->comm, task_pid_nr(tsk), inf->name, sig,
                        addr, esr);
                show_pte(tsk->mm, addr);
                show_regs(regs);
@@ -218,14 +233,16 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re
 {
        struct task_struct *tsk = current;
        struct mm_struct *mm = tsk->active_mm;
+       const struct fault_info *inf;
 
        /*
         * If we are in kernel mode at this point, we have no context to
         * handle this fault with.
         */
-       if (user_mode(regs))
-               __do_user_fault(tsk, addr, esr, SIGSEGV, SEGV_MAPERR, regs);
-       else
+       if (user_mode(regs)) {
+               inf = esr_to_fault_info(esr);
+               __do_user_fault(tsk, addr, esr, inf->sig, inf->code, regs);
+       } else
                __do_kernel_fault(mm, addr, esr, regs);
 }
 
@@ -315,7 +332,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
                mm_flags |= FAULT_FLAG_WRITE;
        }
 
-       if (is_permission_fault(esr) && (addr < USER_DS)) {
+       if (is_permission_fault(esr) && (addr < TASK_SIZE)) {
                /* regs->orig_addr_limit may be 0 if we entered from EL0 */
                if (regs->orig_addr_limit == KERNEL_DS)
                        die("Accessing user space memory with fs=KERNEL_DS", regs, esr);
@@ -356,8 +373,11 @@ retry:
         * signal first. We do not need to release the mmap_sem because it
         * would already be released in __lock_page_or_retry in mm/filemap.c.
         */
-       if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
+       if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) {
+               if (!user_mode(regs))
+                       goto no_context;
                return 0;
+       }
 
        /*
         * Major/minor page fault accounting is only done on the initial
@@ -481,12 +501,7 @@ static int do_bad(unsigned long addr, unsigned int esr, struct pt_regs *regs)
        return 1;
 }
 
-static const struct fault_info {
-       int     (*fn)(unsigned long addr, unsigned int esr, struct pt_regs *regs);
-       int     sig;
-       int     code;
-       const char *name;
-} fault_info[] = {
+static const struct fault_info fault_info[] = {
        { do_bad,               SIGBUS,  0,             "ttbr address size fault"       },
        { do_bad,               SIGBUS,  0,             "level 1 address size fault"    },
        { do_bad,               SIGBUS,  0,             "level 2 address size fault"    },
@@ -494,7 +509,7 @@ static const struct fault_info {
        { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 0 translation fault"     },
        { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 1 translation fault"     },
        { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 2 translation fault"     },
-       { do_page_fault,        SIGSEGV, SEGV_MAPERR,   "level 3 translation fault"     },
+       { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 3 translation fault"     },
        { do_bad,               SIGBUS,  0,             "unknown 8"                     },
        { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 1 access flag fault"     },
        { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 2 access flag fault"     },
@@ -553,19 +568,13 @@ static const struct fault_info {
        { do_bad,               SIGBUS,  0,             "unknown 63"                    },
 };
 
-static const char *fault_name(unsigned int esr)
-{
-       const struct fault_info *inf = fault_info + (esr & 63);
-       return inf->name;
-}
-
 /*
  * Dispatch a data abort to the relevant handler.
  */
 asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr,
                                         struct pt_regs *regs)
 {
-       const struct fault_info *inf = fault_info + (esr & 63);
+       const struct fault_info *inf = esr_to_fault_info(esr);
        struct siginfo info;
 
        if (!inf->fn(addr, esr, regs))