#include <linux/bootmem.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
+#include <linux/kgdb.h>
+#include <linux/kdebug.h>
+#include <linux/notifier.h>
#include <asm/bootinfo.h>
#include <asm/branch.h>
#include <asm/break.h>
+#include <asm/cop2.h>
#include <asm/cpu.h>
#include <asm/dsp.h>
#include <asm/fpu.h>
+#include <asm/fpu_emulator.h>
#include <asm/mipsregs.h>
#include <asm/mipsmtregs.h>
#include <asm/module.h>
#include <asm/tlbdebug.h>
#include <asm/traps.h>
#include <asm/uaccess.h>
+#include <asm/watch.h>
#include <asm/mmu_context.h>
#include <asm/types.h>
#include <asm/stacktrace.h>
+#include <asm/irq.h>
+extern void check_wait(void);
+extern asmlinkage void r4k_wait(void);
+extern asmlinkage void rollback_handle_int(void);
extern asmlinkage void handle_int(void);
extern asmlinkage void handle_tlbm(void);
extern asmlinkage void handle_tlbl(void);
extern int fpu_emulator_cop1Handler(struct pt_regs *xcp,
struct mips_fpu_struct *ctx, int has_fpu);
-void (*board_watchpoint_handler)(struct pt_regs *regs);
void (*board_be_init)(void);
int (*board_be_handler)(struct pt_regs *regs, int is_fixup);
void (*board_nmi_handler_setup)(void);
#ifdef CONFIG_KALLSYMS
printk("\n");
#endif
-#define IS_KVA01(a) ((((unsigned int)a) & 0xc0000000) == 0x80000000)
- if (IS_KVA01(sp)) {
- while (!kstack_end(sp)) {
- addr = *sp++;
- if (__kernel_text_address(addr))
- print_ip_sym(addr);
+ while (!kstack_end(sp)) {
+ unsigned long __user *p =
+ (unsigned long __user *)(unsigned long)sp++;
+ if (__get_user(addr, p)) {
+ printk(" (Bad stack address)");
+ break;
}
- printk("\n");
+ if (__kernel_text_address(addr))
+ print_ip_sym(addr);
}
+ printk("\n");
}
#ifdef CONFIG_KALLSYMS
/*
* Saved cp0 registers
*/
- printk("epc : %0*lx ", field, regs->cp0_epc);
- print_symbol("%s ", regs->cp0_epc);
+ printk("epc : %0*lx %pS\n", field, regs->cp0_epc,
+ (void *) regs->cp0_epc);
printk(" %s\n", print_tainted());
- printk("ra : %0*lx ", field, regs->regs[31]);
- print_symbol("%s\n", regs->regs[31]);
+ printk("ra : %0*lx %pS\n", field, regs->regs[31],
+ (void *) regs->regs[31]);
printk("Status: %08x ", (uint32_t) regs->cp0_status);
do_exit(SIGSEGV);
}
-extern const struct exception_table_entry __start___dbe_table[];
-extern const struct exception_table_entry __stop___dbe_table[];
+extern struct exception_table_entry __start___dbe_table[];
+extern struct exception_table_entry __stop___dbe_table[];
__asm__(
" .section __dbe_table, \"a\"\n"
printk(KERN_ALERT "%s bus error, epc == %0*lx, ra == %0*lx\n",
data ? "Data" : "Instruction",
field, regs->cp0_epc, field, regs->regs[31]);
+ if (notify_die(DIE_OOPS, "bus error", regs, SIGBUS, 0, 0)
+ == NOTIFY_STOP)
+ return;
+
die_if_kernel("Oops", regs);
force_sig(SIGBUS, current);
}
* The ll_bit is cleared by r*_switch.S
*/
-unsigned long ll_bit;
-
-static struct task_struct *ll_task = NULL;
+unsigned int ll_bit;
+struct task_struct *ll_task;
static inline int simulate_ll(struct pt_regs *regs, unsigned int opcode)
{
{
siginfo_t info;
+ if (notify_die(DIE_FP, "FP exception", regs, SIGFPE, 0, 0)
+ == NOTIFY_STOP)
+ return;
die_if_kernel("FP exception in kernel code", regs);
if (fcr31 & FPU_CSR_UNI_X) {
force_sig_info(SIGFPE, &info, current);
}
-asmlinkage void do_bp(struct pt_regs *regs)
+static void do_trap_or_bp(struct pt_regs *regs, unsigned int code,
+ const char *str)
{
- unsigned int opcode, bcode;
siginfo_t info;
+ char b[40];
- if (__get_user(opcode, (unsigned int __user *) exception_epc(regs)))
- goto out_sigsegv;
-
- /*
- * There is the ancient bug in the MIPS assemblers that the break
- * code starts left to bit 16 instead to bit 6 in the opcode.
- * Gas is bug-compatible, but not always, grrr...
- * We handle both cases with a simple heuristics. --macro
- */
- bcode = ((opcode >> 6) & ((1 << 20) - 1));
- if (bcode < (1 << 10))
- bcode <<= 10;
+ if (notify_die(DIE_TRAP, str, regs, code, 0, 0) == NOTIFY_STOP)
+ return;
/*
- * (A short test says that IRIX 5.3 sends SIGTRAP for all break
- * insns, even for break codes that indicate arithmetic failures.
- * Weird ...)
+ * A short test says that IRIX 5.3 sends SIGTRAP for all trap
+ * insns, even for trap and break codes that indicate arithmetic
+ * failures. Weird ...
* But should we continue the brokenness??? --macro
*/
- switch (bcode) {
- case BRK_OVERFLOW << 10:
- case BRK_DIVZERO << 10:
- die_if_kernel("Break instruction in kernel code", regs);
- if (bcode == (BRK_DIVZERO << 10))
+ switch (code) {
+ case BRK_OVERFLOW:
+ case BRK_DIVZERO:
+ scnprintf(b, sizeof(b), "%s instruction in kernel code", str);
+ die_if_kernel(b, regs);
+ if (code == BRK_DIVZERO)
info.si_code = FPE_INTDIV;
else
info.si_code = FPE_INTOVF;
force_sig_info(SIGFPE, &info, current);
break;
case BRK_BUG:
- die("Kernel bug detected", regs);
+ die_if_kernel("Kernel bug detected", regs);
+ force_sig(SIGTRAP, current);
+ break;
+ case BRK_MEMU:
+ /*
+ * Address errors may be deliberately induced by the FPU
+ * emulator to retake control of the CPU after executing the
+ * instruction in the delay slot of an emulated branch.
+ *
+ * Terminate if exception was recognized as a delay slot return
+ * otherwise handle as normal.
+ */
+ if (do_dsemulret(regs))
+ return;
+
+ die_if_kernel("Math emu break/trap", regs);
+ force_sig(SIGTRAP, current);
break;
default:
- die_if_kernel("Break instruction in kernel code", regs);
+ scnprintf(b, sizeof(b), "%s instruction in kernel code", str);
+ die_if_kernel(b, regs);
force_sig(SIGTRAP, current);
}
+}
+
+asmlinkage void do_bp(struct pt_regs *regs)
+{
+ unsigned int opcode, bcode;
+
+ if (__get_user(opcode, (unsigned int __user *) exception_epc(regs)))
+ goto out_sigsegv;
+
+ /*
+ * There is the ancient bug in the MIPS assemblers that the break
+ * code starts left to bit 16 instead to bit 6 in the opcode.
+ * Gas is bug-compatible, but not always, grrr...
+ * We handle both cases with a simple heuristics. --macro
+ */
+ bcode = ((opcode >> 6) & ((1 << 20) - 1));
+ if (bcode >= (1 << 10))
+ bcode >>= 10;
+
+ do_trap_or_bp(regs, bcode, "Break");
return;
out_sigsegv:
asmlinkage void do_tr(struct pt_regs *regs)
{
unsigned int opcode, tcode = 0;
- siginfo_t info;
if (__get_user(opcode, (unsigned int __user *) exception_epc(regs)))
goto out_sigsegv;
if (!(opcode & OPCODE))
tcode = ((opcode >> 6) & ((1 << 10) - 1));
- /*
- * (A short test says that IRIX 5.3 sends SIGTRAP for all trap
- * insns, even for trap codes that indicate arithmetic failures.
- * Weird ...)
- * But should we continue the brokenness??? --macro
- */
- switch (tcode) {
- case BRK_OVERFLOW:
- case BRK_DIVZERO:
- die_if_kernel("Trap instruction in kernel code", regs);
- if (tcode == BRK_DIVZERO)
- info.si_code = FPE_INTDIV;
- else
- info.si_code = FPE_INTOVF;
- info.si_signo = SIGFPE;
- info.si_errno = 0;
- info.si_addr = (void __user *) regs->cp0_epc;
- force_sig_info(SIGFPE, &info, current);
- break;
- case BRK_BUG:
- die("Kernel bug detected", regs);
- break;
- default:
- die_if_kernel("Trap instruction in kernel code", regs);
- force_sig(SIGTRAP, current);
- }
+ do_trap_or_bp(regs, tcode, "Trap");
return;
out_sigsegv:
unsigned int opcode = 0;
int status = -1;
+ if (notify_die(DIE_RI, "RI Fault", regs, SIGSEGV, 0, 0)
+ == NOTIFY_STOP)
+ return;
+
die_if_kernel("Reserved instruction in kernel code", regs);
if (unlikely(compute_return_epc(regs) < 0))
if (cpus_intersects(current->cpus_allowed, mt_fpu_cpumask)) {
cpumask_t tmask;
- cpus_and(tmask, current->thread.user_cpus_allowed,
- mt_fpu_cpumask);
+ current->thread.user_cpus_allowed
+ = current->cpus_allowed;
+ cpus_and(tmask, current->cpus_allowed,
+ mt_fpu_cpumask);
set_cpus_allowed(current, tmask);
set_thread_flag(TIF_FPUBOUND);
}
#endif /* CONFIG_MIPS_MT_FPAFF */
}
+/*
+ * No lock; only written during early bootup by CPU 0.
+ */
+static RAW_NOTIFIER_HEAD(cu2_chain);
+
+int __ref register_cu2_notifier(struct notifier_block *nb)
+{
+ return raw_notifier_chain_register(&cu2_chain, nb);
+}
+
+int cu2_notifier_call_chain(unsigned long val, void *v)
+{
+ return raw_notifier_call_chain(&cu2_chain, val, v);
+}
+
+static int default_cu2_call(struct notifier_block *nfb, unsigned long action,
+ void *data)
+{
+ struct pt_regs *regs = data;
+
+ switch (action) {
+ default:
+ die_if_kernel("Unhandled kernel unaligned access or invalid "
+ "instruction", regs);
+ /* Fall through */
+
+ case CU2_EXCEPTION:
+ force_sig(SIGILL, current);
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block default_cu2_notifier = {
+ .notifier_call = default_cu2_call,
+ .priority = 0x80000000, /* Run last */
+};
+
asmlinkage void do_cpu(struct pt_regs *regs)
{
unsigned int __user *epc;
unsigned int opcode;
unsigned int cpid;
int status;
+ unsigned long __maybe_unused flags;
die_if_kernel("do_cpu invoked from kernel context!", regs);
return;
case 2:
+ raw_notifier_call_chain(&cu2_chain, CU2_EXCEPTION, regs);
+ break;
+
case 3:
break;
}
force_sig(SIGILL, current);
}
+/*
+ * Called with interrupts disabled.
+ */
asmlinkage void do_watch(struct pt_regs *regs)
{
- if (board_watchpoint_handler) {
- (*board_watchpoint_handler)(regs);
- return;
- }
+ u32 cause;
/*
- * We use the watch exception where available to detect stack
- * overflows.
+ * Clear WP (bit 22) bit of cause register so we don't loop
+ * forever.
*/
- dump_tlb_all();
- show_regs(regs);
- panic("Caught WATCH exception - probably caused by stack overflow.");
+ cause = read_c0_cause();
+ cause &= ~(1 << 22);
+ write_c0_cause(cause);
+
+ /*
+ * If the current thread has the watch registers loaded, save
+ * their values and send SIGTRAP. Otherwise another thread
+ * left the registers set, clear them and continue.
+ */
+ if (test_tsk_thread_flag(current, TIF_LOAD_WATCH)) {
+ mips_read_watch_registers();
+ local_irq_enable();
+ force_sig(SIGTRAP, current);
+ } else {
+ mips_clear_watch_registers();
+ local_irq_enable();
+ }
}
asmlinkage void do_mcheck(struct pt_regs *regs)
if (n == 0 && cpu_has_divec) {
*(u32 *)(ebase + 0x200) = 0x08000000 |
(0x03ffffff & (handler >> 2));
- flush_icache_range(ebase + 0x200, ebase + 0x204);
+ local_flush_icache_range(ebase + 0x200, ebase + 0x204);
}
return (void *)old_handler;
}
u32 *w;
unsigned char *b;
- if (!cpu_has_veic && !cpu_has_vint)
- BUG();
+ BUG_ON(!cpu_has_veic && !cpu_has_vint);
if (addr == NULL) {
handler = (unsigned long) do_default_vi;
extern char except_vec_vi, except_vec_vi_lui;
extern char except_vec_vi_ori, except_vec_vi_end;
+ extern char rollback_except_vec_vi;
+ char *vec_start = (cpu_wait == r4k_wait) ?
+ &rollback_except_vec_vi : &except_vec_vi;
#ifdef CONFIG_MIPS_MT_SMTC
/*
* We need to provide the SMTC vectored interrupt handler
* Status.IM bit to be masked before going there.
*/
extern char except_vec_vi_mori;
- const int mori_offset = &except_vec_vi_mori - &except_vec_vi;
+ const int mori_offset = &except_vec_vi_mori - vec_start;
#endif /* CONFIG_MIPS_MT_SMTC */
- const int handler_len = &except_vec_vi_end - &except_vec_vi;
- const int lui_offset = &except_vec_vi_lui - &except_vec_vi;
- const int ori_offset = &except_vec_vi_ori - &except_vec_vi;
+ const int handler_len = &except_vec_vi_end - vec_start;
+ const int lui_offset = &except_vec_vi_lui - vec_start;
+ const int ori_offset = &except_vec_vi_ori - vec_start;
if (handler_len > VECTORSPACING) {
/*
panic("VECTORSPACING too small");
}
- memcpy(b, &except_vec_vi, handler_len);
+ memcpy(b, vec_start, handler_len);
#ifdef CONFIG_MIPS_MT_SMTC
BUG_ON(n > 7); /* Vector index %d exceeds SMTC maximum. */
*w = (*w & 0xffff0000) | (((u32)handler >> 16) & 0xffff);
w = (u32 *)(b + ori_offset);
*w = (*w & 0xffff0000) | ((u32)handler & 0xffff);
- flush_icache_range((unsigned long)b, (unsigned long)(b+handler_len));
+ local_flush_icache_range((unsigned long)b,
+ (unsigned long)(b+handler_len));
}
else {
/*
w = (u32 *)b;
*w++ = 0x08000000 | (((u32)handler >> 2) & 0x03fffff); /* j handler */
*w = 0;
- flush_icache_range((unsigned long)b, (unsigned long)(b+8));
+ local_flush_icache_range((unsigned long)b,
+ (unsigned long)(b+8));
}
return (void *)old_handler;
return set_vi_srs_handler(n, addr, 0);
}
-/*
- * This is used by native signal handling
- */
-asmlinkage int (*save_fp_context)(struct sigcontext __user *sc);
-asmlinkage int (*restore_fp_context)(struct sigcontext __user *sc);
-
-extern asmlinkage int _save_fp_context(struct sigcontext __user *sc);
-extern asmlinkage int _restore_fp_context(struct sigcontext __user *sc);
-
-extern asmlinkage int fpu_emulator_save_context(struct sigcontext __user *sc);
-extern asmlinkage int fpu_emulator_restore_context(struct sigcontext __user *sc);
-
-#ifdef CONFIG_SMP
-static int smp_save_fp_context(struct sigcontext __user *sc)
-{
- return raw_cpu_has_fpu
- ? _save_fp_context(sc)
- : fpu_emulator_save_context(sc);
-}
-
-static int smp_restore_fp_context(struct sigcontext __user *sc)
-{
- return raw_cpu_has_fpu
- ? _restore_fp_context(sc)
- : fpu_emulator_restore_context(sc);
-}
-#endif
-
-static inline void signal_init(void)
-{
-#ifdef CONFIG_SMP
- /* For now just do the cpu_has_fpu check when the functions are invoked */
- save_fp_context = smp_save_fp_context;
- restore_fp_context = smp_restore_fp_context;
-#else
- if (cpu_has_fpu) {
- save_fp_context = _save_fp_context;
- restore_fp_context = _restore_fp_context;
- } else {
- save_fp_context = fpu_emulator_save_context;
- restore_fp_context = fpu_emulator_restore_context;
- }
-#endif
-}
-
-#ifdef CONFIG_MIPS32_COMPAT
-
-/*
- * This is used by 32-bit signal stuff on the 64-bit kernel
- */
-asmlinkage int (*save_fp_context32)(struct sigcontext32 __user *sc);
-asmlinkage int (*restore_fp_context32)(struct sigcontext32 __user *sc);
-
-extern asmlinkage int _save_fp_context32(struct sigcontext32 __user *sc);
-extern asmlinkage int _restore_fp_context32(struct sigcontext32 __user *sc);
-
-extern asmlinkage int fpu_emulator_save_context32(struct sigcontext32 __user *sc);
-extern asmlinkage int fpu_emulator_restore_context32(struct sigcontext32 __user *sc);
-
-static inline void signal32_init(void)
-{
- if (cpu_has_fpu) {
- save_fp_context32 = _save_fp_context32;
- restore_fp_context32 = _restore_fp_context32;
- } else {
- save_fp_context32 = fpu_emulator_save_context32;
- restore_fp_context32 = fpu_emulator_restore_context32;
- }
-}
-#endif
-
extern void cpu_cache_init(void);
extern void tlb_init(void);
extern void flush_tlb_handlers(void);
* Timer interrupt
*/
int cp0_compare_irq;
+int cp0_compare_irq_shift;
/*
* Performance counter IRQ or -1 if shared with timer
status_set);
if (cpu_has_mips_r2) {
- unsigned int enable = 0x0000000f;
+ unsigned int enable = 0x0000000f | cpu_hwrena_impl_bits;
if (!noulri && cpu_has_userlocal)
enable |= (1 << 29);
#endif /* CONFIG_MIPS_MT_SMTC */
if (cpu_has_veic || cpu_has_vint) {
+ unsigned long sr = set_c0_status(ST0_BEV);
write_c0_ebase(ebase);
+ write_c0_status(sr);
/* Setting vector spacing enables EI/VI mode */
change_c0_intctl(0x3e0, VECTORSPACING);
}
* o read IntCtl.IPPCI to determine the performance counter interrupt
*/
if (cpu_has_mips_r2) {
- cp0_compare_irq = (read_c0_intctl() >> 29) & 7;
- cp0_perfcount_irq = (read_c0_intctl() >> 26) & 7;
+ cp0_compare_irq_shift = CAUSEB_TI - CAUSEB_IP;
+ cp0_compare_irq = (read_c0_intctl() >> INTCTLB_IPTI) & 7;
+ cp0_perfcount_irq = (read_c0_intctl() >> INTCTLB_IPPCI) & 7;
if (cp0_perfcount_irq == cp0_compare_irq)
cp0_perfcount_irq = -1;
} else {
cp0_compare_irq = CP0_LEGACY_COMPARE_IRQ;
+ cp0_compare_irq_shift = cp0_compare_irq;
cp0_perfcount_irq = -1;
}
void __init set_handler(unsigned long offset, void *addr, unsigned long size)
{
memcpy((void *)(ebase + offset), addr, size);
- flush_icache_range(ebase + offset, ebase + offset + size);
+ local_flush_icache_range(ebase + offset, ebase + offset + size);
}
static char panic_null_cerr[] __cpuinitdata =
"Trying to set NULL cache error exception handler";
-/* Install uncached CPU exception handler */
+/*
+ * Install uncached CPU exception handler.
+ * This is suitable only for the cache error exception which is the only
+ * exception handler that is being run uncached.
+ */
void __cpuinit set_uncached_handler(unsigned long offset, void *addr,
unsigned long size)
{
extern char except_vec3_generic, except_vec3_r4000;
extern char except_vec4;
unsigned long i;
+ int rollback;
- if (cpu_has_veic || cpu_has_vint)
- ebase = (unsigned long) alloc_bootmem_low_pages(0x200 + VECTORSPACING*64);
- else
+ check_wait();
+ rollback = (cpu_wait == r4k_wait);
+
+#if defined(CONFIG_KGDB)
+ if (kgdb_early_setup)
+ return; /* Already done */
+#endif
+
+ if (cpu_has_veic || cpu_has_vint) {
+ unsigned long size = 0x200 + VECTORSPACING*64;
+ ebase = (unsigned long)
+ __alloc_bootmem(size, 1 << fls(size), 0);
+ } else {
ebase = CAC_BASE;
+ if (cpu_has_mips_r2)
+ ebase += (read_c0_ebase() & 0x3ffff000);
+ }
per_cpu_trap_init();
if (board_be_init)
board_be_init();
- set_except_vector(0, handle_int);
+ set_except_vector(0, rollback ? rollback_handle_int : handle_int);
set_except_vector(1, handle_tlbm);
set_except_vector(2, handle_tlbl);
set_except_vector(3, handle_tlbs);
if (cpu_has_vce)
/* Special exception: R4[04]00 uses also the divec space. */
- memcpy((void *)(CAC_BASE + 0x180), &except_vec3_r4000, 0x100);
+ memcpy((void *)(ebase + 0x180), &except_vec3_r4000, 0x100);
else if (cpu_has_4kex)
- memcpy((void *)(CAC_BASE + 0x180), &except_vec3_generic, 0x80);
+ memcpy((void *)(ebase + 0x180), &except_vec3_generic, 0x80);
else
- memcpy((void *)(CAC_BASE + 0x080), &except_vec3_generic, 0x80);
+ memcpy((void *)(ebase + 0x080), &except_vec3_generic, 0x80);
- signal_init();
-#ifdef CONFIG_MIPS32_COMPAT
- signal32_init();
-#endif
-
- flush_icache_range(ebase, ebase + 0x400);
+ local_flush_icache_range(ebase, ebase + 0x400);
flush_tlb_handlers();
+
+ sort_extable(__start___dbe_table, __stop___dbe_table);
+
+ register_cu2_notifier(&default_cu2_notifier);
}