ARM64: mm: HugeTLB support.
[firefly-linux-kernel-4.4.55.git] / arch / arm64 / mm / fault.c
index 1426468b77f3bb7b6df96d604851449db13ed0da..6c8ba25bf6bb39eed908ea23edb05c9bb605fed2 100644 (file)
@@ -152,25 +152,8 @@ void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *regs)
 #define ESR_CM                 (1 << 8)
 #define ESR_LNX_EXEC           (1 << 24)
 
-/*
- * Check that the permissions on the VMA allow for the fault which occurred.
- * If we encountered a write fault, we must have write permission, otherwise
- * we allow any permission.
- */
-static inline bool access_error(unsigned int esr, struct vm_area_struct *vma)
-{
-       unsigned int mask = VM_READ | VM_WRITE | VM_EXEC;
-
-       if (esr & ESR_WRITE)
-               mask = VM_WRITE;
-       if (esr & ESR_LNX_EXEC)
-               mask = VM_EXEC;
-
-       return vma->vm_flags & mask ? false : true;
-}
-
 static int __do_page_fault(struct mm_struct *mm, unsigned long addr,
-                          unsigned int esr, unsigned int flags,
+                          unsigned int mm_flags, unsigned long vm_flags,
                           struct task_struct *tsk)
 {
        struct vm_area_struct *vma;
@@ -188,12 +171,17 @@ static int __do_page_fault(struct mm_struct *mm, unsigned long addr,
         * it.
         */
 good_area:
-       if (access_error(esr, vma)) {
+       /*
+        * Check that the permissions on the VMA allow for the fault which
+        * occurred. If we encountered a write or exec fault, we must have
+        * appropriate permissions, otherwise we allow any permission.
+        */
+       if (!(vma->vm_flags & vm_flags)) {
                fault = VM_FAULT_BADACCESS;
                goto out;
        }
 
-       return handle_mm_fault(mm, vma, addr & PAGE_MASK, flags);
+       return handle_mm_fault(mm, vma, addr & PAGE_MASK, mm_flags);
 
 check_stack:
        if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr))
@@ -208,9 +196,15 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
        struct task_struct *tsk;
        struct mm_struct *mm;
        int fault, sig, code;
-       bool write = (esr & ESR_WRITE) && !(esr & ESR_CM);
-       unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE |
-               (write ? FAULT_FLAG_WRITE : 0);
+       unsigned long vm_flags = VM_READ | VM_WRITE | VM_EXEC;
+       unsigned int mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
+
+       if (esr & ESR_LNX_EXEC) {
+               vm_flags = VM_EXEC;
+       } else if ((esr & ESR_WRITE) && !(esr & ESR_CM)) {
+               vm_flags = VM_WRITE;
+               mm_flags |= FAULT_FLAG_WRITE;
+       }
 
        tsk = current;
        mm  = tsk->mm;
@@ -248,7 +242,7 @@ retry:
 #endif
        }
 
-       fault = __do_page_fault(mm, addr, esr, flags, tsk);
+       fault = __do_page_fault(mm, addr, mm_flags, vm_flags, tsk);
 
        /*
         * If we need to retry but a fatal signal is pending, handle the
@@ -265,7 +259,7 @@ retry:
         */
 
        perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr);
-       if (flags & FAULT_FLAG_ALLOW_RETRY) {
+       if (mm_flags & FAULT_FLAG_ALLOW_RETRY) {
                if (fault & VM_FAULT_MAJOR) {
                        tsk->maj_flt++;
                        perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, regs,
@@ -280,7 +274,7 @@ retry:
                         * Clear FAULT_FLAG_ALLOW_RETRY to avoid any risk of
                         * starvation.
                         */
-                       flags &= ~FAULT_FLAG_ALLOW_RETRY;
+                       mm_flags &= ~FAULT_FLAG_ALLOW_RETRY;
                        goto retry;
                }
        }
@@ -364,17 +358,6 @@ static int __kprobes do_translation_fault(unsigned long addr,
        return 0;
 }
 
-/*
- * Some section permission faults need to be handled gracefully.  They can
- * happen due to a __{get,put}_user during an oops.
- */
-static int do_sect_fault(unsigned long addr, unsigned int esr,
-                        struct pt_regs *regs)
-{
-       do_bad_area(addr, esr, regs);
-       return 0;
-}
-
 /*
  * This abort handler always returns "fault".
  */
@@ -398,12 +381,12 @@ static struct fault_info {
        { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 2 translation fault"     },
        { do_page_fault,        SIGSEGV, SEGV_MAPERR,   "level 3 translation fault"     },
        { do_bad,               SIGBUS,  0,             "reserved access flag fault"    },
-       { do_bad,               SIGSEGV, SEGV_ACCERR,   "level 1 access flag fault"     },
-       { do_bad,               SIGSEGV, SEGV_ACCERR,   "level 2 access flag fault"     },
+       { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 1 access flag fault"     },
+       { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 2 access flag fault"     },
        { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 3 access flag fault"     },
        { do_bad,               SIGBUS,  0,             "reserved permission fault"     },
-       { do_bad,               SIGSEGV, SEGV_ACCERR,   "level 1 permission fault"      },
-       { do_sect_fault,        SIGSEGV, SEGV_ACCERR,   "level 2 permission fault"      },
+       { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 1 permission fault"      },
+       { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 2 permission fault"      },
        { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 3 permission fault"      },
        { do_bad,               SIGBUS,  0,             "synchronous external abort"    },
        { do_bad,               SIGBUS,  0,             "asynchronous external abort"   },