Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / arch / arm64 / kernel / traps.c
diff --git a/kernel/arch/arm64/kernel/traps.c b/kernel/arch/arm64/kernel/traps.c
new file mode 100644 (file)
index 0000000..1ef2940
--- /dev/null
@@ -0,0 +1,466 @@
+/*
+ * Based on arch/arm/kernel/traps.c
+ *
+ * Copyright (C) 1995-2009 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/signal.h>
+#include <linux/personality.h>
+#include <linux/kallsyms.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/hardirq.h>
+#include <linux/kdebug.h>
+#include <linux/module.h>
+#include <linux/kexec.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/syscalls.h>
+
+#include <asm/atomic.h>
+#include <asm/debug-monitors.h>
+#include <asm/esr.h>
+#include <asm/traps.h>
+#include <asm/stacktrace.h>
+#include <asm/exception.h>
+#include <asm/system_misc.h>
+
+static const char *handler[]= {
+       "Synchronous Abort",
+       "IRQ",
+       "FIQ",
+       "Error"
+};
+
+int show_unhandled_signals = 1;
+
+/*
+ * Dump out the contents of some 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.  Note that we now dump the
+        * code first, just in case the backtrace kills us.
+        */
+       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 < 8 && p < top; i++, p += 4) {
+                       if (p >= bottom && p < top) {
+                               unsigned int val;
+                               if (__get_user(val, (unsigned int *)p) == 0)
+                                       sprintf(str + i * 9, " %08x", val);
+                               else
+                                       sprintf(str + i * 9, " ????????");
+                       }
+               }
+               printk("%s%04lx:%s\n", lvl, first & 0xffff, str);
+       }
+
+       set_fs(fs);
+}
+
+static void dump_backtrace_entry(unsigned long where, unsigned long stack)
+{
+       print_ip_sym(where);
+       if (in_exception_text(where))
+               dump_mem("", "Exception stack", stack,
+                        stack + sizeof(struct pt_regs));
+}
+
+static void dump_instr(const char *lvl, struct pt_regs *regs)
+{
+       unsigned long addr = instruction_pointer(regs);
+       mm_segment_t fs;
+       char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str;
+       int i;
+
+       /*
+        * We need to switch to kernel mode so that we can use __get_user
+        * to safely read from kernel space.  Note that we now dump the
+        * code first, just in case the backtrace kills us.
+        */
+       fs = get_fs();
+       set_fs(KERNEL_DS);
+
+       for (i = -4; i < 1; i++) {
+               unsigned int val, bad;
+
+               bad = __get_user(val, &((u32 *)addr)[i]);
+
+               if (!bad)
+                       p += sprintf(p, i == 0 ? "(%08x) " : "%08x ", val);
+               else {
+                       p += sprintf(p, "bad PC value");
+                       break;
+               }
+       }
+       printk("%sCode: %s\n", lvl, str);
+
+       set_fs(fs);
+}
+
+static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
+{
+       struct stackframe frame;
+
+       pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk);
+
+       if (!tsk)
+               tsk = current;
+
+       if (regs) {
+               frame.fp = regs->regs[29];
+               frame.sp = regs->sp;
+               frame.pc = regs->pc;
+       } else 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);
+       }
+
+       pr_emerg("Call trace:\n");
+       while (1) {
+               unsigned long where = frame.pc;
+               int ret;
+
+               ret = unwind_frame(&frame);
+               if (ret < 0)
+                       break;
+               dump_backtrace_entry(where, frame.sp);
+       }
+}
+
+void show_stack(struct task_struct *tsk, unsigned long *sp)
+{
+       dump_backtrace(NULL, tsk);
+       barrier();
+}
+
+#ifdef CONFIG_PREEMPT
+#define S_PREEMPT " PREEMPT"
+#else
+#define S_PREEMPT ""
+#endif
+#ifdef CONFIG_SMP
+#define S_SMP " SMP"
+#else
+#define S_SMP ""
+#endif
+
+static int __die(const char *str, int err, struct thread_info *thread,
+                struct pt_regs *regs)
+{
+       struct task_struct *tsk = thread->task;
+       static int die_counter;
+       int ret;
+
+       pr_emerg("Internal error: %s: %x [#%d]" S_PREEMPT S_SMP "\n",
+                str, err, ++die_counter);
+
+       /* trap and error numbers are mostly meaningless on ARM */
+       ret = notify_die(DIE_OOPS, str, regs, err, 0, SIGSEGV);
+       if (ret == NOTIFY_STOP)
+               return ret;
+
+       print_modules();
+       __show_regs(regs);
+       pr_emerg("Process %.*s (pid: %d, stack limit = 0x%p)\n",
+                TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), thread + 1);
+
+       if (!user_mode(regs) || in_interrupt()) {
+               dump_mem(KERN_EMERG, "Stack: ", regs->sp,
+                        THREAD_SIZE + (unsigned long)task_stack_page(tsk));
+               dump_backtrace(regs, tsk);
+               dump_instr(KERN_EMERG, regs);
+       }
+
+       return ret;
+}
+
+static DEFINE_RAW_SPINLOCK(die_lock);
+
+/*
+ * This function is protected against re-entrancy.
+ */
+void die(const char *str, struct pt_regs *regs, int err)
+{
+       struct thread_info *thread = current_thread_info();
+       int ret;
+
+       oops_enter();
+
+       raw_spin_lock_irq(&die_lock);
+       console_verbose();
+       bust_spinlocks(1);
+       ret = __die(str, err, thread, regs);
+
+       if (regs && kexec_should_crash(thread->task))
+               crash_kexec(regs);
+
+       bust_spinlocks(0);
+       add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);
+       raw_spin_unlock_irq(&die_lock);
+       oops_exit();
+
+       if (in_interrupt())
+               panic("Fatal exception in interrupt");
+       if (panic_on_oops)
+               panic("Fatal exception");
+       if (ret != NOTIFY_STOP)
+               do_exit(SIGSEGV);
+}
+
+void arm64_notify_die(const char *str, struct pt_regs *regs,
+                     struct siginfo *info, int err)
+{
+       if (user_mode(regs)) {
+               current->thread.fault_address = 0;
+               current->thread.fault_code = err;
+               force_sig_info(info->si_signo, info, current);
+       } else {
+               die(str, regs, err);
+       }
+}
+
+static LIST_HEAD(undef_hook);
+static DEFINE_RAW_SPINLOCK(undef_lock);
+
+void register_undef_hook(struct undef_hook *hook)
+{
+       unsigned long flags;
+
+       raw_spin_lock_irqsave(&undef_lock, flags);
+       list_add(&hook->node, &undef_hook);
+       raw_spin_unlock_irqrestore(&undef_lock, flags);
+}
+
+void unregister_undef_hook(struct undef_hook *hook)
+{
+       unsigned long flags;
+
+       raw_spin_lock_irqsave(&undef_lock, flags);
+       list_del(&hook->node);
+       raw_spin_unlock_irqrestore(&undef_lock, flags);
+}
+
+static int call_undef_hook(struct pt_regs *regs)
+{
+       struct undef_hook *hook;
+       unsigned long flags;
+       u32 instr;
+       int (*fn)(struct pt_regs *regs, u32 instr) = NULL;
+       void __user *pc = (void __user *)instruction_pointer(regs);
+
+       if (!user_mode(regs))
+               return 1;
+
+       if (compat_thumb_mode(regs)) {
+               /* 16-bit Thumb instruction */
+               if (get_user(instr, (u16 __user *)pc))
+                       goto exit;
+               instr = le16_to_cpu(instr);
+               if (aarch32_insn_is_wide(instr)) {
+                       u32 instr2;
+
+                       if (get_user(instr2, (u16 __user *)(pc + 2)))
+                               goto exit;
+                       instr2 = le16_to_cpu(instr2);
+                       instr = (instr << 16) | instr2;
+               }
+       } else {
+               /* 32-bit ARM instruction */
+               if (get_user(instr, (u32 __user *)pc))
+                       goto exit;
+               instr = le32_to_cpu(instr);
+       }
+
+       raw_spin_lock_irqsave(&undef_lock, flags);
+       list_for_each_entry(hook, &undef_hook, node)
+               if ((instr & hook->instr_mask) == hook->instr_val &&
+                       (regs->pstate & hook->pstate_mask) == hook->pstate_val)
+                       fn = hook->fn;
+
+       raw_spin_unlock_irqrestore(&undef_lock, flags);
+exit:
+       return fn ? fn(regs, instr) : 1;
+}
+
+asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
+{
+       siginfo_t info;
+       void __user *pc = (void __user *)instruction_pointer(regs);
+
+       /* check for AArch32 breakpoint instructions */
+       if (!aarch32_break_handler(regs))
+               return;
+
+       if (call_undef_hook(regs) == 0)
+               return;
+
+       if (show_unhandled_signals && unhandled_signal(current, SIGILL) &&
+           printk_ratelimit()) {
+               pr_info("%s[%d]: undefined instruction: pc=%p\n",
+                       current->comm, task_pid_nr(current), pc);
+               dump_instr(KERN_INFO, regs);
+       }
+
+       info.si_signo = SIGILL;
+       info.si_errno = 0;
+       info.si_code  = ILL_ILLOPC;
+       info.si_addr  = pc;
+
+       arm64_notify_die("Oops - undefined instruction", regs, &info, 0);
+}
+
+long compat_arm_syscall(struct pt_regs *regs);
+
+asmlinkage long do_ni_syscall(struct pt_regs *regs)
+{
+#ifdef CONFIG_COMPAT
+       long ret;
+       if (is_compat_task()) {
+               ret = compat_arm_syscall(regs);
+               if (ret != -ENOSYS)
+                       return ret;
+       }
+#endif
+
+       if (show_unhandled_signals && printk_ratelimit()) {
+               pr_info("%s[%d]: syscall %d\n", current->comm,
+                       task_pid_nr(current), (int)regs->syscallno);
+               dump_instr("", regs);
+               if (user_mode(regs))
+                       __show_regs(regs);
+       }
+
+       return sys_ni_syscall();
+}
+
+static const char *esr_class_str[] = {
+       [0 ... ESR_ELx_EC_MAX]          = "UNRECOGNIZED EC",
+       [ESR_ELx_EC_UNKNOWN]            = "Unknown/Uncategorized",
+       [ESR_ELx_EC_WFx]                = "WFI/WFE",
+       [ESR_ELx_EC_CP15_32]            = "CP15 MCR/MRC",
+       [ESR_ELx_EC_CP15_64]            = "CP15 MCRR/MRRC",
+       [ESR_ELx_EC_CP14_MR]            = "CP14 MCR/MRC",
+       [ESR_ELx_EC_CP14_LS]            = "CP14 LDC/STC",
+       [ESR_ELx_EC_FP_ASIMD]           = "ASIMD",
+       [ESR_ELx_EC_CP10_ID]            = "CP10 MRC/VMRS",
+       [ESR_ELx_EC_CP14_64]            = "CP14 MCRR/MRRC",
+       [ESR_ELx_EC_ILL]                = "PSTATE.IL",
+       [ESR_ELx_EC_SVC32]              = "SVC (AArch32)",
+       [ESR_ELx_EC_HVC32]              = "HVC (AArch32)",
+       [ESR_ELx_EC_SMC32]              = "SMC (AArch32)",
+       [ESR_ELx_EC_SVC64]              = "SVC (AArch64)",
+       [ESR_ELx_EC_HVC64]              = "HVC (AArch64)",
+       [ESR_ELx_EC_SMC64]              = "SMC (AArch64)",
+       [ESR_ELx_EC_SYS64]              = "MSR/MRS (AArch64)",
+       [ESR_ELx_EC_IMP_DEF]            = "EL3 IMP DEF",
+       [ESR_ELx_EC_IABT_LOW]           = "IABT (lower EL)",
+       [ESR_ELx_EC_IABT_CUR]           = "IABT (current EL)",
+       [ESR_ELx_EC_PC_ALIGN]           = "PC Alignment",
+       [ESR_ELx_EC_DABT_LOW]           = "DABT (lower EL)",
+       [ESR_ELx_EC_DABT_CUR]           = "DABT (current EL)",
+       [ESR_ELx_EC_SP_ALIGN]           = "SP Alignment",
+       [ESR_ELx_EC_FP_EXC32]           = "FP (AArch32)",
+       [ESR_ELx_EC_FP_EXC64]           = "FP (AArch64)",
+       [ESR_ELx_EC_SERROR]             = "SError",
+       [ESR_ELx_EC_BREAKPT_LOW]        = "Breakpoint (lower EL)",
+       [ESR_ELx_EC_BREAKPT_CUR]        = "Breakpoint (current EL)",
+       [ESR_ELx_EC_SOFTSTP_LOW]        = "Software Step (lower EL)",
+       [ESR_ELx_EC_SOFTSTP_CUR]        = "Software Step (current EL)",
+       [ESR_ELx_EC_WATCHPT_LOW]        = "Watchpoint (lower EL)",
+       [ESR_ELx_EC_WATCHPT_CUR]        = "Watchpoint (current EL)",
+       [ESR_ELx_EC_BKPT32]             = "BKPT (AArch32)",
+       [ESR_ELx_EC_VECTOR32]           = "Vector catch (AArch32)",
+       [ESR_ELx_EC_BRK64]              = "BRK (AArch64)",
+};
+
+const char *esr_get_class_string(u32 esr)
+{
+       return esr_class_str[esr >> ESR_ELx_EC_SHIFT];
+}
+
+/*
+ * bad_mode handles the impossible case in the exception vector.
+ */
+asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr)
+{
+       siginfo_t info;
+       void __user *pc = (void __user *)instruction_pointer(regs);
+       console_verbose();
+
+       pr_crit("Bad mode in %s handler detected, code 0x%08x -- %s\n",
+               handler[reason], esr, esr_get_class_string(esr));
+       __show_regs(regs);
+
+       info.si_signo = SIGILL;
+       info.si_errno = 0;
+       info.si_code  = ILL_ILLOPC;
+       info.si_addr  = pc;
+
+       arm64_notify_die("Oops - bad mode", regs, &info, 0);
+}
+
+void __pte_error(const char *file, int line, unsigned long val)
+{
+       pr_crit("%s:%d: bad pte %016lx.\n", file, line, val);
+}
+
+void __pmd_error(const char *file, int line, unsigned long val)
+{
+       pr_crit("%s:%d: bad pmd %016lx.\n", file, line, val);
+}
+
+void __pud_error(const char *file, int line, unsigned long val)
+{
+       pr_crit("%s:%d: bad pud %016lx.\n", file, line, val);
+}
+
+void __pgd_error(const char *file, int line, unsigned long val)
+{
+       pr_crit("%s:%d: bad pgd %016lx.\n", file, line, val);
+}
+
+void __init trap_init(void)
+{
+       return;
+}