]> git.ipfire.org Git - thirdparty/kernel/stable.git/blobdiff - arch/arm64/kernel/traps.c
arm64/sve: Core task context handling
[thirdparty/kernel/stable.git] / arch / arm64 / kernel / traps.c
index 5ea4b85aee0e96e93c80a7aca339ffb2e987a53e..03f8f8ef542d85e247d1eaee1acfcdff73d55fe7 100644 (file)
@@ -38,6 +38,7 @@
 
 #include <asm/atomic.h>
 #include <asm/bug.h>
+#include <asm/daifflags.h>
 #include <asm/debug-monitors.h>
 #include <asm/esr.h>
 #include <asm/insn.h>
@@ -58,55 +59,9 @@ static const char *handler[]= {
 
 int show_unhandled_signals = 1;
 
-/*
- * Dump out the contents of some kernel memory nicely...
- */
-static void dump_mem(const char *lvl, const char *str, unsigned long bottom,
-                    unsigned long top)
-{
-       unsigned long first;
-       mm_segment_t fs;
-       int i;
-
-       /*
-        * We need to switch to kernel mode so that we can use __get_user
-        * to safely read from kernel space.
-        */
-       fs = get_fs();
-       set_fs(KERNEL_DS);
-
-       printk("%s%s(0x%016lx to 0x%016lx)\n", lvl, str, bottom, top);
-
-       for (first = bottom & ~31; first < top; first += 32) {
-               unsigned long p;
-               char str[sizeof(" 12345678") * 8 + 1];
-
-               memset(str, ' ', sizeof(str));
-               str[sizeof(str) - 1] = '\0';
-
-               for (p = first, i = 0; i < (32 / 8)
-                                       && p < top; i++, p += 8) {
-                       if (p >= bottom && p < top) {
-                               unsigned long val;
-
-                               if (__get_user(val, (unsigned long *)p) == 0)
-                                       sprintf(str + i * 17, " %016lx", val);
-                               else
-                                       sprintf(str + i * 17, " ????????????????");
-                       }
-               }
-               printk("%s%04lx:%s\n", lvl, first & 0xffff, str);
-       }
-
-       set_fs(fs);
-}
-
 static void dump_backtrace_entry(unsigned long where)
 {
-       /*
-        * Note that 'where' can have a physical address, but it's not handled.
-        */
-       print_ip_sym(where);
+       printk(" %pS\n", (void *)where);
 }
 
 static void __dump_instr(const char *lvl, struct pt_regs *regs)
@@ -171,10 +126,7 @@ void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
 
        skip = !!regs;
        printk("Call trace:\n");
-       while (1) {
-               unsigned long stack;
-               int ret;
-
+       do {
                /* skip until specified stack frame */
                if (!skip) {
                        dump_backtrace_entry(frame.pc);
@@ -189,17 +141,7 @@ void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
                         */
                        dump_backtrace_entry(regs->pc);
                }
-               ret = unwind_frame(tsk, &frame);
-               if (ret < 0)
-                       break;
-               if (in_entry_text(frame.pc)) {
-                       stack = frame.fp - offsetof(struct pt_regs, stackframe);
-
-                       if (on_accessible_stack(tsk, stack))
-                               dump_mem("", "Exception stack", stack,
-                                        stack + sizeof(struct pt_regs));
-               }
-       }
+       } while (!unwind_frame(tsk, &frame));
 
        put_task_stack(tsk);
 }
@@ -293,6 +235,17 @@ void arm64_notify_die(const char *str, struct pt_regs *regs,
        }
 }
 
+void arm64_skip_faulting_instruction(struct pt_regs *regs, unsigned long size)
+{
+       regs->pc += size;
+
+       /*
+        * If we were single stepping, we want to get the step exception after
+        * we return from the trap.
+        */
+       user_fastforward_single_step(current);
+}
+
 static LIST_HEAD(undef_hook);
 static DEFINE_RAW_SPINLOCK(undef_lock);
 
@@ -358,8 +311,8 @@ exit:
        return fn ? fn(regs, instr) : 1;
 }
 
-static void force_signal_inject(int signal, int code, struct pt_regs *regs,
-                               unsigned long address)
+void force_signal_inject(int signal, int code, struct pt_regs *regs,
+                        unsigned long address)
 {
        siginfo_t info;
        void __user *pc = (void __user *)instruction_pointer(regs);
@@ -373,7 +326,7 @@ static void force_signal_inject(int signal, int code, struct pt_regs *regs,
                desc = "illegal memory access";
                break;
        default:
-               desc = "bad mode";
+               desc = "unknown or unrecoverable error";
                break;
        }
 
@@ -480,7 +433,7 @@ static void user_cache_maint_handler(unsigned int esr, struct pt_regs *regs)
        if (ret)
                arm64_notify_segfault(regs, address);
        else
-               regs->pc += 4;
+               arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
 }
 
 static void ctr_read_handler(unsigned int esr, struct pt_regs *regs)
@@ -490,7 +443,7 @@ static void ctr_read_handler(unsigned int esr, struct pt_regs *regs)
 
        pt_regs_write_reg(regs, rt, val);
 
-       regs->pc += 4;
+       arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
 }
 
 static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs)
@@ -498,7 +451,7 @@ static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs)
        int rt = (esr & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT;
 
        pt_regs_write_reg(regs, rt, arch_counter_get_cntvct());
-       regs->pc += 4;
+       arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
 }
 
 static void cntfrq_read_handler(unsigned int esr, struct pt_regs *regs)
@@ -506,7 +459,7 @@ static void cntfrq_read_handler(unsigned int esr, struct pt_regs *regs)
        int rt = (esr & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT;
 
        pt_regs_write_reg(regs, rt, arch_timer_get_rate());
-       regs->pc += 4;
+       arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
 }
 
 struct sys64_hook {
@@ -603,6 +556,7 @@ static const char *esr_class_str[] = {
        [ESR_ELx_EC_HVC64]              = "HVC (AArch64)",
        [ESR_ELx_EC_SMC64]              = "SMC (AArch64)",
        [ESR_ELx_EC_SYS64]              = "MSR/MRS (AArch64)",
+       [ESR_ELx_EC_SVE]                = "SVE",
        [ESR_ELx_EC_IMP_DEF]            = "EL3 IMP DEF",
        [ESR_ELx_EC_IABT_LOW]           = "IABT (lower EL)",
        [ESR_ELx_EC_IABT_CUR]           = "IABT (current EL)",
@@ -642,7 +596,7 @@ asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr)
                esr_get_class_string(esr));
 
        die("Oops - bad mode", regs, 0);
-       local_irq_disable();
+       local_daif_mask();
        panic("bad mode");
 }
 
@@ -708,6 +662,19 @@ asmlinkage void handle_bad_stack(struct pt_regs *regs)
 }
 #endif
 
+asmlinkage void do_serror(struct pt_regs *regs, unsigned int esr)
+{
+       nmi_enter();
+
+       console_verbose();
+
+       pr_crit("SError Interrupt on CPU%d, code 0x%08x -- %s\n",
+               smp_processor_id(), esr, esr_get_class_string(esr));
+       __show_regs(regs);
+
+       panic("Asynchronous SError Interrupt");
+}
+
 void __pte_error(const char *file, int line, unsigned long val)
 {
        pr_err("%s:%d: bad pte %016lx.\n", file, line, val);
@@ -761,7 +728,7 @@ static int bug_handler(struct pt_regs *regs, unsigned int esr)
        }
 
        /* If thread survives, skip over the BUG instruction and continue: */
-       regs->pc += AARCH64_INSN_SIZE;  /* skip BRK and resume */
+       arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
        return DBG_HOOK_HANDLED;
 }