diff options
Diffstat (limited to 'arch/arm64/mm/fault.c')
| -rw-r--r-- | arch/arm64/mm/fault.c | 57 |
1 files changed, 52 insertions, 5 deletions
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index f243132b426c..1f9a2620c692 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -40,9 +40,35 @@ #include <asm/system_misc.h> #include <asm/pgtable.h> #include <asm/tlbflush.h> +#include <asm/edac.h> +#include <soc/qcom/scm.h> + +#include <trace/events/exception.h> static const char *fault_name(unsigned int esr); +#ifdef CONFIG_KPROBES +static inline int notify_page_fault(struct pt_regs *regs, unsigned int esr) +{ + int ret = 0; + + /* kprobe_running() needs smp_processor_id() */ + if (!user_mode(regs)) { + preempt_disable(); + if (kprobe_running() && kprobe_fault_handler(regs, esr)) + ret = 1; + preempt_enable(); + } + + return ret; +} +#else +static inline int notify_page_fault(struct pt_regs *regs, unsigned int esr) +{ + return 0; +} +#endif + /* * Dump out the page tables associated with 'addr' in mm 'mm'. */ @@ -175,6 +201,8 @@ static void __do_user_fault(struct task_struct *tsk, unsigned long addr, { struct siginfo si; + trace_user_fault(tsk, addr, esr); + if (unhandled_signal(tsk, sig) && show_unhandled_signals_ratelimited()) { pr_info("%s[%d]: unhandled %s (%d) at 0x%08lx, esr 0x%03x\n", tsk->comm, task_pid_nr(tsk), fault_name(esr), sig, @@ -277,6 +305,9 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr, unsigned long vm_flags = VM_READ | VM_WRITE | VM_EXEC; unsigned int mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; + if (notify_page_fault(regs, esr)) + return 0; + tsk = current; mm = tsk->mm; @@ -296,16 +327,13 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr, if (is_el0_instruction_abort(esr)) { vm_flags = VM_EXEC; - } else if ((esr & ESR_ELx_WNR) && !(esr & ESR_ELx_CM)) { + } else if (((esr & ESR_ELx_WNR) && !(esr & ESR_ELx_CM)) || + ((esr & ESR_ELx_CM) && !(mm_flags & FAULT_FLAG_USER))) { vm_flags = VM_WRITE; mm_flags |= FAULT_FLAG_WRITE; } if (addr < USER_DS && is_permission_fault(esr, regs)) { - /* regs->orig_addr_limit may be 0 if we entered from EL0 */ - if (regs->orig_addr_limit == KERNEL_DS) - die("Accessing user space memory with fs=KERNEL_DS", regs, esr); - if (is_el1_instruction_abort(esr)) die("Attempting to execute userspace memory", regs, esr); @@ -428,6 +456,19 @@ no_context: } /* + * TLB conflict is already handled in EL2. This rourtine should return zero + * so that, do_mem_abort would not crash kernel thinking TLB conflict not + * handled. +*/ +#ifdef CONFIG_QCOM_TLB_EL2_HANDLER +static int do_tlb_conf_fault(unsigned long addr, + unsigned int esr, + struct pt_regs *regs) +{ + return 0; +} +#endif +/* * First Level Translation Fault Handler * * We enter here because the first level page table doesn't contain a valid @@ -460,6 +501,7 @@ static int __kprobes do_translation_fault(unsigned long addr, */ static int do_bad(unsigned long addr, unsigned int esr, struct pt_regs *regs) { + arm64_check_cache_ecc(NULL); return 1; } @@ -517,7 +559,11 @@ static const struct fault_info { { do_bad, SIGBUS, 0, "unknown 45" }, { do_bad, SIGBUS, 0, "unknown 46" }, { do_bad, SIGBUS, 0, "unknown 47" }, +#ifdef CONFIG_QCOM_TLB_EL2_HANDLER + { do_tlb_conf_fault, SIGBUS, 0, "TLB conflict abort" }, +#else { do_bad, SIGBUS, 0, "TLB conflict abort" }, +#endif { do_bad, SIGBUS, 0, "unknown 49" }, { do_bad, SIGBUS, 0, "unknown 50" }, { do_bad, SIGBUS, 0, "unknown 51" }, @@ -638,6 +684,7 @@ asmlinkage int __exception do_debug_exception(unsigned long addr, return 0; } +NOKPROBE_SYMBOL(do_debug_exception); #ifdef CONFIG_ARM64_PAN int cpu_enable_pan(void *__unused) |
