18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * fault.c: Page fault handlers for the Sparc. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) 68c2ecf20Sopenharmony_ci * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) 78c2ecf20Sopenharmony_ci * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <asm/head.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/string.h> 138c2ecf20Sopenharmony_ci#include <linux/types.h> 148c2ecf20Sopenharmony_ci#include <linux/sched.h> 158c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 168c2ecf20Sopenharmony_ci#include <linux/mman.h> 178c2ecf20Sopenharmony_ci#include <linux/threads.h> 188c2ecf20Sopenharmony_ci#include <linux/kernel.h> 198c2ecf20Sopenharmony_ci#include <linux/signal.h> 208c2ecf20Sopenharmony_ci#include <linux/mm.h> 218c2ecf20Sopenharmony_ci#include <linux/smp.h> 228c2ecf20Sopenharmony_ci#include <linux/perf_event.h> 238c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 248c2ecf20Sopenharmony_ci#include <linux/kdebug.h> 258c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <asm/page.h> 288c2ecf20Sopenharmony_ci#include <asm/openprom.h> 298c2ecf20Sopenharmony_ci#include <asm/oplib.h> 308c2ecf20Sopenharmony_ci#include <asm/setup.h> 318c2ecf20Sopenharmony_ci#include <asm/smp.h> 328c2ecf20Sopenharmony_ci#include <asm/traps.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include "mm_32.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ciint show_unhandled_signals = 1; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic void __noreturn unhandled_fault(unsigned long address, 398c2ecf20Sopenharmony_ci struct task_struct *tsk, 408c2ecf20Sopenharmony_ci struct pt_regs *regs) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci if ((unsigned long) address < PAGE_SIZE) { 438c2ecf20Sopenharmony_ci printk(KERN_ALERT 448c2ecf20Sopenharmony_ci "Unable to handle kernel NULL pointer dereference\n"); 458c2ecf20Sopenharmony_ci } else { 468c2ecf20Sopenharmony_ci printk(KERN_ALERT "Unable to handle kernel paging request at virtual address %08lx\n", 478c2ecf20Sopenharmony_ci address); 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci printk(KERN_ALERT "tsk->{mm,active_mm}->context = %08lx\n", 508c2ecf20Sopenharmony_ci (tsk->mm ? tsk->mm->context : tsk->active_mm->context)); 518c2ecf20Sopenharmony_ci printk(KERN_ALERT "tsk->{mm,active_mm}->pgd = %08lx\n", 528c2ecf20Sopenharmony_ci (tsk->mm ? (unsigned long) tsk->mm->pgd : 538c2ecf20Sopenharmony_ci (unsigned long) tsk->active_mm->pgd)); 548c2ecf20Sopenharmony_ci die_if_kernel("Oops", regs); 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ciasmlinkage int lookup_fault(unsigned long pc, unsigned long ret_pc, 588c2ecf20Sopenharmony_ci unsigned long address) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct pt_regs regs; 618c2ecf20Sopenharmony_ci unsigned long g2; 628c2ecf20Sopenharmony_ci unsigned int insn; 638c2ecf20Sopenharmony_ci int i; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci i = search_extables_range(ret_pc, &g2); 668c2ecf20Sopenharmony_ci switch (i) { 678c2ecf20Sopenharmony_ci case 3: 688c2ecf20Sopenharmony_ci /* load & store will be handled by fixup */ 698c2ecf20Sopenharmony_ci return 3; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci case 1: 728c2ecf20Sopenharmony_ci /* store will be handled by fixup, load will bump out */ 738c2ecf20Sopenharmony_ci /* for _to_ macros */ 748c2ecf20Sopenharmony_ci insn = *((unsigned int *) pc); 758c2ecf20Sopenharmony_ci if ((insn >> 21) & 1) 768c2ecf20Sopenharmony_ci return 1; 778c2ecf20Sopenharmony_ci break; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci case 2: 808c2ecf20Sopenharmony_ci /* load will be handled by fixup, store will bump out */ 818c2ecf20Sopenharmony_ci /* for _from_ macros */ 828c2ecf20Sopenharmony_ci insn = *((unsigned int *) pc); 838c2ecf20Sopenharmony_ci if (!((insn >> 21) & 1) || ((insn>>19)&0x3f) == 15) 848c2ecf20Sopenharmony_ci return 2; 858c2ecf20Sopenharmony_ci break; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci default: 888c2ecf20Sopenharmony_ci break; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci memset(®s, 0, sizeof(regs)); 928c2ecf20Sopenharmony_ci regs.pc = pc; 938c2ecf20Sopenharmony_ci regs.npc = pc + 4; 948c2ecf20Sopenharmony_ci __asm__ __volatile__( 958c2ecf20Sopenharmony_ci "rd %%psr, %0\n\t" 968c2ecf20Sopenharmony_ci "nop\n\t" 978c2ecf20Sopenharmony_ci "nop\n\t" 988c2ecf20Sopenharmony_ci "nop\n" : "=r" (regs.psr)); 998c2ecf20Sopenharmony_ci unhandled_fault(address, current, ®s); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* Not reached */ 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic inline void 1068c2ecf20Sopenharmony_cishow_signal_msg(struct pt_regs *regs, int sig, int code, 1078c2ecf20Sopenharmony_ci unsigned long address, struct task_struct *tsk) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci if (!unhandled_signal(tsk, sig)) 1108c2ecf20Sopenharmony_ci return; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (!printk_ratelimit()) 1138c2ecf20Sopenharmony_ci return; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci printk("%s%s[%d]: segfault at %lx ip %px (rpc %px) sp %px error %x", 1168c2ecf20Sopenharmony_ci task_pid_nr(tsk) > 1 ? KERN_INFO : KERN_EMERG, 1178c2ecf20Sopenharmony_ci tsk->comm, task_pid_nr(tsk), address, 1188c2ecf20Sopenharmony_ci (void *)regs->pc, (void *)regs->u_regs[UREG_I7], 1198c2ecf20Sopenharmony_ci (void *)regs->u_regs[UREG_FP], code); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci print_vma_addr(KERN_CONT " in ", regs->pc); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci printk(KERN_CONT "\n"); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void __do_fault_siginfo(int code, int sig, struct pt_regs *regs, 1278c2ecf20Sopenharmony_ci unsigned long addr) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci if (unlikely(show_unhandled_signals)) 1308c2ecf20Sopenharmony_ci show_signal_msg(regs, sig, code, 1318c2ecf20Sopenharmony_ci addr, current); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci force_sig_fault(sig, code, (void __user *) addr, 0); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic unsigned long compute_si_addr(struct pt_regs *regs, int text_fault) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci unsigned int insn; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (text_fault) 1418c2ecf20Sopenharmony_ci return regs->pc; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (regs->psr & PSR_PS) 1448c2ecf20Sopenharmony_ci insn = *(unsigned int *) regs->pc; 1458c2ecf20Sopenharmony_ci else 1468c2ecf20Sopenharmony_ci __get_user(insn, (unsigned int *) regs->pc); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return safe_compute_effective_address(regs, insn); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic noinline void do_fault_siginfo(int code, int sig, struct pt_regs *regs, 1528c2ecf20Sopenharmony_ci int text_fault) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci unsigned long addr = compute_si_addr(regs, text_fault); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci __do_fault_siginfo(code, sig, regs, addr); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ciasmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write, 1608c2ecf20Sopenharmony_ci unsigned long address) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct vm_area_struct *vma; 1638c2ecf20Sopenharmony_ci struct task_struct *tsk = current; 1648c2ecf20Sopenharmony_ci struct mm_struct *mm = tsk->mm; 1658c2ecf20Sopenharmony_ci unsigned int fixup; 1668c2ecf20Sopenharmony_ci unsigned long g2; 1678c2ecf20Sopenharmony_ci int from_user = !(regs->psr & PSR_PS); 1688c2ecf20Sopenharmony_ci int code; 1698c2ecf20Sopenharmony_ci vm_fault_t fault; 1708c2ecf20Sopenharmony_ci unsigned int flags = FAULT_FLAG_DEFAULT; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (text_fault) 1738c2ecf20Sopenharmony_ci address = regs->pc; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* 1768c2ecf20Sopenharmony_ci * We fault-in kernel-space virtual memory on-demand. The 1778c2ecf20Sopenharmony_ci * 'reference' page table is init_mm.pgd. 1788c2ecf20Sopenharmony_ci * 1798c2ecf20Sopenharmony_ci * NOTE! We MUST NOT take any locks for this case. We may 1808c2ecf20Sopenharmony_ci * be in an interrupt or a critical region, and should 1818c2ecf20Sopenharmony_ci * only copy the information from the master page table, 1828c2ecf20Sopenharmony_ci * nothing more. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ci code = SEGV_MAPERR; 1858c2ecf20Sopenharmony_ci if (address >= TASK_SIZE) 1868c2ecf20Sopenharmony_ci goto vmalloc_fault; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* 1898c2ecf20Sopenharmony_ci * If we're in an interrupt or have no user 1908c2ecf20Sopenharmony_ci * context, we must not take the fault.. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_ci if (pagefault_disabled() || !mm) 1938c2ecf20Sopenharmony_ci goto no_context; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ciretry: 1988c2ecf20Sopenharmony_ci mmap_read_lock(mm); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (!from_user && address >= PAGE_OFFSET) 2018c2ecf20Sopenharmony_ci goto bad_area; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci vma = find_vma(mm, address); 2048c2ecf20Sopenharmony_ci if (!vma) 2058c2ecf20Sopenharmony_ci goto bad_area; 2068c2ecf20Sopenharmony_ci if (vma->vm_start <= address) 2078c2ecf20Sopenharmony_ci goto good_area; 2088c2ecf20Sopenharmony_ci if (!(vma->vm_flags & VM_GROWSDOWN)) 2098c2ecf20Sopenharmony_ci goto bad_area; 2108c2ecf20Sopenharmony_ci if (expand_stack(vma, address)) 2118c2ecf20Sopenharmony_ci goto bad_area; 2128c2ecf20Sopenharmony_ci /* 2138c2ecf20Sopenharmony_ci * Ok, we have a good vm_area for this memory access, so 2148c2ecf20Sopenharmony_ci * we can handle it.. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_cigood_area: 2178c2ecf20Sopenharmony_ci code = SEGV_ACCERR; 2188c2ecf20Sopenharmony_ci if (write) { 2198c2ecf20Sopenharmony_ci if (!(vma->vm_flags & VM_WRITE)) 2208c2ecf20Sopenharmony_ci goto bad_area; 2218c2ecf20Sopenharmony_ci } else { 2228c2ecf20Sopenharmony_ci /* Allow reads even for write-only mappings */ 2238c2ecf20Sopenharmony_ci if (!(vma->vm_flags & (VM_READ | VM_EXEC))) 2248c2ecf20Sopenharmony_ci goto bad_area; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (from_user) 2288c2ecf20Sopenharmony_ci flags |= FAULT_FLAG_USER; 2298c2ecf20Sopenharmony_ci if (write) 2308c2ecf20Sopenharmony_ci flags |= FAULT_FLAG_WRITE; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* 2338c2ecf20Sopenharmony_ci * If for any reason at all we couldn't handle the fault, 2348c2ecf20Sopenharmony_ci * make sure we exit gracefully rather than endlessly redo 2358c2ecf20Sopenharmony_ci * the fault. 2368c2ecf20Sopenharmony_ci */ 2378c2ecf20Sopenharmony_ci fault = handle_mm_fault(vma, address, flags, regs); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (fault_signal_pending(fault, regs)) 2408c2ecf20Sopenharmony_ci return; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (unlikely(fault & VM_FAULT_ERROR)) { 2438c2ecf20Sopenharmony_ci if (fault & VM_FAULT_OOM) 2448c2ecf20Sopenharmony_ci goto out_of_memory; 2458c2ecf20Sopenharmony_ci else if (fault & VM_FAULT_SIGSEGV) 2468c2ecf20Sopenharmony_ci goto bad_area; 2478c2ecf20Sopenharmony_ci else if (fault & VM_FAULT_SIGBUS) 2488c2ecf20Sopenharmony_ci goto do_sigbus; 2498c2ecf20Sopenharmony_ci BUG(); 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (flags & FAULT_FLAG_ALLOW_RETRY) { 2538c2ecf20Sopenharmony_ci if (fault & VM_FAULT_RETRY) { 2548c2ecf20Sopenharmony_ci flags |= FAULT_FLAG_TRIED; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* No need to mmap_read_unlock(mm) as we would 2578c2ecf20Sopenharmony_ci * have already released it in __lock_page_or_retry 2588c2ecf20Sopenharmony_ci * in mm/filemap.c. 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci goto retry; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci mmap_read_unlock(mm); 2668c2ecf20Sopenharmony_ci return; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* 2698c2ecf20Sopenharmony_ci * Something tried to access memory that isn't in our memory map.. 2708c2ecf20Sopenharmony_ci * Fix it, but check if it's kernel or user first.. 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_cibad_area: 2738c2ecf20Sopenharmony_ci mmap_read_unlock(mm); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cibad_area_nosemaphore: 2768c2ecf20Sopenharmony_ci /* User mode accesses just cause a SIGSEGV */ 2778c2ecf20Sopenharmony_ci if (from_user) { 2788c2ecf20Sopenharmony_ci do_fault_siginfo(code, SIGSEGV, regs, text_fault); 2798c2ecf20Sopenharmony_ci return; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* Is this in ex_table? */ 2838c2ecf20Sopenharmony_cino_context: 2848c2ecf20Sopenharmony_ci g2 = regs->u_regs[UREG_G2]; 2858c2ecf20Sopenharmony_ci if (!from_user) { 2868c2ecf20Sopenharmony_ci fixup = search_extables_range(regs->pc, &g2); 2878c2ecf20Sopenharmony_ci /* Values below 10 are reserved for other things */ 2888c2ecf20Sopenharmony_ci if (fixup > 10) { 2898c2ecf20Sopenharmony_ci extern const unsigned int __memset_start[]; 2908c2ecf20Sopenharmony_ci extern const unsigned int __memset_end[]; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci#ifdef DEBUG_EXCEPTIONS 2938c2ecf20Sopenharmony_ci printk("Exception: PC<%08lx> faddr<%08lx>\n", 2948c2ecf20Sopenharmony_ci regs->pc, address); 2958c2ecf20Sopenharmony_ci printk("EX_TABLE: insn<%08lx> fixup<%08x> g2<%08lx>\n", 2968c2ecf20Sopenharmony_ci regs->pc, fixup, g2); 2978c2ecf20Sopenharmony_ci#endif 2988c2ecf20Sopenharmony_ci if ((regs->pc >= (unsigned long)__memset_start && 2998c2ecf20Sopenharmony_ci regs->pc < (unsigned long)__memset_end)) { 3008c2ecf20Sopenharmony_ci regs->u_regs[UREG_I4] = address; 3018c2ecf20Sopenharmony_ci regs->u_regs[UREG_I5] = regs->pc; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci regs->u_regs[UREG_G2] = g2; 3048c2ecf20Sopenharmony_ci regs->pc = fixup; 3058c2ecf20Sopenharmony_ci regs->npc = regs->pc + 4; 3068c2ecf20Sopenharmony_ci return; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci unhandled_fault(address, tsk, regs); 3118c2ecf20Sopenharmony_ci do_exit(SIGKILL); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci/* 3148c2ecf20Sopenharmony_ci * We ran out of memory, or some other thing happened to us that made 3158c2ecf20Sopenharmony_ci * us unable to handle the page fault gracefully. 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_ciout_of_memory: 3188c2ecf20Sopenharmony_ci mmap_read_unlock(mm); 3198c2ecf20Sopenharmony_ci if (from_user) { 3208c2ecf20Sopenharmony_ci pagefault_out_of_memory(); 3218c2ecf20Sopenharmony_ci return; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci goto no_context; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cido_sigbus: 3268c2ecf20Sopenharmony_ci mmap_read_unlock(mm); 3278c2ecf20Sopenharmony_ci do_fault_siginfo(BUS_ADRERR, SIGBUS, regs, text_fault); 3288c2ecf20Sopenharmony_ci if (!from_user) 3298c2ecf20Sopenharmony_ci goto no_context; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_civmalloc_fault: 3328c2ecf20Sopenharmony_ci { 3338c2ecf20Sopenharmony_ci /* 3348c2ecf20Sopenharmony_ci * Synchronize this task's top level page-table 3358c2ecf20Sopenharmony_ci * with the 'reference' page table. 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_ci int offset = pgd_index(address); 3388c2ecf20Sopenharmony_ci pgd_t *pgd, *pgd_k; 3398c2ecf20Sopenharmony_ci p4d_t *p4d, *p4d_k; 3408c2ecf20Sopenharmony_ci pud_t *pud, *pud_k; 3418c2ecf20Sopenharmony_ci pmd_t *pmd, *pmd_k; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci pgd = tsk->active_mm->pgd + offset; 3448c2ecf20Sopenharmony_ci pgd_k = init_mm.pgd + offset; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (!pgd_present(*pgd)) { 3478c2ecf20Sopenharmony_ci if (!pgd_present(*pgd_k)) 3488c2ecf20Sopenharmony_ci goto bad_area_nosemaphore; 3498c2ecf20Sopenharmony_ci pgd_val(*pgd) = pgd_val(*pgd_k); 3508c2ecf20Sopenharmony_ci return; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci p4d = p4d_offset(pgd, address); 3548c2ecf20Sopenharmony_ci pud = pud_offset(p4d, address); 3558c2ecf20Sopenharmony_ci pmd = pmd_offset(pud, address); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci p4d_k = p4d_offset(pgd_k, address); 3588c2ecf20Sopenharmony_ci pud_k = pud_offset(p4d_k, address); 3598c2ecf20Sopenharmony_ci pmd_k = pmd_offset(pud_k, address); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (pmd_present(*pmd) || !pmd_present(*pmd_k)) 3628c2ecf20Sopenharmony_ci goto bad_area_nosemaphore; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci *pmd = *pmd_k; 3658c2ecf20Sopenharmony_ci return; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci/* This always deals with user addresses. */ 3708c2ecf20Sopenharmony_cistatic void force_user_fault(unsigned long address, int write) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct vm_area_struct *vma; 3738c2ecf20Sopenharmony_ci struct task_struct *tsk = current; 3748c2ecf20Sopenharmony_ci struct mm_struct *mm = tsk->mm; 3758c2ecf20Sopenharmony_ci unsigned int flags = FAULT_FLAG_USER; 3768c2ecf20Sopenharmony_ci int code; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci code = SEGV_MAPERR; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci mmap_read_lock(mm); 3818c2ecf20Sopenharmony_ci vma = find_vma(mm, address); 3828c2ecf20Sopenharmony_ci if (!vma) 3838c2ecf20Sopenharmony_ci goto bad_area; 3848c2ecf20Sopenharmony_ci if (vma->vm_start <= address) 3858c2ecf20Sopenharmony_ci goto good_area; 3868c2ecf20Sopenharmony_ci if (!(vma->vm_flags & VM_GROWSDOWN)) 3878c2ecf20Sopenharmony_ci goto bad_area; 3888c2ecf20Sopenharmony_ci if (expand_stack(vma, address)) 3898c2ecf20Sopenharmony_ci goto bad_area; 3908c2ecf20Sopenharmony_cigood_area: 3918c2ecf20Sopenharmony_ci code = SEGV_ACCERR; 3928c2ecf20Sopenharmony_ci if (write) { 3938c2ecf20Sopenharmony_ci if (!(vma->vm_flags & VM_WRITE)) 3948c2ecf20Sopenharmony_ci goto bad_area; 3958c2ecf20Sopenharmony_ci flags |= FAULT_FLAG_WRITE; 3968c2ecf20Sopenharmony_ci } else { 3978c2ecf20Sopenharmony_ci if (!(vma->vm_flags & (VM_READ | VM_EXEC))) 3988c2ecf20Sopenharmony_ci goto bad_area; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci switch (handle_mm_fault(vma, address, flags, NULL)) { 4018c2ecf20Sopenharmony_ci case VM_FAULT_SIGBUS: 4028c2ecf20Sopenharmony_ci case VM_FAULT_OOM: 4038c2ecf20Sopenharmony_ci goto do_sigbus; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci mmap_read_unlock(mm); 4068c2ecf20Sopenharmony_ci return; 4078c2ecf20Sopenharmony_cibad_area: 4088c2ecf20Sopenharmony_ci mmap_read_unlock(mm); 4098c2ecf20Sopenharmony_ci __do_fault_siginfo(code, SIGSEGV, tsk->thread.kregs, address); 4108c2ecf20Sopenharmony_ci return; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cido_sigbus: 4138c2ecf20Sopenharmony_ci mmap_read_unlock(mm); 4148c2ecf20Sopenharmony_ci __do_fault_siginfo(BUS_ADRERR, SIGBUS, tsk->thread.kregs, address); 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic void check_stack_aligned(unsigned long sp) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci if (sp & 0x7UL) 4208c2ecf20Sopenharmony_ci force_sig(SIGILL); 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_civoid window_overflow_fault(void) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci unsigned long sp; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci sp = current_thread_info()->rwbuf_stkptrs[0]; 4288c2ecf20Sopenharmony_ci if (((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK)) 4298c2ecf20Sopenharmony_ci force_user_fault(sp + 0x38, 1); 4308c2ecf20Sopenharmony_ci force_user_fault(sp, 1); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci check_stack_aligned(sp); 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_civoid window_underflow_fault(unsigned long sp) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci if (((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK)) 4388c2ecf20Sopenharmony_ci force_user_fault(sp + 0x38, 0); 4398c2ecf20Sopenharmony_ci force_user_fault(sp, 0); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci check_stack_aligned(sp); 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_civoid window_ret_fault(struct pt_regs *regs) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci unsigned long sp; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci sp = regs->u_regs[UREG_FP]; 4498c2ecf20Sopenharmony_ci if (((sp + 0x38) & PAGE_MASK) != (sp & PAGE_MASK)) 4508c2ecf20Sopenharmony_ci force_user_fault(sp + 0x38, 0); 4518c2ecf20Sopenharmony_ci force_user_fault(sp, 0); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci check_stack_aligned(sp); 4548c2ecf20Sopenharmony_ci} 455