162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Memory fault handling for Hexagon 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci * Page fault handling for the Hexagon Virtual Machine. 1062306a36Sopenharmony_ci * Can also be called by a native port emulating the HVM 1162306a36Sopenharmony_ci * execptions. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <asm/traps.h> 1562306a36Sopenharmony_ci#include <linux/uaccess.h> 1662306a36Sopenharmony_ci#include <linux/mm.h> 1762306a36Sopenharmony_ci#include <linux/sched/signal.h> 1862306a36Sopenharmony_ci#include <linux/signal.h> 1962306a36Sopenharmony_ci#include <linux/extable.h> 2062306a36Sopenharmony_ci#include <linux/hardirq.h> 2162306a36Sopenharmony_ci#include <linux/perf_event.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * Decode of hardware exception sends us to one of several 2562306a36Sopenharmony_ci * entry points. At each, we generate canonical arguments 2662306a36Sopenharmony_ci * for handling by the abstract memory management code. 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci#define FLT_IFETCH -1 2962306a36Sopenharmony_ci#define FLT_LOAD 0 3062306a36Sopenharmony_ci#define FLT_STORE 1 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * Canonical page fault handler 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_civoid do_page_fault(unsigned long address, long cause, struct pt_regs *regs) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct vm_area_struct *vma; 3962306a36Sopenharmony_ci struct mm_struct *mm = current->mm; 4062306a36Sopenharmony_ci int si_signo; 4162306a36Sopenharmony_ci int si_code = SEGV_MAPERR; 4262306a36Sopenharmony_ci vm_fault_t fault; 4362306a36Sopenharmony_ci const struct exception_table_entry *fixup; 4462306a36Sopenharmony_ci unsigned int flags = FAULT_FLAG_DEFAULT; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* 4762306a36Sopenharmony_ci * If we're in an interrupt or have no user context, 4862306a36Sopenharmony_ci * then must not take the fault. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci if (unlikely(in_interrupt() || !mm)) 5162306a36Sopenharmony_ci goto no_context; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci local_irq_enable(); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (user_mode(regs)) 5662306a36Sopenharmony_ci flags |= FAULT_FLAG_USER; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); 5962306a36Sopenharmony_ciretry: 6062306a36Sopenharmony_ci vma = lock_mm_and_find_vma(mm, address, regs); 6162306a36Sopenharmony_ci if (unlikely(!vma)) 6262306a36Sopenharmony_ci goto bad_area_nosemaphore; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* Address space is OK. Now check access rights. */ 6562306a36Sopenharmony_ci si_code = SEGV_ACCERR; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci switch (cause) { 6862306a36Sopenharmony_ci case FLT_IFETCH: 6962306a36Sopenharmony_ci if (!(vma->vm_flags & VM_EXEC)) 7062306a36Sopenharmony_ci goto bad_area; 7162306a36Sopenharmony_ci break; 7262306a36Sopenharmony_ci case FLT_LOAD: 7362306a36Sopenharmony_ci if (!(vma->vm_flags & VM_READ)) 7462306a36Sopenharmony_ci goto bad_area; 7562306a36Sopenharmony_ci break; 7662306a36Sopenharmony_ci case FLT_STORE: 7762306a36Sopenharmony_ci if (!(vma->vm_flags & VM_WRITE)) 7862306a36Sopenharmony_ci goto bad_area; 7962306a36Sopenharmony_ci flags |= FAULT_FLAG_WRITE; 8062306a36Sopenharmony_ci break; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci fault = handle_mm_fault(vma, address, flags, regs); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (fault_signal_pending(fault, regs)) { 8662306a36Sopenharmony_ci if (!user_mode(regs)) 8762306a36Sopenharmony_ci goto no_context; 8862306a36Sopenharmony_ci return; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* The fault is fully completed (including releasing mmap lock) */ 9262306a36Sopenharmony_ci if (fault & VM_FAULT_COMPLETED) 9362306a36Sopenharmony_ci return; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* The most common case -- we are done. */ 9662306a36Sopenharmony_ci if (likely(!(fault & VM_FAULT_ERROR))) { 9762306a36Sopenharmony_ci if (fault & VM_FAULT_RETRY) { 9862306a36Sopenharmony_ci flags |= FAULT_FLAG_TRIED; 9962306a36Sopenharmony_ci goto retry; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci mmap_read_unlock(mm); 10362306a36Sopenharmony_ci return; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci mmap_read_unlock(mm); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* Handle copyin/out exception cases */ 10962306a36Sopenharmony_ci if (!user_mode(regs)) 11062306a36Sopenharmony_ci goto no_context; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (fault & VM_FAULT_OOM) { 11362306a36Sopenharmony_ci pagefault_out_of_memory(); 11462306a36Sopenharmony_ci return; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* User-mode address is in the memory map, but we are 11862306a36Sopenharmony_ci * unable to fix up the page fault. 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_ci if (fault & VM_FAULT_SIGBUS) { 12162306a36Sopenharmony_ci si_signo = SIGBUS; 12262306a36Sopenharmony_ci si_code = BUS_ADRERR; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci /* Address is not in the memory map */ 12562306a36Sopenharmony_ci else { 12662306a36Sopenharmony_ci si_signo = SIGSEGV; 12762306a36Sopenharmony_ci si_code = SEGV_ACCERR; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci force_sig_fault(si_signo, si_code, (void __user *)address); 13062306a36Sopenharmony_ci return; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cibad_area: 13362306a36Sopenharmony_ci mmap_read_unlock(mm); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cibad_area_nosemaphore: 13662306a36Sopenharmony_ci if (user_mode(regs)) { 13762306a36Sopenharmony_ci force_sig_fault(SIGSEGV, si_code, (void __user *)address); 13862306a36Sopenharmony_ci return; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci /* Kernel-mode fault falls through */ 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cino_context: 14362306a36Sopenharmony_ci fixup = search_exception_tables(pt_elr(regs)); 14462306a36Sopenharmony_ci if (fixup) { 14562306a36Sopenharmony_ci pt_set_elr(regs, fixup->fixup); 14662306a36Sopenharmony_ci return; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* Things are looking very, very bad now */ 15062306a36Sopenharmony_ci bust_spinlocks(1); 15162306a36Sopenharmony_ci printk(KERN_EMERG "Unable to handle kernel paging request at " 15262306a36Sopenharmony_ci "virtual address 0x%08lx, regs %p\n", address, regs); 15362306a36Sopenharmony_ci die("Bad Kernel VA", regs, SIGKILL); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_civoid read_protection_fault(struct pt_regs *regs) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci unsigned long badvadr = pt_badva(regs); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci do_page_fault(badvadr, FLT_LOAD, regs); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_civoid write_protection_fault(struct pt_regs *regs) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci unsigned long badvadr = pt_badva(regs); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci do_page_fault(badvadr, FLT_STORE, regs); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_civoid execute_protection_fault(struct pt_regs *regs) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci unsigned long badvadr = pt_badva(regs); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci do_page_fault(badvadr, FLT_IFETCH, regs); 17662306a36Sopenharmony_ci} 177