162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Page Fault Handling for ARC (TLB Miss / ProtV) 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/signal.h> 862306a36Sopenharmony_ci#include <linux/interrupt.h> 962306a36Sopenharmony_ci#include <linux/sched/signal.h> 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/ptrace.h> 1262306a36Sopenharmony_ci#include <linux/uaccess.h> 1362306a36Sopenharmony_ci#include <linux/kdebug.h> 1462306a36Sopenharmony_ci#include <linux/perf_event.h> 1562306a36Sopenharmony_ci#include <linux/mm_types.h> 1662306a36Sopenharmony_ci#include <asm/entry.h> 1762306a36Sopenharmony_ci#include <asm/mmu.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * kernel virtual address is required to implement vmalloc/pkmap/fixmap 2162306a36Sopenharmony_ci * Refer to asm/processor.h for System Memory Map 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * It simply copies the PMD entry (pointer to 2nd level page table or hugepage) 2462306a36Sopenharmony_ci * from swapper pgdir to task pgdir. The 2nd level table/page is thus shared 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_cinoinline static int handle_kernel_vaddr_fault(unsigned long address) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci /* 2962306a36Sopenharmony_ci * Synchronize this task's top level page-table 3062306a36Sopenharmony_ci * with the 'reference' page table. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci pgd_t *pgd, *pgd_k; 3362306a36Sopenharmony_ci p4d_t *p4d, *p4d_k; 3462306a36Sopenharmony_ci pud_t *pud, *pud_k; 3562306a36Sopenharmony_ci pmd_t *pmd, *pmd_k; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci pgd = pgd_offset(current->active_mm, address); 3862306a36Sopenharmony_ci pgd_k = pgd_offset_k(address); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (pgd_none (*pgd_k)) 4162306a36Sopenharmony_ci goto bad_area; 4262306a36Sopenharmony_ci if (!pgd_present(*pgd)) 4362306a36Sopenharmony_ci set_pgd(pgd, *pgd_k); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci p4d = p4d_offset(pgd, address); 4662306a36Sopenharmony_ci p4d_k = p4d_offset(pgd_k, address); 4762306a36Sopenharmony_ci if (p4d_none(*p4d_k)) 4862306a36Sopenharmony_ci goto bad_area; 4962306a36Sopenharmony_ci if (!p4d_present(*p4d)) 5062306a36Sopenharmony_ci set_p4d(p4d, *p4d_k); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci pud = pud_offset(p4d, address); 5362306a36Sopenharmony_ci pud_k = pud_offset(p4d_k, address); 5462306a36Sopenharmony_ci if (pud_none(*pud_k)) 5562306a36Sopenharmony_ci goto bad_area; 5662306a36Sopenharmony_ci if (!pud_present(*pud)) 5762306a36Sopenharmony_ci set_pud(pud, *pud_k); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci pmd = pmd_offset(pud, address); 6062306a36Sopenharmony_ci pmd_k = pmd_offset(pud_k, address); 6162306a36Sopenharmony_ci if (pmd_none(*pmd_k)) 6262306a36Sopenharmony_ci goto bad_area; 6362306a36Sopenharmony_ci if (!pmd_present(*pmd)) 6462306a36Sopenharmony_ci set_pmd(pmd, *pmd_k); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* XXX: create the TLB entry here */ 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cibad_area: 7062306a36Sopenharmony_ci return 1; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_civoid do_page_fault(unsigned long address, struct pt_regs *regs) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct vm_area_struct *vma = NULL; 7662306a36Sopenharmony_ci struct task_struct *tsk = current; 7762306a36Sopenharmony_ci struct mm_struct *mm = tsk->mm; 7862306a36Sopenharmony_ci int sig, si_code = SEGV_MAPERR; 7962306a36Sopenharmony_ci unsigned int write = 0, exec = 0, mask; 8062306a36Sopenharmony_ci vm_fault_t fault = VM_FAULT_SIGSEGV; /* handle_mm_fault() output */ 8162306a36Sopenharmony_ci unsigned int flags; /* handle_mm_fault() input */ 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* 8462306a36Sopenharmony_ci * NOTE! We MUST NOT take any locks for this case. We may 8562306a36Sopenharmony_ci * be in an interrupt or a critical region, and should 8662306a36Sopenharmony_ci * only copy the information from the master page table, 8762306a36Sopenharmony_ci * nothing more. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci if (address >= VMALLOC_START && !user_mode(regs)) { 9062306a36Sopenharmony_ci if (unlikely(handle_kernel_vaddr_fault(address))) 9162306a36Sopenharmony_ci goto no_context; 9262306a36Sopenharmony_ci else 9362306a36Sopenharmony_ci return; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* 9762306a36Sopenharmony_ci * If we're in an interrupt or have no user 9862306a36Sopenharmony_ci * context, we must not take the fault.. 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_ci if (faulthandler_disabled() || !mm) 10162306a36Sopenharmony_ci goto no_context; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (regs->ecr.cause & ECR_C_PROTV_STORE) /* ST/EX */ 10462306a36Sopenharmony_ci write = 1; 10562306a36Sopenharmony_ci else if ((regs->ecr.vec == ECR_V_PROTV) && 10662306a36Sopenharmony_ci (regs->ecr.cause == ECR_C_PROTV_INST_FETCH)) 10762306a36Sopenharmony_ci exec = 1; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci flags = FAULT_FLAG_DEFAULT; 11062306a36Sopenharmony_ci if (user_mode(regs)) 11162306a36Sopenharmony_ci flags |= FAULT_FLAG_USER; 11262306a36Sopenharmony_ci if (write) 11362306a36Sopenharmony_ci flags |= FAULT_FLAG_WRITE; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); 11662306a36Sopenharmony_ciretry: 11762306a36Sopenharmony_ci vma = lock_mm_and_find_vma(mm, address, regs); 11862306a36Sopenharmony_ci if (!vma) 11962306a36Sopenharmony_ci goto bad_area_nosemaphore; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* 12262306a36Sopenharmony_ci * vm_area is good, now check permissions for this memory access 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_ci mask = VM_READ; 12562306a36Sopenharmony_ci if (write) 12662306a36Sopenharmony_ci mask = VM_WRITE; 12762306a36Sopenharmony_ci if (exec) 12862306a36Sopenharmony_ci mask = VM_EXEC; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (!(vma->vm_flags & mask)) { 13162306a36Sopenharmony_ci si_code = SEGV_ACCERR; 13262306a36Sopenharmony_ci goto bad_area; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci fault = handle_mm_fault(vma, address, flags, regs); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* Quick path to respond to signals */ 13862306a36Sopenharmony_ci if (fault_signal_pending(fault, regs)) { 13962306a36Sopenharmony_ci if (!user_mode(regs)) 14062306a36Sopenharmony_ci goto no_context; 14162306a36Sopenharmony_ci return; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* The fault is fully completed (including releasing mmap lock) */ 14562306a36Sopenharmony_ci if (fault & VM_FAULT_COMPLETED) 14662306a36Sopenharmony_ci return; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* 14962306a36Sopenharmony_ci * Fault retry nuances, mmap_lock already relinquished by core mm 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_ci if (unlikely(fault & VM_FAULT_RETRY)) { 15262306a36Sopenharmony_ci flags |= FAULT_FLAG_TRIED; 15362306a36Sopenharmony_ci goto retry; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cibad_area: 15762306a36Sopenharmony_ci mmap_read_unlock(mm); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cibad_area_nosemaphore: 16062306a36Sopenharmony_ci /* 16162306a36Sopenharmony_ci * Major/minor page fault accounting 16262306a36Sopenharmony_ci * (in case of retry we only land here once) 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_ci if (likely(!(fault & VM_FAULT_ERROR))) 16562306a36Sopenharmony_ci /* Normal return path: fault Handled Gracefully */ 16662306a36Sopenharmony_ci return; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (!user_mode(regs)) 16962306a36Sopenharmony_ci goto no_context; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (fault & VM_FAULT_OOM) { 17262306a36Sopenharmony_ci pagefault_out_of_memory(); 17362306a36Sopenharmony_ci return; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (fault & VM_FAULT_SIGBUS) { 17762306a36Sopenharmony_ci sig = SIGBUS; 17862306a36Sopenharmony_ci si_code = BUS_ADRERR; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci else { 18162306a36Sopenharmony_ci sig = SIGSEGV; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci tsk->thread.fault_address = address; 18562306a36Sopenharmony_ci force_sig_fault(sig, si_code, (void __user *)address); 18662306a36Sopenharmony_ci return; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cino_context: 18962306a36Sopenharmony_ci if (fixup_exception(regs)) 19062306a36Sopenharmony_ci return; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci die("Oops", regs, address); 19362306a36Sopenharmony_ci} 194