diff options
Diffstat (limited to 'arch/arm64/mm/fault.c')
-rw-r--r-- | arch/arm64/mm/fault.c | 236 |
1 files changed, 103 insertions, 133 deletions
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index bff11553eb05..4165485e8b6e 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -43,6 +43,7 @@ #include <asm/system_misc.h> #include <asm/pgtable.h> #include <asm/tlbflush.h> +#include <asm/traps.h> #include <acpi/ghes.h> @@ -289,58 +290,31 @@ static void __do_kernel_fault(unsigned long addr, unsigned int esr, do_exit(SIGKILL); } -static void __do_user_fault(struct task_struct *tsk, unsigned long addr, - unsigned int esr, unsigned int sig, int code, - struct pt_regs *regs, int fault) +static void __do_user_fault(struct siginfo *info, unsigned int esr) { - struct siginfo si; - const struct fault_info *inf; - unsigned int lsb = 0; - - if (unhandled_signal(tsk, sig) && show_unhandled_signals_ratelimited()) { - inf = esr_to_fault_info(esr); - pr_info("%s[%d]: unhandled %s (%d) at 0x%08lx, esr 0x%03x", - tsk->comm, task_pid_nr(tsk), inf->name, sig, - addr, esr); - print_vma_addr(KERN_CONT ", in ", regs->pc); - pr_cont("\n"); - __show_regs(regs); - } - - tsk->thread.fault_address = addr; - tsk->thread.fault_code = esr; - si.si_signo = sig; - si.si_errno = 0; - si.si_code = code; - si.si_addr = (void __user *)addr; - /* - * Either small page or large page may be poisoned. - * In other words, VM_FAULT_HWPOISON_LARGE and - * VM_FAULT_HWPOISON are mutually exclusive. - */ - if (fault & VM_FAULT_HWPOISON_LARGE) - lsb = hstate_index_to_shift(VM_FAULT_GET_HINDEX(fault)); - else if (fault & VM_FAULT_HWPOISON) - lsb = PAGE_SHIFT; - si.si_addr_lsb = lsb; - - force_sig_info(sig, &si, tsk); + current->thread.fault_address = (unsigned long)info->si_addr; + current->thread.fault_code = esr; + arm64_force_sig_info(info, esr_to_fault_info(esr)->name, current); } static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *regs) { - struct task_struct *tsk = current; - const struct fault_info *inf; - /* * If we are in kernel mode at this point, we have no context to * handle this fault with. */ if (user_mode(regs)) { - inf = esr_to_fault_info(esr); - __do_user_fault(tsk, addr, esr, inf->sig, inf->code, regs, 0); - } else + const struct fault_info *inf = esr_to_fault_info(esr); + struct siginfo si = { + .si_signo = inf->sig, + .si_code = inf->code, + .si_addr = (void __user *)addr, + }; + + __do_user_fault(&si, esr); + } else { __do_kernel_fault(addr, esr, regs); + } } #define VM_FAULT_BADMAP 0x010000 @@ -393,7 +367,8 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr, { struct task_struct *tsk; struct mm_struct *mm; - int fault, sig, code, major = 0; + struct siginfo si; + int fault, major = 0; unsigned long vm_flags = VM_READ | VM_WRITE; unsigned int mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; @@ -525,27 +500,37 @@ retry: return 0; } + clear_siginfo(&si); + si.si_addr = (void __user *)addr; + if (fault & VM_FAULT_SIGBUS) { /* * We had some memory, but were unable to successfully fix up * this page fault. */ - sig = SIGBUS; - code = BUS_ADRERR; - } else if (fault & (VM_FAULT_HWPOISON | VM_FAULT_HWPOISON_LARGE)) { - sig = SIGBUS; - code = BUS_MCEERR_AR; + si.si_signo = SIGBUS; + si.si_code = BUS_ADRERR; + } else if (fault & VM_FAULT_HWPOISON_LARGE) { + unsigned int hindex = VM_FAULT_GET_HINDEX(fault); + + si.si_signo = SIGBUS; + si.si_code = BUS_MCEERR_AR; + si.si_addr_lsb = hstate_index_to_shift(hindex); + } else if (fault & VM_FAULT_HWPOISON) { + si.si_signo = SIGBUS; + si.si_code = BUS_MCEERR_AR; + si.si_addr_lsb = PAGE_SHIFT; } else { /* * Something tried to access memory that isn't in our memory * map. */ - sig = SIGSEGV; - code = fault == VM_FAULT_BADACCESS ? - SEGV_ACCERR : SEGV_MAPERR; + si.si_signo = SIGSEGV; + si.si_code = fault == VM_FAULT_BADACCESS ? + SEGV_ACCERR : SEGV_MAPERR; } - __do_user_fault(tsk, addr, esr, sig, code, regs, fault); + __do_user_fault(&si, esr); return 0; no_context: @@ -582,8 +567,6 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs) const struct fault_info *inf; inf = esr_to_fault_info(esr); - pr_err("Synchronous External Abort: %s (0x%08x) at 0x%016lx\n", - inf->name, esr, addr); /* * Synchronous aborts may interrupt code which had interrupts masked. @@ -600,83 +583,83 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs) nmi_exit(); } - info.si_signo = SIGBUS; + info.si_signo = inf->sig; info.si_errno = 0; - info.si_code = BUS_FIXME; + info.si_code = inf->code; if (esr & ESR_ELx_FnV) info.si_addr = NULL; else info.si_addr = (void __user *)addr; - arm64_notify_die("", regs, &info, esr); + arm64_notify_die(inf->name, regs, &info, esr); return 0; } static const struct fault_info fault_info[] = { - { do_bad, SIGBUS, BUS_FIXME, "ttbr address size fault" }, - { do_bad, SIGBUS, BUS_FIXME, "level 1 address size fault" }, - { do_bad, SIGBUS, BUS_FIXME, "level 2 address size fault" }, - { do_bad, SIGBUS, BUS_FIXME, "level 3 address size fault" }, + { do_bad, SIGKILL, SI_KERNEL, "ttbr address size fault" }, + { do_bad, SIGKILL, SI_KERNEL, "level 1 address size fault" }, + { do_bad, SIGKILL, SI_KERNEL, "level 2 address size fault" }, + { do_bad, SIGKILL, SI_KERNEL, "level 3 address size fault" }, { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 0 translation fault" }, { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 1 translation fault" }, { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 2 translation fault" }, { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 3 translation fault" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 8" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 8" }, { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 1 access flag fault" }, { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 access flag fault" }, { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 access flag fault" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 12" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 12" }, { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 1 permission fault" }, { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 permission fault" }, { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 permission fault" }, - { do_sea, SIGBUS, BUS_FIXME, "synchronous external abort" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 17" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 18" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 19" }, - { do_sea, SIGBUS, BUS_FIXME, "level 0 (translation table walk)" }, - { do_sea, SIGBUS, BUS_FIXME, "level 1 (translation table walk)" }, - { do_sea, SIGBUS, BUS_FIXME, "level 2 (translation table walk)" }, - { do_sea, SIGBUS, BUS_FIXME, "level 3 (translation table walk)" }, - { do_sea, SIGBUS, BUS_FIXME, "synchronous parity or ECC error" }, // Reserved when RAS is implemented - { do_bad, SIGBUS, BUS_FIXME, "unknown 25" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 26" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 27" }, - { do_sea, SIGBUS, BUS_FIXME, "level 0 synchronous parity error (translation table walk)" }, // Reserved when RAS is implemented - { do_sea, SIGBUS, BUS_FIXME, "level 1 synchronous parity error (translation table walk)" }, // Reserved when RAS is implemented - { do_sea, SIGBUS, BUS_FIXME, "level 2 synchronous parity error (translation table walk)" }, // Reserved when RAS is implemented - { do_sea, SIGBUS, BUS_FIXME, "level 3 synchronous parity error (translation table walk)" }, // Reserved when RAS is implemented - { do_bad, SIGBUS, BUS_FIXME, "unknown 32" }, + { do_sea, SIGBUS, BUS_OBJERR, "synchronous external abort" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 17" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 18" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 19" }, + { do_sea, SIGKILL, SI_KERNEL, "level 0 (translation table walk)" }, + { do_sea, SIGKILL, SI_KERNEL, "level 1 (translation table walk)" }, + { do_sea, SIGKILL, SI_KERNEL, "level 2 (translation table walk)" }, + { do_sea, SIGKILL, SI_KERNEL, "level 3 (translation table walk)" }, + { do_sea, SIGBUS, BUS_OBJERR, "synchronous parity or ECC error" }, // Reserved when RAS is implemented + { do_bad, SIGKILL, SI_KERNEL, "unknown 25" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 26" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 27" }, + { do_sea, SIGKILL, SI_KERNEL, "level 0 synchronous parity error (translation table walk)" }, // Reserved when RAS is implemented + { do_sea, SIGKILL, SI_KERNEL, "level 1 synchronous parity error (translation table walk)" }, // Reserved when RAS is implemented + { do_sea, SIGKILL, SI_KERNEL, "level 2 synchronous parity error (translation table walk)" }, // Reserved when RAS is implemented + { do_sea, SIGKILL, SI_KERNEL, "level 3 synchronous parity error (translation table walk)" }, // Reserved when RAS is implemented + { do_bad, SIGKILL, SI_KERNEL, "unknown 32" }, { do_alignment_fault, SIGBUS, BUS_ADRALN, "alignment fault" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 34" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 35" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 36" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 37" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 38" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 39" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 40" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 41" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 42" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 43" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 44" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 45" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 46" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 47" }, - { do_bad, SIGBUS, BUS_FIXME, "TLB conflict abort" }, - { do_bad, SIGBUS, BUS_FIXME, "Unsupported atomic hardware update fault" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 50" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 51" }, - { do_bad, SIGBUS, BUS_FIXME, "implementation fault (lockdown abort)" }, - { do_bad, SIGBUS, BUS_FIXME, "implementation fault (unsupported exclusive)" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 54" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 55" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 56" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 57" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 58" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 59" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 60" }, - { do_bad, SIGBUS, BUS_FIXME, "section domain fault" }, - { do_bad, SIGBUS, BUS_FIXME, "page domain fault" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 63" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 34" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 35" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 36" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 37" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 38" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 39" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 40" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 41" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 42" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 43" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 44" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 45" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 46" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 47" }, + { do_bad, SIGKILL, SI_KERNEL, "TLB conflict abort" }, + { do_bad, SIGKILL, SI_KERNEL, "Unsupported atomic hardware update fault" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 50" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 51" }, + { do_bad, SIGKILL, SI_KERNEL, "implementation fault (lockdown abort)" }, + { do_bad, SIGBUS, BUS_OBJERR, "implementation fault (unsupported exclusive)" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 54" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 55" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 56" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 57" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 58" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 59" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 60" }, + { do_bad, SIGKILL, SI_KERNEL, "section domain fault" }, + { do_bad, SIGKILL, SI_KERNEL, "page domain fault" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 63" }, }; int handle_guest_sea(phys_addr_t addr, unsigned int esr) @@ -698,19 +681,17 @@ asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr, if (!inf->fn(addr, esr, regs)) return; - pr_alert("Unhandled fault: %s at 0x%016lx\n", - inf->name, addr); - - mem_abort_decode(esr); - - if (!user_mode(regs)) + if (!user_mode(regs)) { + pr_alert("Unhandled fault at 0x%016lx\n", addr); + mem_abort_decode(esr); show_pte(addr); + } info.si_signo = inf->sig; info.si_errno = 0; info.si_code = inf->code; info.si_addr = (void __user *)addr; - arm64_notify_die("", regs, &info, esr); + arm64_notify_die(inf->name, regs, &info, esr); } asmlinkage void __exception do_el0_irq_bp_hardening(void) @@ -741,7 +722,6 @@ asmlinkage void __exception do_sp_pc_abort(unsigned long addr, struct pt_regs *regs) { struct siginfo info; - struct task_struct *tsk = current; if (user_mode(regs)) { if (instruction_pointer(regs) > TASK_SIZE) @@ -749,17 +729,11 @@ asmlinkage void __exception do_sp_pc_abort(unsigned long addr, local_irq_enable(); } - if (show_unhandled_signals && unhandled_signal(tsk, SIGBUS)) - pr_info_ratelimited("%s[%d]: %s exception: pc=%p sp=%p\n", - tsk->comm, task_pid_nr(tsk), - esr_get_class_string(esr), (void *)regs->pc, - (void *)regs->sp); - info.si_signo = SIGBUS; info.si_errno = 0; info.si_code = BUS_ADRALN; info.si_addr = (void __user *)addr; - arm64_notify_die("Oops - SP/PC alignment exception", regs, &info, esr); + arm64_notify_die("SP/PC alignment exception", regs, &info, esr); } int __init early_brk64(unsigned long addr, unsigned int esr, @@ -774,11 +748,11 @@ static struct fault_info __refdata debug_fault_info[] = { { do_bad, SIGTRAP, TRAP_HWBKPT, "hardware breakpoint" }, { do_bad, SIGTRAP, TRAP_HWBKPT, "hardware single-step" }, { do_bad, SIGTRAP, TRAP_HWBKPT, "hardware watchpoint" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 3" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 3" }, { do_bad, SIGTRAP, TRAP_BRKPT, "aarch32 BKPT" }, - { do_bad, SIGTRAP, TRAP_FIXME, "aarch32 vector catch" }, + { do_bad, SIGKILL, SI_KERNEL, "aarch32 vector catch" }, { early_brk64, SIGTRAP, TRAP_BRKPT, "aarch64 BRK" }, - { do_bad, SIGBUS, BUS_FIXME, "unknown 7" }, + { do_bad, SIGKILL, SI_KERNEL, "unknown 7" }, }; void __init hook_debug_fault_code(int nr, @@ -814,14 +788,11 @@ asmlinkage int __exception do_debug_exception(unsigned long addr, if (!inf->fn(addr, esr, regs)) { rv = 1; } else { - pr_alert("Unhandled debug exception: %s (0x%08x) at 0x%016lx\n", - inf->name, esr, addr); - info.si_signo = inf->sig; info.si_errno = 0; info.si_code = inf->code; info.si_addr = (void __user *)addr; - arm64_notify_die("", regs, &info, 0); + arm64_notify_die(inf->name, regs, &info, esr); rv = 0; } @@ -833,7 +804,7 @@ asmlinkage int __exception do_debug_exception(unsigned long addr, NOKPROBE_SYMBOL(do_debug_exception); #ifdef CONFIG_ARM64_PAN -int cpu_enable_pan(void *__unused) +void cpu_enable_pan(const struct arm64_cpu_capabilities *__unused) { /* * We modify PSTATE. This won't work from irq context as the PSTATE @@ -843,6 +814,5 @@ int cpu_enable_pan(void *__unused) config_sctlr_el1(SCTLR_EL1_SPAN, 0); asm(SET_PSTATE_PAN(1)); - return 0; } #endif /* CONFIG_ARM64_PAN */ |