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.c233
1 files changed, 155 insertions, 78 deletions
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 0abd55e78..779b72805 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -7,6 +7,9 @@
* Modified for R3000 by Paul M. Antoine, 1995, 1996
* Complete output from die() by Ulf Carlsson, 1998
* Copyright (C) 1999 Silicon Graphics, Inc.
+ *
+ * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
+ * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved.
*/
#include <linux/config.h>
#include <linux/init.h>
@@ -28,6 +31,9 @@
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
+#ifndef CONFIG_MIPS_FPU_EMULATOR
+#include <asm/inst.h>
+#endif
extern int console_loglevel;
@@ -66,6 +72,7 @@ extern asmlinkage void handle_ov(void);
extern asmlinkage void handle_tr(void);
extern asmlinkage void handle_fpe(void);
extern asmlinkage void handle_watch(void);
+extern asmlinkage void handle_mcheck(void);
extern asmlinkage void handle_reserved(void);
extern int fpu_emulator_cop1Handler(int, struct pt_regs *);
@@ -377,6 +384,8 @@ void do_fpe(struct pt_regs *regs, unsigned long fcr31)
/* Retry instruction with flush to zero ... */
if (!(fcr31 & (1<<24))) {
+ printk("Setting flush to zero for %s.\n",
+ current->comm);
fcr31 &= ~FPU_CSR_UNI_X;
fcr31 |= (1<<24);
__asm__ __volatile__(
@@ -386,13 +395,26 @@ void do_fpe(struct pt_regs *regs, unsigned long fcr31)
return;
}
pc = regs->cp0_epc + ((regs->cp0_cause & CAUSEF_BD) ? 4 : 0);
- if (get_user(insn, (unsigned int *)pc))
+ if(pc & 0x80000000) insn = *(unsigned int *)pc;
+ else if (get_user(insn, (unsigned int *)pc)) {
+ /* XXX Can this happen? */
force_sig(SIGSEGV, current);
-#endif /* !CONFIG_MIPS_FPU_EMULATOR */
+ }
+
+ printk(KERN_DEBUG "Unimplemented exception for insn %08x at 0x%08lx in %s.\n",
+ insn, regs->cp0_epc, current->comm);
+ simfp(MIPSInst(insn));
+ compute_return_epc(regs);
+#endif /* CONFIG_MIPS_FPU_EMULATOR */
+
+ return;
}
- if (!compute_return_epc(regs))
- force_sig(SIGFPE, current);
+ if (compute_return_epc(regs))
+ return;
+
+ force_sig(SIGFPE, current);
+ printk(KERN_DEBUG "Sent send SIGFPE to %s\n", current->comm);
}
static inline int get_insn_opcode(struct pt_regs *regs, unsigned int *opcode)
@@ -465,8 +487,9 @@ void do_ri(struct pt_regs *regs)
if ((opcode & OPCODE) == SC)
simulate_sc(regs, opcode);
} else {
- printk("[%s:%d] Illegal instruction at %08lx ra=%08lx\n",
- current->comm, current->pid, regs->cp0_epc, regs->regs[31]);
+ printk("[%s:%ld] Illegal instruction %08x at %08lx ra=%08lx\n",
+ current->comm, (unsigned long)current->pid, opcode,
+ regs->cp0_epc, regs->regs[31]);
}
if (compute_return_epc(regs))
return;
@@ -553,8 +576,12 @@ void simulate_sc(struct pt_regs *regp, unsigned int opcode)
void do_ri(struct pt_regs *regs)
{
- printk("[%s:%d] Illegal instruction at %08lx ra=%08lx\n",
- current->comm, current->pid, regs->cp0_epc, regs->regs[31]);
+ unsigned int opcode;
+
+ get_insn_opcode(regs, &opcode);
+ printk("[%s:%ld] Illegal instruction %08x at %08lx ra=%08lx\n",
+ current->comm, (unsigned long)current->pid, opcode,
+ regs->cp0_epc, regs->regs[31]);
if (compute_return_epc(regs))
return;
force_sig(SIGILL, current);
@@ -622,6 +649,12 @@ void do_watch(struct pt_regs *regs)
panic("Caught WATCH exception - probably caused by stack overflow.");
}
+void do_mcheck(struct pt_regs *regs)
+{
+ show_regs(regs);
+ panic("Caught Machine Check exception - probably caused by multiple matching entries in the TLB.");
+}
+
void do_reserved(struct pt_regs *regs)
{
/*
@@ -632,40 +665,61 @@ void do_reserved(struct pt_regs *regs)
panic("Caught reserved exception - should not happen.");
}
-static inline void watch_init(unsigned long cputype)
+static inline void watch_init(void)
{
- switch(cputype) {
- case CPU_R10000:
- case CPU_R4000MC:
- case CPU_R4400MC:
- case CPU_R4000SC:
- case CPU_R4400SC:
- case CPU_R4000PC:
- case CPU_R4400PC:
- case CPU_R4200:
- case CPU_R4300:
- set_except_vector(23, handle_watch);
- watch_available = 1;
- break;
- }
+ if(mips_cpu.options & MIPS_CPU_WATCH ) {
+ (void)set_except_vector(23, handle_watch);
+ watch_available = 1;
+ }
}
/*
* Some MIPS CPUs have a dedicated interrupt vector which reduces the
* interrupt processing overhead. Use it where available.
- * FIXME: more CPUs than just the Nevada have this feature.
*/
static inline void setup_dedicated_int(void)
{
extern void except_vec4(void);
- switch(mips_cpu.cputype) {
- case CPU_NEVADA:
+
+ if(mips_cpu.options & MIPS_CPU_DIVEC) {
memcpy((void *)(KSEG0 + 0x200), except_vec4, 8);
set_cp0_cause(CAUSEF_IV, CAUSEF_IV);
dedicated_iv_available = 1;
}
}
+/*
+ * Some MIPS CPUs can enable/disable for cache parity detection, but does
+ * it different ways.
+ */
+static inline void parity_protection_init(void)
+{
+ switch(mips_cpu.cputype)
+ {
+ case CPU_5KC:
+ /* Set the PE bit (bit 31) in the CP0_ECC register. */
+ printk("Enable the cache parity protection for MIPS 5KC CPUs.\n");
+ write_32bit_cp0_register(CP0_ECC, read_32bit_cp0_register(CP0_ECC)
+ | 0x80000000);
+ break;
+ default:
+ }
+}
+
+void cache_parity_error(void)
+{
+ unsigned int reg_val;
+
+ /* For the moment, report the problem and hang. */
+ reg_val = read_32bit_cp0_register(CP0_ERROREPC);
+ printk("Cache error exception:\n");
+ printk("cp0_errorepc == %08x\n", reg_val);
+ reg_val = read_32bit_cp0_register(CP0_CACHEERR);
+ printk("c0_cacheerr == %08x\n", reg_val);
+
+ panic("Can't handle the cache error - panic!");
+}
+
unsigned long exception_handlers[32];
/*
@@ -673,23 +727,36 @@ unsigned long exception_handlers[32];
* to interrupt handlers in the address range from
* KSEG0 <= x < KSEG0 + 256mb on the Nevada. Oh well ...
*/
-void set_except_vector(int n, void *addr)
+void *set_except_vector(int n, void *addr)
{
unsigned handler = (unsigned long) addr;
+ unsigned old_handler = exception_handlers[n];
exception_handlers[n] = handler;
if (n == 0 && dedicated_iv_available) {
*(volatile u32 *)(KSEG0+0x200) = 0x08000000 |
(0x03ffffff & (handler >> 2));
flush_icache_range(KSEG0+0x200, KSEG0 + 0x204);
}
+ return (void *)old_handler;
}
+asmlinkage int (*save_fp_context)(struct sigcontext *sc);
+asmlinkage int (*restore_fp_context)(struct sigcontext *sc);
+extern asmlinkage int _save_fp_context(struct sigcontext *sc);
+extern asmlinkage int _restore_fp_context(struct sigcontext *sc);
+
+#ifdef CONFIG_MIPS_FPU_EMULATOR
+extern asmlinkage int fpu_emulator_save_context(struct sigcontext *sc);
+extern asmlinkage int fpu_emulator_restore_context(struct sigcontext *sc);
+#endif
+
void __init trap_init(void)
{
extern char except_vec0_nevada, except_vec0_r4000;
extern char except_vec0_r4600, except_vec0_r2300;
extern char except_vec1_generic, except_vec2_generic;
extern char except_vec3_generic, except_vec3_r4000;
+ extern char except_vec_ejtag_debug;
unsigned long i;
if(mips_machtype == MACH_MIPS_MAGNUM_4000 ||
@@ -708,71 +775,55 @@ void __init trap_init(void)
* Setup default vectors
*/
for(i = 0; i <= 31; i++)
- set_except_vector(i, handle_reserved);
+ (void)set_except_vector(i, handle_reserved);
+
+ /*
+ * Copy the EJTAG debug exception vector handler code to it's final
+ * destination.
+ */
+ memcpy((void *)(KSEG0 + 0x300), &except_vec_ejtag_debug, 0x80);
/*
* Only some CPUs have the watch exceptions or a dedicated
* interrupt vector.
*/
- watch_init(mips_cpu.cputype);
+ watch_init();
setup_dedicated_int();
- set_except_vector(1, handle_mod);
- set_except_vector(2, handle_tlbl);
- set_except_vector(3, handle_tlbs);
- set_except_vector(4, handle_adel);
- set_except_vector(5, handle_ades);
+ /*
+ * Some CPUs can enable/disable for cache parity detection, but does
+ * it different ways.
+ */
+ parity_protection_init();
+
+ (void)set_except_vector(1, handle_mod);
+ (void)set_except_vector(2, handle_tlbl);
+ (void)set_except_vector(3, handle_tlbs);
+ (void)set_except_vector(4, handle_adel);
+ (void)set_except_vector(5, handle_ades);
/*
* The Data Bus Error/ Instruction Bus Errors are signaled
* by external hardware. Therefore these two expection have
* board specific handlers.
*/
- set_except_vector(6, handle_ibe);
- set_except_vector(7, handle_dbe);
+ (void)set_except_vector(6, handle_ibe);
+ (void)set_except_vector(7, handle_dbe);
ibe_board_handler = default_be_board_handler;
dbe_board_handler = default_be_board_handler;
- set_except_vector(8, handle_sys);
- set_except_vector(9, handle_bp);
- set_except_vector(10, handle_ri);
- set_except_vector(11, handle_cpu);
- set_except_vector(12, handle_ov);
- set_except_vector(13, handle_tr);
- set_except_vector(15, handle_fpe);
-
+ (void)set_except_vector(8, handle_sys);
+ (void)set_except_vector(9, handle_bp);
+ (void)set_except_vector(10, handle_ri);
+ (void)set_except_vector(11, handle_cpu);
+ (void)set_except_vector(12, handle_ov);
+ (void)set_except_vector(13, handle_tr);
+ (void)set_except_vector(15, handle_fpe);
+
/*
* Handling the following exceptions depends mostly of the cpu type
*/
- switch(mips_cpu.cputype) {
- case CPU_R10000:
- /*
- * The R10000 is in most aspects similar to the R4400. It
- * should get some special optimizations.
- */
- write_32bit_cp0_register(CP0_FRAMEMASK, 0);
- set_cp0_status(ST0_XX, ST0_XX);
- /*
- * The R10k might even work for Linux/MIPS - but we're paranoid
- * and refuse to run until this is tested on real silicon
- */
- panic("CPU too expensive - making holiday in the ANDES!");
- break;
- case CPU_R4000MC:
- case CPU_R4400MC:
- case CPU_R4000SC:
- case CPU_R4400SC:
- vce_available = 1;
- /* Fall through ... */
- case CPU_R4000PC:
- case CPU_R4400PC:
- case CPU_R4200:
- case CPU_R4300:
- /* case CPU_R4640: */
- case CPU_R4600:
- case CPU_R5000:
- case CPU_R5432:
- case CPU_NEVADA:
- case CPU_RM7000:
+ if((mips_cpu.options & MIPS_CPU_4KEX)
+ && (mips_cpu.options & MIPS_CPU_4KTLB)) {
if(mips_cpu.cputype == CPU_NEVADA) {
memcpy((void *)KSEG0, &except_vec0_nevada, 0x80);
} else if (mips_cpu.cputype == CPU_R4600)
@@ -782,17 +833,41 @@ void __init trap_init(void)
/* Cache error vector already set above. */
- if (vce_available) {
+ if (mips_cpu.options & MIPS_CPU_VCE) {
memcpy((void *)(KSEG0 + 0x180), &except_vec3_r4000,
0x80);
} else {
memcpy((void *)(KSEG0 + 0x180), &except_vec3_generic,
0x80);
}
- break;
+ if(mips_cpu.options & MIPS_CPU_FPU) {
+ save_fp_context = _save_fp_context;
+ restore_fp_context = _restore_fp_context;
+#ifdef CONFIG_MIPS_FPU_EMULATOR
+ } else {
+ save_fp_context = fpu_emulator_save_context;
+ restore_fp_context = fpu_emulator_restore_context;
+#endif
+ }
+ } else switch(mips_cpu.cputype) {
+ case CPU_R10000:
+ /*
+ * The R10000 is in most aspects similar to the R4400. It
+ * should get some special optimizations.
+ */
+ write_32bit_cp0_register(CP0_FRAMEMASK, 0);
+ set_cp0_status(ST0_XX, ST0_XX);
+ /*
+ * The R10k might even work for Linux/MIPS - but we're paranoid
+ * and refuse to run until this is tested on real silicon
+ */
+ panic("CPU too expensive - making holiday in the ANDES!");
+ break;
case CPU_R6000:
case CPU_R6000A:
+ save_fp_context = _save_fp_context;
+ restore_fp_context = _restore_fp_context;
#if 0
/*
* The R6000 is the only R-series CPU that features a machine
@@ -802,8 +877,8 @@ void __init trap_init(void)
* current list of targets for Linux/MIPS.
* (Duh, crap, there is someone with a tripple R6k machine)
*/
- set_except_vector(14, handle_mc);
- set_except_vector(15, handle_ndc);
+ (void)set_except_vector(14, handle_mc);
+ (void)set_except_vector(15, handle_ndc);
#endif
case CPU_R2000:
case CPU_R3000:
@@ -813,6 +888,8 @@ void __init trap_init(void)
case CPU_R3052:
case CPU_R3081:
case CPU_R3081E:
+ save_fp_context = _save_fp_context;
+ restore_fp_context = _restore_fp_context;
memcpy((void *)KSEG0, &except_vec0_r2300, 0x80);
memcpy((void *)(KSEG0 + 0x80), &except_vec3_generic, 0x80);
break;