18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/arch/arm/mm/fault.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1995 Linus Torvalds 68c2ecf20Sopenharmony_ci * Modifications for ARM processor (c) 1995-2004 Russell King 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/extable.h> 98c2ecf20Sopenharmony_ci#include <linux/signal.h> 108c2ecf20Sopenharmony_ci#include <linux/mm.h> 118c2ecf20Sopenharmony_ci#include <linux/hardirq.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/kprobes.h> 148c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 158c2ecf20Sopenharmony_ci#include <linux/page-flags.h> 168c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 178c2ecf20Sopenharmony_ci#include <linux/sched/debug.h> 188c2ecf20Sopenharmony_ci#include <linux/highmem.h> 198c2ecf20Sopenharmony_ci#include <linux/perf_event.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <asm/system_misc.h> 228c2ecf20Sopenharmony_ci#include <asm/system_info.h> 238c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "fault.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#ifdef CONFIG_MMU 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* 308c2ecf20Sopenharmony_ci * This is useful to dump out the page tables associated with 318c2ecf20Sopenharmony_ci * 'addr' in mm 'mm'. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_civoid show_pte(const char *lvl, struct mm_struct *mm, unsigned long addr) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci pgd_t *pgd; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (!mm) 388c2ecf20Sopenharmony_ci mm = &init_mm; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci pgd = pgd_offset(mm, addr); 418c2ecf20Sopenharmony_ci printk("%s[%08lx] *pgd=%08llx", lvl, addr, (long long)pgd_val(*pgd)); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci do { 448c2ecf20Sopenharmony_ci p4d_t *p4d; 458c2ecf20Sopenharmony_ci pud_t *pud; 468c2ecf20Sopenharmony_ci pmd_t *pmd; 478c2ecf20Sopenharmony_ci pte_t *pte; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci p4d = p4d_offset(pgd, addr); 508c2ecf20Sopenharmony_ci if (p4d_none(*p4d)) 518c2ecf20Sopenharmony_ci break; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (p4d_bad(*p4d)) { 548c2ecf20Sopenharmony_ci pr_cont("(bad)"); 558c2ecf20Sopenharmony_ci break; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci pud = pud_offset(p4d, addr); 598c2ecf20Sopenharmony_ci if (PTRS_PER_PUD != 1) 608c2ecf20Sopenharmony_ci pr_cont(", *pud=%08llx", (long long)pud_val(*pud)); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (pud_none(*pud)) 638c2ecf20Sopenharmony_ci break; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (pud_bad(*pud)) { 668c2ecf20Sopenharmony_ci pr_cont("(bad)"); 678c2ecf20Sopenharmony_ci break; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci pmd = pmd_offset(pud, addr); 718c2ecf20Sopenharmony_ci if (PTRS_PER_PMD != 1) 728c2ecf20Sopenharmony_ci pr_cont(", *pmd=%08llx", (long long)pmd_val(*pmd)); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (pmd_none(*pmd)) 758c2ecf20Sopenharmony_ci break; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (pmd_bad(*pmd)) { 788c2ecf20Sopenharmony_ci pr_cont("(bad)"); 798c2ecf20Sopenharmony_ci break; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* We must not map this if we have highmem enabled */ 838c2ecf20Sopenharmony_ci if (PageHighMem(pfn_to_page(pmd_val(*pmd) >> PAGE_SHIFT))) 848c2ecf20Sopenharmony_ci break; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci pte = pte_offset_map(pmd, addr); 878c2ecf20Sopenharmony_ci pr_cont(", *pte=%08llx", (long long)pte_val(*pte)); 888c2ecf20Sopenharmony_ci#ifndef CONFIG_ARM_LPAE 898c2ecf20Sopenharmony_ci pr_cont(", *ppte=%08llx", 908c2ecf20Sopenharmony_ci (long long)pte_val(pte[PTE_HWTABLE_PTRS])); 918c2ecf20Sopenharmony_ci#endif 928c2ecf20Sopenharmony_ci pte_unmap(pte); 938c2ecf20Sopenharmony_ci } while(0); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci pr_cont("\n"); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci#else /* CONFIG_MMU */ 988c2ecf20Sopenharmony_civoid show_pte(const char *lvl, struct mm_struct *mm, unsigned long addr) 998c2ecf20Sopenharmony_ci{ } 1008c2ecf20Sopenharmony_ci#endif /* CONFIG_MMU */ 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic void die_kernel_fault(const char *msg, struct mm_struct *mm, 1038c2ecf20Sopenharmony_ci unsigned long addr, unsigned int fsr, 1048c2ecf20Sopenharmony_ci struct pt_regs *regs) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci bust_spinlocks(1); 1078c2ecf20Sopenharmony_ci pr_alert("8<--- cut here ---\n"); 1088c2ecf20Sopenharmony_ci pr_alert("Unable to handle kernel %s at virtual address %08lx\n", 1098c2ecf20Sopenharmony_ci msg, addr); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci show_pte(KERN_ALERT, mm, addr); 1128c2ecf20Sopenharmony_ci die("Oops", regs, fsr); 1138c2ecf20Sopenharmony_ci bust_spinlocks(0); 1148c2ecf20Sopenharmony_ci make_task_dead(SIGKILL); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* 1188c2ecf20Sopenharmony_ci * Oops. The kernel tried to access some page that wasn't present. 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_cistatic void 1218c2ecf20Sopenharmony_ci__do_kernel_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr, 1228c2ecf20Sopenharmony_ci struct pt_regs *regs) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci const char *msg; 1258c2ecf20Sopenharmony_ci /* 1268c2ecf20Sopenharmony_ci * Are we prepared to handle this kernel fault? 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ci if (fixup_exception(regs)) 1298c2ecf20Sopenharmony_ci return; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* 1328c2ecf20Sopenharmony_ci * No handler, we'll have to terminate things with extreme prejudice. 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_ci if (addr < PAGE_SIZE) 1358c2ecf20Sopenharmony_ci msg = "NULL pointer dereference"; 1368c2ecf20Sopenharmony_ci else 1378c2ecf20Sopenharmony_ci msg = "paging request"; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci die_kernel_fault(msg, mm, addr, fsr, regs); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci/* 1438c2ecf20Sopenharmony_ci * Something tried to access memory that isn't in our memory map.. 1448c2ecf20Sopenharmony_ci * User mode accesses just cause a SIGSEGV 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_cistatic void 1478c2ecf20Sopenharmony_ci__do_user_fault(unsigned long addr, unsigned int fsr, unsigned int sig, 1488c2ecf20Sopenharmony_ci int code, struct pt_regs *regs) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct task_struct *tsk = current; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (addr > TASK_SIZE) 1538c2ecf20Sopenharmony_ci harden_branch_predictor(); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_USER 1568c2ecf20Sopenharmony_ci if (((user_debug & UDBG_SEGV) && (sig == SIGSEGV)) || 1578c2ecf20Sopenharmony_ci ((user_debug & UDBG_BUS) && (sig == SIGBUS))) { 1588c2ecf20Sopenharmony_ci pr_err("8<--- cut here ---\n"); 1598c2ecf20Sopenharmony_ci pr_err("%s: unhandled page fault (%d) at 0x%08lx, code 0x%03x\n", 1608c2ecf20Sopenharmony_ci tsk->comm, sig, addr, fsr); 1618c2ecf20Sopenharmony_ci show_pte(KERN_ERR, tsk->mm, addr); 1628c2ecf20Sopenharmony_ci show_regs(regs); 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci#endif 1658c2ecf20Sopenharmony_ci#ifndef CONFIG_KUSER_HELPERS 1668c2ecf20Sopenharmony_ci if ((sig == SIGSEGV) && ((addr & PAGE_MASK) == 0xffff0000)) 1678c2ecf20Sopenharmony_ci printk_ratelimited(KERN_DEBUG 1688c2ecf20Sopenharmony_ci "%s: CONFIG_KUSER_HELPERS disabled at 0x%08lx\n", 1698c2ecf20Sopenharmony_ci tsk->comm, addr); 1708c2ecf20Sopenharmony_ci#endif 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci tsk->thread.address = addr; 1738c2ecf20Sopenharmony_ci tsk->thread.error_code = fsr; 1748c2ecf20Sopenharmony_ci tsk->thread.trap_no = 14; 1758c2ecf20Sopenharmony_ci force_sig_fault(sig, code, (void __user *)addr); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_civoid do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct task_struct *tsk = current; 1818c2ecf20Sopenharmony_ci struct mm_struct *mm = tsk->active_mm; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* 1848c2ecf20Sopenharmony_ci * If we are in kernel mode at this point, we 1858c2ecf20Sopenharmony_ci * have no context to handle this fault with. 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_ci if (user_mode(regs)) 1888c2ecf20Sopenharmony_ci __do_user_fault(addr, fsr, SIGSEGV, SEGV_MAPERR, regs); 1898c2ecf20Sopenharmony_ci else 1908c2ecf20Sopenharmony_ci __do_kernel_fault(mm, addr, fsr, regs); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci#ifdef CONFIG_MMU 1948c2ecf20Sopenharmony_ci#define VM_FAULT_BADMAP 0x010000 1958c2ecf20Sopenharmony_ci#define VM_FAULT_BADACCESS 0x020000 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic inline bool is_permission_fault(unsigned int fsr) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci int fs = fsr_fs(fsr); 2008c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_LPAE 2018c2ecf20Sopenharmony_ci if ((fs & FS_PERM_NOLL_MASK) == FS_PERM_NOLL) 2028c2ecf20Sopenharmony_ci return true; 2038c2ecf20Sopenharmony_ci#else 2048c2ecf20Sopenharmony_ci if (fs == FS_L1_PERM || fs == FS_L2_PERM) 2058c2ecf20Sopenharmony_ci return true; 2068c2ecf20Sopenharmony_ci#endif 2078c2ecf20Sopenharmony_ci return false; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic vm_fault_t __kprobes 2118c2ecf20Sopenharmony_ci__do_page_fault(struct mm_struct *mm, unsigned long addr, unsigned int flags, 2128c2ecf20Sopenharmony_ci unsigned long vma_flags, struct pt_regs *regs) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct vm_area_struct *vma = find_vma(mm, addr); 2158c2ecf20Sopenharmony_ci if (unlikely(!vma)) 2168c2ecf20Sopenharmony_ci return VM_FAULT_BADMAP; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (unlikely(vma->vm_start > addr)) { 2198c2ecf20Sopenharmony_ci if (!(vma->vm_flags & VM_GROWSDOWN)) 2208c2ecf20Sopenharmony_ci return VM_FAULT_BADMAP; 2218c2ecf20Sopenharmony_ci if (addr < FIRST_USER_ADDRESS) 2228c2ecf20Sopenharmony_ci return VM_FAULT_BADMAP; 2238c2ecf20Sopenharmony_ci if (expand_stack(vma, addr)) 2248c2ecf20Sopenharmony_ci return VM_FAULT_BADMAP; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* 2288c2ecf20Sopenharmony_ci * ok, we have a good vm_area for this memory access, check the 2298c2ecf20Sopenharmony_ci * permissions on the VMA allow for the fault which occurred. 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci if (!(vma->vm_flags & vma_flags)) 2328c2ecf20Sopenharmony_ci return VM_FAULT_BADACCESS; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return handle_mm_fault(vma, addr & PAGE_MASK, flags, regs); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int __kprobes 2388c2ecf20Sopenharmony_cido_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct mm_struct *mm = current->mm; 2418c2ecf20Sopenharmony_ci int sig, code; 2428c2ecf20Sopenharmony_ci vm_fault_t fault; 2438c2ecf20Sopenharmony_ci unsigned int flags = FAULT_FLAG_DEFAULT; 2448c2ecf20Sopenharmony_ci unsigned long vm_flags = VM_ACCESS_FLAGS; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (kprobe_page_fault(regs, fsr)) 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* Enable interrupts if they were enabled in the parent context. */ 2518c2ecf20Sopenharmony_ci if (interrupts_enabled(regs)) 2528c2ecf20Sopenharmony_ci local_irq_enable(); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* 2558c2ecf20Sopenharmony_ci * If we're in an interrupt or have no user 2568c2ecf20Sopenharmony_ci * context, we must not take the fault.. 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_ci if (faulthandler_disabled() || !mm) 2598c2ecf20Sopenharmony_ci goto no_context; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (user_mode(regs)) 2628c2ecf20Sopenharmony_ci flags |= FAULT_FLAG_USER; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if ((fsr & FSR_WRITE) && !(fsr & FSR_CM)) { 2658c2ecf20Sopenharmony_ci flags |= FAULT_FLAG_WRITE; 2668c2ecf20Sopenharmony_ci vm_flags = VM_WRITE; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (fsr & FSR_LNX_PF) { 2708c2ecf20Sopenharmony_ci vm_flags = VM_EXEC; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (is_permission_fault(fsr) && !user_mode(regs)) 2738c2ecf20Sopenharmony_ci die_kernel_fault("execution of memory", 2748c2ecf20Sopenharmony_ci mm, addr, fsr, regs); 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* 2808c2ecf20Sopenharmony_ci * As per x86, we may deadlock here. However, since the kernel only 2818c2ecf20Sopenharmony_ci * validly references user space from well defined areas of the code, 2828c2ecf20Sopenharmony_ci * we can bug out early if this is from code which shouldn't. 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_ci if (!mmap_read_trylock(mm)) { 2858c2ecf20Sopenharmony_ci if (!user_mode(regs) && !search_exception_tables(regs->ARM_pc)) 2868c2ecf20Sopenharmony_ci goto no_context; 2878c2ecf20Sopenharmony_ciretry: 2888c2ecf20Sopenharmony_ci mmap_read_lock(mm); 2898c2ecf20Sopenharmony_ci } else { 2908c2ecf20Sopenharmony_ci /* 2918c2ecf20Sopenharmony_ci * The above down_read_trylock() might have succeeded in 2928c2ecf20Sopenharmony_ci * which case, we'll have missed the might_sleep() from 2938c2ecf20Sopenharmony_ci * down_read() 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_ci might_sleep(); 2968c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_VM 2978c2ecf20Sopenharmony_ci if (!user_mode(regs) && 2988c2ecf20Sopenharmony_ci !search_exception_tables(regs->ARM_pc)) 2998c2ecf20Sopenharmony_ci goto no_context; 3008c2ecf20Sopenharmony_ci#endif 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci fault = __do_page_fault(mm, addr, flags, vm_flags, regs); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* If we need to retry but a fatal signal is pending, handle the 3068c2ecf20Sopenharmony_ci * signal first. We do not need to release the mmap_lock because 3078c2ecf20Sopenharmony_ci * it would already be released in __lock_page_or_retry in 3088c2ecf20Sopenharmony_ci * mm/filemap.c. */ 3098c2ecf20Sopenharmony_ci if (fault_signal_pending(fault, regs)) { 3108c2ecf20Sopenharmony_ci if (!user_mode(regs)) 3118c2ecf20Sopenharmony_ci goto no_context; 3128c2ecf20Sopenharmony_ci return 0; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (!(fault & VM_FAULT_ERROR) && flags & FAULT_FLAG_ALLOW_RETRY) { 3168c2ecf20Sopenharmony_ci if (fault & VM_FAULT_RETRY) { 3178c2ecf20Sopenharmony_ci flags |= FAULT_FLAG_TRIED; 3188c2ecf20Sopenharmony_ci goto retry; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci mmap_read_unlock(mm); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* 3258c2ecf20Sopenharmony_ci * Handle the "normal" case first - VM_FAULT_MAJOR 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_ci if (likely(!(fault & (VM_FAULT_ERROR | VM_FAULT_BADMAP | VM_FAULT_BADACCESS)))) 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* 3318c2ecf20Sopenharmony_ci * If we are in kernel mode at this point, we 3328c2ecf20Sopenharmony_ci * have no context to handle this fault with. 3338c2ecf20Sopenharmony_ci */ 3348c2ecf20Sopenharmony_ci if (!user_mode(regs)) 3358c2ecf20Sopenharmony_ci goto no_context; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (fault & VM_FAULT_OOM) { 3388c2ecf20Sopenharmony_ci /* 3398c2ecf20Sopenharmony_ci * We ran out of memory, call the OOM killer, and return to 3408c2ecf20Sopenharmony_ci * userspace (which will retry the fault, or kill us if we 3418c2ecf20Sopenharmony_ci * got oom-killed) 3428c2ecf20Sopenharmony_ci */ 3438c2ecf20Sopenharmony_ci pagefault_out_of_memory(); 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (fault & VM_FAULT_SIGBUS) { 3488c2ecf20Sopenharmony_ci /* 3498c2ecf20Sopenharmony_ci * We had some memory, but were unable to 3508c2ecf20Sopenharmony_ci * successfully fix up this page fault. 3518c2ecf20Sopenharmony_ci */ 3528c2ecf20Sopenharmony_ci sig = SIGBUS; 3538c2ecf20Sopenharmony_ci code = BUS_ADRERR; 3548c2ecf20Sopenharmony_ci } else { 3558c2ecf20Sopenharmony_ci /* 3568c2ecf20Sopenharmony_ci * Something tried to access memory that 3578c2ecf20Sopenharmony_ci * isn't in our memory map.. 3588c2ecf20Sopenharmony_ci */ 3598c2ecf20Sopenharmony_ci sig = SIGSEGV; 3608c2ecf20Sopenharmony_ci code = fault == VM_FAULT_BADACCESS ? 3618c2ecf20Sopenharmony_ci SEGV_ACCERR : SEGV_MAPERR; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci __do_user_fault(addr, fsr, sig, code, regs); 3658c2ecf20Sopenharmony_ci return 0; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cino_context: 3688c2ecf20Sopenharmony_ci __do_kernel_fault(mm, addr, fsr, regs); 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci#else /* CONFIG_MMU */ 3728c2ecf20Sopenharmony_cistatic int 3738c2ecf20Sopenharmony_cido_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci return 0; 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci#endif /* CONFIG_MMU */ 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci/* 3808c2ecf20Sopenharmony_ci * First Level Translation Fault Handler 3818c2ecf20Sopenharmony_ci * 3828c2ecf20Sopenharmony_ci * We enter here because the first level page table doesn't contain 3838c2ecf20Sopenharmony_ci * a valid entry for the address. 3848c2ecf20Sopenharmony_ci * 3858c2ecf20Sopenharmony_ci * If the address is in kernel space (>= TASK_SIZE), then we are 3868c2ecf20Sopenharmony_ci * probably faulting in the vmalloc() area. 3878c2ecf20Sopenharmony_ci * 3888c2ecf20Sopenharmony_ci * If the init_task's first level page tables contains the relevant 3898c2ecf20Sopenharmony_ci * entry, we copy the it to this task. If not, we send the process 3908c2ecf20Sopenharmony_ci * a signal, fixup the exception, or oops the kernel. 3918c2ecf20Sopenharmony_ci * 3928c2ecf20Sopenharmony_ci * NOTE! We MUST NOT take any locks for this case. We may be in an 3938c2ecf20Sopenharmony_ci * interrupt or a critical region, and should only copy the information 3948c2ecf20Sopenharmony_ci * from the master page table, nothing more. 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_ci#ifdef CONFIG_MMU 3978c2ecf20Sopenharmony_cistatic int __kprobes 3988c2ecf20Sopenharmony_cido_translation_fault(unsigned long addr, unsigned int fsr, 3998c2ecf20Sopenharmony_ci struct pt_regs *regs) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci unsigned int index; 4028c2ecf20Sopenharmony_ci pgd_t *pgd, *pgd_k; 4038c2ecf20Sopenharmony_ci p4d_t *p4d, *p4d_k; 4048c2ecf20Sopenharmony_ci pud_t *pud, *pud_k; 4058c2ecf20Sopenharmony_ci pmd_t *pmd, *pmd_k; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if (addr < TASK_SIZE) 4088c2ecf20Sopenharmony_ci return do_page_fault(addr, fsr, regs); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (user_mode(regs)) 4118c2ecf20Sopenharmony_ci goto bad_area; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci index = pgd_index(addr); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci pgd = cpu_get_pgd() + index; 4168c2ecf20Sopenharmony_ci pgd_k = init_mm.pgd + index; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci p4d = p4d_offset(pgd, addr); 4198c2ecf20Sopenharmony_ci p4d_k = p4d_offset(pgd_k, addr); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (p4d_none(*p4d_k)) 4228c2ecf20Sopenharmony_ci goto bad_area; 4238c2ecf20Sopenharmony_ci if (!p4d_present(*p4d)) 4248c2ecf20Sopenharmony_ci set_p4d(p4d, *p4d_k); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci pud = pud_offset(p4d, addr); 4278c2ecf20Sopenharmony_ci pud_k = pud_offset(p4d_k, addr); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (pud_none(*pud_k)) 4308c2ecf20Sopenharmony_ci goto bad_area; 4318c2ecf20Sopenharmony_ci if (!pud_present(*pud)) 4328c2ecf20Sopenharmony_ci set_pud(pud, *pud_k); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci pmd = pmd_offset(pud, addr); 4358c2ecf20Sopenharmony_ci pmd_k = pmd_offset(pud_k, addr); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_LPAE 4388c2ecf20Sopenharmony_ci /* 4398c2ecf20Sopenharmony_ci * Only one hardware entry per PMD with LPAE. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci index = 0; 4428c2ecf20Sopenharmony_ci#else 4438c2ecf20Sopenharmony_ci /* 4448c2ecf20Sopenharmony_ci * On ARM one Linux PGD entry contains two hardware entries (see page 4458c2ecf20Sopenharmony_ci * tables layout in pgtable.h). We normally guarantee that we always 4468c2ecf20Sopenharmony_ci * fill both L1 entries. But create_mapping() doesn't follow the rule. 4478c2ecf20Sopenharmony_ci * It can create inidividual L1 entries, so here we have to call 4488c2ecf20Sopenharmony_ci * pmd_none() check for the entry really corresponded to address, not 4498c2ecf20Sopenharmony_ci * for the first of pair. 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_ci index = (addr >> SECTION_SHIFT) & 1; 4528c2ecf20Sopenharmony_ci#endif 4538c2ecf20Sopenharmony_ci if (pmd_none(pmd_k[index])) 4548c2ecf20Sopenharmony_ci goto bad_area; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci copy_pmd(pmd, pmd_k); 4578c2ecf20Sopenharmony_ci return 0; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cibad_area: 4608c2ecf20Sopenharmony_ci do_bad_area(addr, fsr, regs); 4618c2ecf20Sopenharmony_ci return 0; 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci#else /* CONFIG_MMU */ 4648c2ecf20Sopenharmony_cistatic int 4658c2ecf20Sopenharmony_cido_translation_fault(unsigned long addr, unsigned int fsr, 4668c2ecf20Sopenharmony_ci struct pt_regs *regs) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci return 0; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci#endif /* CONFIG_MMU */ 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci/* 4738c2ecf20Sopenharmony_ci * Some section permission faults need to be handled gracefully. 4748c2ecf20Sopenharmony_ci * They can happen due to a __{get,put}_user during an oops. 4758c2ecf20Sopenharmony_ci */ 4768c2ecf20Sopenharmony_ci#ifndef CONFIG_ARM_LPAE 4778c2ecf20Sopenharmony_cistatic int 4788c2ecf20Sopenharmony_cido_sect_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci do_bad_area(addr, fsr, regs); 4818c2ecf20Sopenharmony_ci return 0; 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci#endif /* CONFIG_ARM_LPAE */ 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci/* 4868c2ecf20Sopenharmony_ci * This abort handler always returns "fault". 4878c2ecf20Sopenharmony_ci */ 4888c2ecf20Sopenharmony_cistatic int 4898c2ecf20Sopenharmony_cido_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci return 1; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistruct fsr_info { 4958c2ecf20Sopenharmony_ci int (*fn)(unsigned long addr, unsigned int fsr, struct pt_regs *regs); 4968c2ecf20Sopenharmony_ci int sig; 4978c2ecf20Sopenharmony_ci int code; 4988c2ecf20Sopenharmony_ci const char *name; 4998c2ecf20Sopenharmony_ci}; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci/* FSR definition */ 5028c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_LPAE 5038c2ecf20Sopenharmony_ci#include "fsr-3level.c" 5048c2ecf20Sopenharmony_ci#else 5058c2ecf20Sopenharmony_ci#include "fsr-2level.c" 5068c2ecf20Sopenharmony_ci#endif 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_civoid __init 5098c2ecf20Sopenharmony_cihook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *), 5108c2ecf20Sopenharmony_ci int sig, int code, const char *name) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci if (nr < 0 || nr >= ARRAY_SIZE(fsr_info)) 5138c2ecf20Sopenharmony_ci BUG(); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci fsr_info[nr].fn = fn; 5168c2ecf20Sopenharmony_ci fsr_info[nr].sig = sig; 5178c2ecf20Sopenharmony_ci fsr_info[nr].code = code; 5188c2ecf20Sopenharmony_ci fsr_info[nr].name = name; 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci/* 5228c2ecf20Sopenharmony_ci * Dispatch a data abort to the relevant handler. 5238c2ecf20Sopenharmony_ci */ 5248c2ecf20Sopenharmony_ciasmlinkage void 5258c2ecf20Sopenharmony_cido_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci const struct fsr_info *inf = fsr_info + fsr_fs(fsr); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci if (!inf->fn(addr, fsr & ~FSR_LNX_PF, regs)) 5308c2ecf20Sopenharmony_ci return; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci pr_alert("8<--- cut here ---\n"); 5338c2ecf20Sopenharmony_ci pr_alert("Unhandled fault: %s (0x%03x) at 0x%08lx\n", 5348c2ecf20Sopenharmony_ci inf->name, fsr, addr); 5358c2ecf20Sopenharmony_ci show_pte(KERN_ALERT, current->mm, addr); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci arm_notify_die("", regs, inf->sig, inf->code, (void __user *)addr, 5388c2ecf20Sopenharmony_ci fsr, 0); 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_civoid __init 5428c2ecf20Sopenharmony_cihook_ifault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *), 5438c2ecf20Sopenharmony_ci int sig, int code, const char *name) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci if (nr < 0 || nr >= ARRAY_SIZE(ifsr_info)) 5468c2ecf20Sopenharmony_ci BUG(); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci ifsr_info[nr].fn = fn; 5498c2ecf20Sopenharmony_ci ifsr_info[nr].sig = sig; 5508c2ecf20Sopenharmony_ci ifsr_info[nr].code = code; 5518c2ecf20Sopenharmony_ci ifsr_info[nr].name = name; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ciasmlinkage void 5558c2ecf20Sopenharmony_cido_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci const struct fsr_info *inf = ifsr_info + fsr_fs(ifsr); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (!inf->fn(addr, ifsr | FSR_LNX_PF, regs)) 5608c2ecf20Sopenharmony_ci return; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci pr_alert("Unhandled prefetch abort: %s (0x%03x) at 0x%08lx\n", 5638c2ecf20Sopenharmony_ci inf->name, ifsr, addr); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci arm_notify_die("", regs, inf->sig, inf->code, (void __user *)addr, 5668c2ecf20Sopenharmony_ci ifsr, 0); 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci/* 5708c2ecf20Sopenharmony_ci * Abort handler to be used only during first unmasking of asynchronous aborts 5718c2ecf20Sopenharmony_ci * on the boot CPU. This makes sure that the machine will not die if the 5728c2ecf20Sopenharmony_ci * firmware/bootloader left an imprecise abort pending for us to trip over. 5738c2ecf20Sopenharmony_ci */ 5748c2ecf20Sopenharmony_cistatic int __init early_abort_handler(unsigned long addr, unsigned int fsr, 5758c2ecf20Sopenharmony_ci struct pt_regs *regs) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci pr_warn("Hit pending asynchronous external abort (FSR=0x%08x) during " 5788c2ecf20Sopenharmony_ci "first unmask, this is most likely caused by a " 5798c2ecf20Sopenharmony_ci "firmware/bootloader bug.\n", fsr); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci return 0; 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_civoid __init early_abt_enable(void) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci fsr_info[FSR_FS_AEA].fn = early_abort_handler; 5878c2ecf20Sopenharmony_ci local_abt_enable(); 5888c2ecf20Sopenharmony_ci fsr_info[FSR_FS_AEA].fn = do_bad; 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci#ifndef CONFIG_ARM_LPAE 5928c2ecf20Sopenharmony_cistatic int __init exceptions_init(void) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci if (cpu_architecture() >= CPU_ARCH_ARMv6) { 5958c2ecf20Sopenharmony_ci hook_fault_code(4, do_translation_fault, SIGSEGV, SEGV_MAPERR, 5968c2ecf20Sopenharmony_ci "I-cache maintenance fault"); 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (cpu_architecture() >= CPU_ARCH_ARMv7) { 6008c2ecf20Sopenharmony_ci /* 6018c2ecf20Sopenharmony_ci * TODO: Access flag faults introduced in ARMv6K. 6028c2ecf20Sopenharmony_ci * Runtime check for 'K' extension is needed 6038c2ecf20Sopenharmony_ci */ 6048c2ecf20Sopenharmony_ci hook_fault_code(3, do_bad, SIGSEGV, SEGV_MAPERR, 6058c2ecf20Sopenharmony_ci "section access flag fault"); 6068c2ecf20Sopenharmony_ci hook_fault_code(6, do_bad, SIGSEGV, SEGV_MAPERR, 6078c2ecf20Sopenharmony_ci "section access flag fault"); 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci return 0; 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ciarch_initcall(exceptions_init); 6148c2ecf20Sopenharmony_ci#endif 615