]> git.samba.org - sfrench/cifs-2.6.git/blobdiff - arch/arm64/kernel/traps.c
Merge tag 'secureexec-v4.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.git] / arch / arm64 / kernel / traps.c
index 8a62648848e5754a747649078206e782ef550172..5ea4b85aee0e96e93c80a7aca339ffb2e987a53e 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/sched/signal.h>
 #include <linux/sched/debug.h>
 #include <linux/sched/task_stack.h>
+#include <linux/sizes.h>
 #include <linux/syscalls.h>
 #include <linux/mm_types.h>
 
@@ -41,6 +42,7 @@
 #include <asm/esr.h>
 #include <asm/insn.h>
 #include <asm/traps.h>
+#include <asm/smp.h>
 #include <asm/stack_pointer.h>
 #include <asm/stacktrace.h>
 #include <asm/exception.h>
@@ -143,7 +145,6 @@ static void dump_instr(const char *lvl, struct pt_regs *regs)
 void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
 {
        struct stackframe frame;
-       unsigned long irq_stack_ptr;
        int skip;
 
        pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk);
@@ -154,25 +155,14 @@ void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
        if (!try_get_task_stack(tsk))
                return;
 
-       /*
-        * Switching between stacks is valid when tracing current and in
-        * non-preemptible context.
-        */
-       if (tsk == current && !preemptible())
-               irq_stack_ptr = IRQ_STACK_PTR(smp_processor_id());
-       else
-               irq_stack_ptr = 0;
-
        if (tsk == current) {
                frame.fp = (unsigned long)__builtin_frame_address(0);
-               frame.sp = current_stack_pointer;
                frame.pc = (unsigned long)dump_backtrace;
        } else {
                /*
                 * task blocked in __switch_to
                 */
                frame.fp = thread_saved_fp(tsk);
-               frame.sp = thread_saved_sp(tsk);
                frame.pc = thread_saved_pc(tsk);
        }
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
@@ -182,13 +172,12 @@ void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
        skip = !!regs;
        printk("Call trace:\n");
        while (1) {
-               unsigned long where = frame.pc;
                unsigned long stack;
                int ret;
 
                /* skip until specified stack frame */
                if (!skip) {
-                       dump_backtrace_entry(where);
+                       dump_backtrace_entry(frame.pc);
                } else if (frame.fp == regs->regs[29]) {
                        skip = 0;
                        /*
@@ -203,20 +192,12 @@ void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
                ret = unwind_frame(tsk, &frame);
                if (ret < 0)
                        break;
-               stack = frame.sp;
-               if (in_exception_text(where)) {
-                       /*
-                        * If we switched to the irq_stack before calling this
-                        * exception handler, then the pt_regs will be on the
-                        * task stack. The easiest way to tell is if the large
-                        * pt_regs would overlap with the end of the irq_stack.
-                        */
-                       if (stack < irq_stack_ptr &&
-                           (stack + sizeof(struct pt_regs)) > irq_stack_ptr)
-                               stack = IRQ_STACK_TO_TASK_STACK(irq_stack_ptr);
+               if (in_entry_text(frame.pc)) {
+                       stack = frame.fp - offsetof(struct pt_regs, stackframe);
 
-                       dump_mem("", "Exception stack", stack,
-                                stack + sizeof(struct pt_regs));
+                       if (on_accessible_stack(tsk, stack))
+                               dump_mem("", "Exception stack", stack,
+                                        stack + sizeof(struct pt_regs));
                }
        }
 
@@ -257,8 +238,6 @@ static int __die(const char *str, int err, struct pt_regs *regs)
                 end_of_stack(tsk));
 
        if (!user_mode(regs)) {
-               dump_mem(KERN_EMERG, "Stack: ", regs->sp,
-                        THREAD_SIZE + (unsigned long)task_stack_page(tsk));
                dump_backtrace(regs, tsk);
                dump_instr(KERN_EMERG, regs);
        }
@@ -484,6 +463,9 @@ static void user_cache_maint_handler(unsigned int esr, struct pt_regs *regs)
        case ESR_ELx_SYS64_ISS_CRM_DC_CVAC:     /* DC CVAC, gets promoted */
                __user_cache_maint("dc civac", address, ret);
                break;
+       case ESR_ELx_SYS64_ISS_CRM_DC_CVAP:     /* DC CVAP */
+               __user_cache_maint("sys 3, c7, c12, 1", address, ret);
+               break;
        case ESR_ELx_SYS64_ISS_CRM_DC_CIVAC:    /* DC CIVAC */
                __user_cache_maint("dc civac", address, ret);
                break;
@@ -593,7 +575,7 @@ asmlinkage long do_ni_syscall(struct pt_regs *regs)
 
        if (show_unhandled_signals_ratelimited()) {
                pr_info("%s[%d]: syscall %d\n", current->comm,
-                       task_pid_nr(current), (int)regs->syscallno);
+                       task_pid_nr(current), regs->syscallno);
                dump_instr("", regs);
                if (user_mode(regs))
                        __show_regs(regs);
@@ -689,6 +671,43 @@ asmlinkage void bad_el0_sync(struct pt_regs *regs, int reason, unsigned int esr)
        force_sig_info(info.si_signo, &info, current);
 }
 
+#ifdef CONFIG_VMAP_STACK
+
+DEFINE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack)
+       __aligned(16);
+
+asmlinkage void handle_bad_stack(struct pt_regs *regs)
+{
+       unsigned long tsk_stk = (unsigned long)current->stack;
+       unsigned long irq_stk = (unsigned long)this_cpu_read(irq_stack_ptr);
+       unsigned long ovf_stk = (unsigned long)this_cpu_ptr(overflow_stack);
+       unsigned int esr = read_sysreg(esr_el1);
+       unsigned long far = read_sysreg(far_el1);
+
+       console_verbose();
+       pr_emerg("Insufficient stack space to handle exception!");
+
+       pr_emerg("ESR: 0x%08x -- %s\n", esr, esr_get_class_string(esr));
+       pr_emerg("FAR: 0x%016lx\n", far);
+
+       pr_emerg("Task stack:     [0x%016lx..0x%016lx]\n",
+                tsk_stk, tsk_stk + THREAD_SIZE);
+       pr_emerg("IRQ stack:      [0x%016lx..0x%016lx]\n",
+                irq_stk, irq_stk + THREAD_SIZE);
+       pr_emerg("Overflow stack: [0x%016lx..0x%016lx]\n",
+                ovf_stk, ovf_stk + OVERFLOW_STACK_SIZE);
+
+       __show_regs(regs);
+
+       /*
+        * We use nmi_panic to limit the potential for recusive overflows, and
+        * to get a better stack trace.
+        */
+       nmi_panic(NULL, "kernel stack overflow");
+       cpu_park_loop();
+}
+#endif
+
 void __pte_error(const char *file, int line, unsigned long val)
 {
        pr_err("%s:%d: bad pte %016lx.\n", file, line, val);