summaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/traps.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/kernel/traps.c')
-rw-r--r--arch/mips/kernel/traps.c81
1 files changed, 64 insertions, 17 deletions
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 6bee29097a5..353056110f2 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -32,6 +32,7 @@
#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>
@@ -42,10 +43,14 @@
#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>
+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);
@@ -718,6 +723,21 @@ static void do_trap_or_bp(struct pt_regs *regs, unsigned int code,
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:
scnprintf(b, sizeof(b), "%s instruction in kernel code", str);
die_if_kernel(b, regs);
@@ -822,8 +842,10 @@ static void mt_ase_fp_affinity(void)
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);
}
@@ -907,13 +929,26 @@ asmlinkage void do_mdmx(struct pt_regs *regs)
asmlinkage void do_watch(struct pt_regs *regs)
{
+ 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();
+ force_sig(SIGTRAP, current);
+ } else
+ mips_clear_watch_registers();
}
asmlinkage void do_mcheck(struct pt_regs *regs)
@@ -1251,6 +1286,9 @@ static void *set_vi_srs_handler(int n, vi_handler_t addr, int srs)
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
@@ -1258,11 +1296,11 @@ static void *set_vi_srs_handler(int n, vi_handler_t addr, int srs)
* 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) {
/*
@@ -1272,7 +1310,7 @@ static void *set_vi_srs_handler(int n, vi_handler_t addr, int srs)
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. */
@@ -1533,6 +1571,8 @@ void __cpuinit set_uncached_handler(unsigned long offset, void *addr,
#ifdef CONFIG_64BIT
unsigned long uncached_ebase = TO_UNCAC(ebase);
#endif
+ if (cpu_has_mips_r2)
+ ebase += (read_c0_ebase() & 0x3ffff000);
if (!addr)
panic(panic_null_cerr);
@@ -1554,6 +1594,10 @@ void __init trap_init(void)
extern char except_vec3_generic, except_vec3_r4000;
extern char except_vec4;
unsigned long i;
+ int rollback;
+
+ check_wait();
+ rollback = (cpu_wait == r4k_wait);
#if defined(CONFIG_KGDB)
if (kgdb_early_setup)
@@ -1562,8 +1606,11 @@ void __init trap_init(void)
if (cpu_has_veic || cpu_has_vint)
ebase = (unsigned long) alloc_bootmem_low_pages(0x200 + VECTORSPACING*64);
- else
+ else {
ebase = CAC_BASE;
+ if (cpu_has_mips_r2)
+ ebase += (read_c0_ebase() & 0x3ffff000);
+ }
per_cpu_trap_init();
@@ -1618,7 +1665,7 @@ void __init trap_init(void)
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);
@@ -1671,11 +1718,11 @@ void __init trap_init(void)
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