ARC: mm: do_page_fault refactor #3: tidyup vma access permission code
[linux-2.6-microblaze.git] / arch / arc / mm / fault.c
index 6836095..8c7c81c 100644 (file)
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /* Page Fault Handling for ARC (TLB Miss / ProtV)
  *
  * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
  */
 
 #include <linux/signal.h>
@@ -67,23 +64,18 @@ void do_page_fault(unsigned long address, struct pt_regs *regs)
        struct task_struct *tsk = current;
        struct mm_struct *mm = tsk->mm;
        int si_code = SEGV_MAPERR;
-       int ret;
+       unsigned int write = 0, exec = 0, mask;
        vm_fault_t fault;
-       int write = regs->ecr_cause & ECR_C_PROTV_STORE;  /* ST/EX */
-       unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
+       unsigned int flags;
 
        /*
-        * We fault-in kernel-space virtual memory on-demand. The
-        * 'reference' page table is init_mm.pgd.
-        *
         * NOTE! We MUST NOT take any locks for this case. We may
         * be in an interrupt or a critical region, and should
         * only copy the information from the master page table,
         * nothing more.
         */
        if (address >= VMALLOC_START && !user_mode(regs)) {
-               ret = handle_kernel_vaddr_fault(address);
-               if (unlikely(ret))
+               if (unlikely(handle_kernel_vaddr_fault(address)))
                        goto no_context;
                else
                        return;
@@ -96,40 +88,41 @@ void do_page_fault(unsigned long address, struct pt_regs *regs)
        if (faulthandler_disabled() || !mm)
                goto no_context;
 
+       if (regs->ecr_cause & ECR_C_PROTV_STORE)        /* ST/EX */
+               write = 1;
+       else if ((regs->ecr_vec == ECR_V_PROTV) &&
+                (regs->ecr_cause == ECR_C_PROTV_INST_FETCH))
+               exec = 1;
+
+       flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
        if (user_mode(regs))
                flags |= FAULT_FLAG_USER;
+       if (write)
+               flags |= FAULT_FLAG_WRITE;
+
 retry:
        down_read(&mm->mmap_sem);
+
        vma = find_vma(mm, address);
        if (!vma)
                goto bad_area;
-       if (vma->vm_start <= address)
-               goto good_area;
-       if (!(vma->vm_flags & VM_GROWSDOWN))
-               goto bad_area;
-       if (expand_stack(vma, address))
-               goto bad_area;
+       if (unlikely(address < vma->vm_start)) {
+               if (!(vma->vm_flags & VM_GROWSDOWN) || expand_stack(vma, address))
+                       goto bad_area;
+       }
 
        /*
-        * Ok, we have a good vm_area for this memory access, so
-        * we can handle it..
+        * vm_area is good, now check permissions for this memory access
         */
-good_area:
-       si_code = SEGV_ACCERR;
-
-       /* Handle protection violation, execute on heap or stack */
-
-       if ((regs->ecr_vec == ECR_V_PROTV) &&
-           (regs->ecr_cause == ECR_C_PROTV_INST_FETCH))
+       mask = VM_READ;
+       if (write)
+               mask = VM_WRITE;
+       if (exec)
+               mask = VM_EXEC;
+
+       if (!(vma->vm_flags & mask)) {
+               si_code = SEGV_ACCERR;
                goto bad_area;
-
-       if (write) {
-               if (!(vma->vm_flags & VM_WRITE))
-                       goto bad_area;
-               flags |= FAULT_FLAG_WRITE;
-       } else {
-               if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
-                       goto bad_area;
        }
 
        /*