162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/arch/m68k/mm/fault.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1995 Hamish Macdonald 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/mman.h> 962306a36Sopenharmony_ci#include <linux/mm.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/ptrace.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/uaccess.h> 1562306a36Sopenharmony_ci#include <linux/perf_event.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/setup.h> 1862306a36Sopenharmony_ci#include <asm/traps.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ciextern void die_if_kernel(char *, struct pt_regs *, long); 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ciint send_fault_sig(struct pt_regs *regs) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci int signo, si_code; 2562306a36Sopenharmony_ci void __user *addr; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci signo = current->thread.signo; 2862306a36Sopenharmony_ci si_code = current->thread.code; 2962306a36Sopenharmony_ci addr = (void __user *)current->thread.faddr; 3062306a36Sopenharmony_ci pr_debug("send_fault_sig: %p,%d,%d\n", addr, signo, si_code); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci if (user_mode(regs)) { 3362306a36Sopenharmony_ci force_sig_fault(signo, si_code, addr); 3462306a36Sopenharmony_ci } else { 3562306a36Sopenharmony_ci if (fixup_exception(regs)) 3662306a36Sopenharmony_ci return -1; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci //if (signo == SIGBUS) 3962306a36Sopenharmony_ci // force_sig_fault(si_signo, si_code, addr); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci /* 4262306a36Sopenharmony_ci * Oops. The kernel tried to access some bad page. We'll have to 4362306a36Sopenharmony_ci * terminate things with extreme prejudice. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci if ((unsigned long)addr < PAGE_SIZE) 4662306a36Sopenharmony_ci pr_alert("Unable to handle kernel NULL pointer dereference"); 4762306a36Sopenharmony_ci else 4862306a36Sopenharmony_ci pr_alert("Unable to handle kernel access"); 4962306a36Sopenharmony_ci pr_cont(" at virtual address %p\n", addr); 5062306a36Sopenharmony_ci die_if_kernel("Oops", regs, 0 /*error_code*/); 5162306a36Sopenharmony_ci make_task_dead(SIGKILL); 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return 1; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* 5862306a36Sopenharmony_ci * This routine handles page faults. It determines the problem, and 5962306a36Sopenharmony_ci * then passes it off to one of the appropriate routines. 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * error_code: 6262306a36Sopenharmony_ci * bit 0 == 0 means no page found, 1 means protection fault 6362306a36Sopenharmony_ci * bit 1 == 0 means read, 1 means write 6462306a36Sopenharmony_ci * 6562306a36Sopenharmony_ci * If this routine detects a bad access, it returns 1, otherwise it 6662306a36Sopenharmony_ci * returns 0. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ciint do_page_fault(struct pt_regs *regs, unsigned long address, 6962306a36Sopenharmony_ci unsigned long error_code) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct mm_struct *mm = current->mm; 7262306a36Sopenharmony_ci struct vm_area_struct * vma; 7362306a36Sopenharmony_ci vm_fault_t fault; 7462306a36Sopenharmony_ci unsigned int flags = FAULT_FLAG_DEFAULT; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci pr_debug("do page fault:\nregs->sr=%#x, regs->pc=%#lx, address=%#lx, %ld, %p\n", 7762306a36Sopenharmony_ci regs->sr, regs->pc, address, error_code, mm ? mm->pgd : NULL); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* 8062306a36Sopenharmony_ci * If we're in an interrupt or have no user 8162306a36Sopenharmony_ci * context, we must not take the fault.. 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci if (faulthandler_disabled() || !mm) 8462306a36Sopenharmony_ci goto no_context; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (user_mode(regs)) 8762306a36Sopenharmony_ci flags |= FAULT_FLAG_USER; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); 9062306a36Sopenharmony_ciretry: 9162306a36Sopenharmony_ci mmap_read_lock(mm); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci vma = find_vma(mm, address); 9462306a36Sopenharmony_ci if (!vma) 9562306a36Sopenharmony_ci goto map_err; 9662306a36Sopenharmony_ci if (vma->vm_start <= address) 9762306a36Sopenharmony_ci goto good_area; 9862306a36Sopenharmony_ci if (!(vma->vm_flags & VM_GROWSDOWN)) 9962306a36Sopenharmony_ci goto map_err; 10062306a36Sopenharmony_ci if (user_mode(regs)) { 10162306a36Sopenharmony_ci /* Accessing the stack below usp is always a bug. The 10262306a36Sopenharmony_ci "+ 256" is there due to some instructions doing 10362306a36Sopenharmony_ci pre-decrement on the stack and that doesn't show up 10462306a36Sopenharmony_ci until later. */ 10562306a36Sopenharmony_ci if (address + 256 < rdusp()) 10662306a36Sopenharmony_ci goto map_err; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci vma = expand_stack(mm, address); 10962306a36Sopenharmony_ci if (!vma) 11062306a36Sopenharmony_ci goto map_err_nosemaphore; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* 11362306a36Sopenharmony_ci * Ok, we have a good vm_area for this memory access, so 11462306a36Sopenharmony_ci * we can handle it.. 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_cigood_area: 11762306a36Sopenharmony_ci pr_debug("do_page_fault: good_area\n"); 11862306a36Sopenharmony_ci switch (error_code & 3) { 11962306a36Sopenharmony_ci default: /* 3: write, present */ 12062306a36Sopenharmony_ci fallthrough; 12162306a36Sopenharmony_ci case 2: /* write, not present */ 12262306a36Sopenharmony_ci if (!(vma->vm_flags & VM_WRITE)) 12362306a36Sopenharmony_ci goto acc_err; 12462306a36Sopenharmony_ci flags |= FAULT_FLAG_WRITE; 12562306a36Sopenharmony_ci break; 12662306a36Sopenharmony_ci case 1: /* read, present */ 12762306a36Sopenharmony_ci goto acc_err; 12862306a36Sopenharmony_ci case 0: /* read, not present */ 12962306a36Sopenharmony_ci if (unlikely(!vma_is_accessible(vma))) 13062306a36Sopenharmony_ci goto acc_err; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* 13462306a36Sopenharmony_ci * If for any reason at all we couldn't handle the fault, 13562306a36Sopenharmony_ci * make sure we exit gracefully rather than endlessly redo 13662306a36Sopenharmony_ci * the fault. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci fault = handle_mm_fault(vma, address, flags, regs); 14062306a36Sopenharmony_ci pr_debug("handle_mm_fault returns %x\n", fault); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (fault_signal_pending(fault, regs)) { 14362306a36Sopenharmony_ci if (!user_mode(regs)) 14462306a36Sopenharmony_ci goto no_context; 14562306a36Sopenharmony_ci return 0; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* The fault is fully completed (including releasing mmap lock) */ 14962306a36Sopenharmony_ci if (fault & VM_FAULT_COMPLETED) 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (unlikely(fault & VM_FAULT_ERROR)) { 15362306a36Sopenharmony_ci if (fault & VM_FAULT_OOM) 15462306a36Sopenharmony_ci goto out_of_memory; 15562306a36Sopenharmony_ci else if (fault & VM_FAULT_SIGSEGV) 15662306a36Sopenharmony_ci goto map_err; 15762306a36Sopenharmony_ci else if (fault & VM_FAULT_SIGBUS) 15862306a36Sopenharmony_ci goto bus_err; 15962306a36Sopenharmony_ci BUG(); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (fault & VM_FAULT_RETRY) { 16362306a36Sopenharmony_ci flags |= FAULT_FLAG_TRIED; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* 16662306a36Sopenharmony_ci * No need to mmap_read_unlock(mm) as we would 16762306a36Sopenharmony_ci * have already released it in __lock_page_or_retry 16862306a36Sopenharmony_ci * in mm/filemap.c. 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci goto retry; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci mmap_read_unlock(mm); 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/* 17862306a36Sopenharmony_ci * We ran out of memory, or some other thing happened to us that made 17962306a36Sopenharmony_ci * us unable to handle the page fault gracefully. 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ciout_of_memory: 18262306a36Sopenharmony_ci mmap_read_unlock(mm); 18362306a36Sopenharmony_ci if (!user_mode(regs)) 18462306a36Sopenharmony_ci goto no_context; 18562306a36Sopenharmony_ci pagefault_out_of_memory(); 18662306a36Sopenharmony_ci return 0; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cino_context: 18962306a36Sopenharmony_ci current->thread.signo = SIGBUS; 19062306a36Sopenharmony_ci current->thread.faddr = address; 19162306a36Sopenharmony_ci return send_fault_sig(regs); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cibus_err: 19462306a36Sopenharmony_ci current->thread.signo = SIGBUS; 19562306a36Sopenharmony_ci current->thread.code = BUS_ADRERR; 19662306a36Sopenharmony_ci current->thread.faddr = address; 19762306a36Sopenharmony_ci goto send_sig; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cimap_err: 20062306a36Sopenharmony_ci mmap_read_unlock(mm); 20162306a36Sopenharmony_cimap_err_nosemaphore: 20262306a36Sopenharmony_ci current->thread.signo = SIGSEGV; 20362306a36Sopenharmony_ci current->thread.code = SEGV_MAPERR; 20462306a36Sopenharmony_ci current->thread.faddr = address; 20562306a36Sopenharmony_ci return send_fault_sig(regs); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ciacc_err: 20862306a36Sopenharmony_ci current->thread.signo = SIGSEGV; 20962306a36Sopenharmony_ci current->thread.code = SEGV_ACCERR; 21062306a36Sopenharmony_ci current->thread.faddr = address; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cisend_sig: 21362306a36Sopenharmony_ci mmap_read_unlock(mm); 21462306a36Sopenharmony_ci return send_fault_sig(regs); 21562306a36Sopenharmony_ci} 216