patch-2.4.6 linux/arch/mips/kernel/traps.c
Next file: linux/arch/mips/kernel/unaligned.c
Previous file: linux/arch/mips/kernel/time.c
Back to the patch index
Back to the overall index
- Lines: 614
- Date:
Mon Jul 2 13:56:40 2001
- Orig file:
v2.4.5/linux/arch/mips/kernel/traps.c
- Orig date:
Fri Apr 13 20:26:07 2001
diff -u --recursive --new-file v2.4.5/linux/arch/mips/kernel/traps.c linux/arch/mips/kernel/traps.c
@@ -32,6 +32,9 @@
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
+#ifndef CONFIG_MIPS_FPU_EMULATOR
+#include <asm/inst.h>
+#endif
/*
* Machine specific interrupt handlers
@@ -90,9 +93,9 @@
#define RT 0x001f0000
#define OFFSET 0x0000ffff
#define LL 0xc0000000
-#define SC 0xd0000000
+#define SC 0xe0000000
-#define DEBUG_LLSC
+#undef DEBUG_LLSC
#endif
/*
@@ -268,9 +271,6 @@
/*
* Assume it would be too dangerous to continue ...
*/
-/* XXX */
-printk("Got Bus Error at %08x\n", (unsigned int)regs->cp0_epc);
-show_regs(regs); while(1);
force_sig(SIGBUS, current);
}
@@ -410,7 +410,7 @@
epc = (unsigned int *) (unsigned long) regs->cp0_epc;
if (regs->cp0_cause & CAUSEF_BD)
- epc += 4;
+ epc++;
if (verify_area(VERIFY_READ, epc, 4)) {
force_sig(SIGSEGV, current);
@@ -423,6 +423,7 @@
void do_bp(struct pt_regs *regs)
{
+ siginfo_t info;
unsigned int opcode, bcode;
/*
@@ -491,48 +492,69 @@
#if !defined(CONFIG_CPU_HAS_LLSC)
+#ifdef CONFIG_SMP
+#error "ll/sc emulation is not SMP safe"
+#endif
+
/*
* userland emulation for R2300 CPUs
* needed for the multithreading part of glibc
+ *
+ * this implementation can handle only sychronization between 2 or more
+ * user contexts and is not SMP safe.
*/
void do_ri(struct pt_regs *regs)
{
unsigned int opcode;
+ if (!user_mode(regs))
+ BUG();
+
if (!get_insn_opcode(regs, &opcode)) {
- if ((opcode & OPCODE) == LL)
+ if ((opcode & OPCODE) == LL) {
simulate_ll(regs, opcode);
- if ((opcode & OPCODE) == SC)
+ return;
+ }
+ 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]);
+ return;
+ }
}
+ printk("[%s:%d] Illegal instruction %08lx at %08lx, ra=%08lx, CP0_STATUS=%08lx\n",
+ current->comm, current->pid, *((unsigned long*)regs->cp0_epc), regs->cp0_epc,
+ regs->regs[31], regs->cp0_status);
if (compute_return_epc(regs))
return;
force_sig(SIGILL, current);
}
/*
- * the ll_bit will be cleared by r2300_switch.S
+ * The ll_bit is cleared by r*_switch.S
*/
-unsigned long ll_bit, *lladdr;
-
+
+unsigned long ll_bit;
+#ifdef CONFIG_PROC_FS
+extern unsigned long ll_ops;
+extern unsigned long sc_ops;
+#endif
+
+static struct task_struct *ll_task = NULL;
+
void simulate_ll(struct pt_regs *regp, unsigned int opcode)
{
- unsigned long *addr, *vaddr;
+ unsigned long value, *vaddr;
long offset;
-
+ int signal = 0;
+
/*
* analyse the ll instruction that just caused a ri exception
* and put the referenced address to addr.
*/
+
/* sign extend offset */
offset = opcode & OFFSET;
- if (offset & 0x00008000)
- offset = -(offset & 0x00007fff);
- else
- offset = (offset & 0x00007fff);
+ offset <<= 16;
+ offset >>= 16;
vaddr = (unsigned long *)((long)(regp->regs[(opcode & BASE) >> 21]) + offset);
@@ -540,31 +562,44 @@
printk("ll: vaddr = 0x%08x, reg = %d\n", (unsigned int)vaddr, (opcode & RT) >> 16);
#endif
- /*
- * TODO: compute physical address from vaddr
- */
- panic("ll: emulation not yet finished!");
+#ifdef CONFIG_PROC_FS
+ ll_ops++;
+#endif
- lladdr = addr;
- ll_bit = 1;
- regp->regs[(opcode & RT) >> 16] = *addr;
+ if ((unsigned long)vaddr & 3)
+ signal = SIGBUS;
+ else if (get_user(value, vaddr))
+ signal = SIGSEGV;
+ else {
+ if (ll_task == NULL || ll_task == current) {
+ ll_bit = 1;
+ } else {
+ ll_bit = 0;
+ }
+ ll_task = current;
+ regp->regs[(opcode & RT) >> 16] = value;
+ }
+ if (compute_return_epc(regp))
+ return;
+ if (signal)
+ send_sig(signal, current, 1);
}
-
+
void simulate_sc(struct pt_regs *regp, unsigned int opcode)
{
- unsigned long *addr, *vaddr, reg;
+ unsigned long *vaddr, reg;
long offset;
+ int signal = 0;
/*
* analyse the sc instruction that just caused a ri exception
* and put the referenced address to addr.
*/
+
/* sign extend offset */
offset = opcode & OFFSET;
- if (offset & 0x00008000)
- offset = -(offset & 0x00007fff);
- else
- offset = (offset & 0x00007fff);
+ offset <<= 16;
+ offset >>= 16;
vaddr = (unsigned long *)((long)(regp->regs[(opcode & BASE) >> 21]) + offset);
reg = (opcode & RT) >> 16;
@@ -573,28 +608,34 @@
printk("sc: vaddr = 0x%08x, reg = %d\n", (unsigned int)vaddr, (unsigned int)reg);
#endif
- /*
- * TODO: compute physical address from vaddr
- */
- panic("sc: emulation not yet finished!");
-
- lladdr = addr;
+#ifdef CONFIG_PROC_FS
+ sc_ops++;
+#endif
- if (ll_bit == 0) {
+ if ((unsigned long)vaddr & 3)
+ signal = SIGBUS;
+ else if (ll_bit == 0 || ll_task != current)
regp->regs[reg] = 0;
+ else if (put_user(regp->regs[reg], vaddr))
+ signal = SIGSEGV;
+ else
+ regp->regs[reg] = 1;
+ if (compute_return_epc(regp))
return;
- }
-
- *addr = regp->regs[reg];
- regp->regs[reg] = 1;
+ if (signal)
+ send_sig(signal, current, 1);
}
#else /* MIPS 2 or higher */
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);
@@ -662,50 +703,101 @@
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)
{
/*
- * Game over - no way to handle this if it ever occurs.
- * Most probably caused by a new unknown cpu type or
- * after another deadly hard/software error.
+ * Game over - no way to handle this if it ever occurs. Most probably
+ * caused by a new unknown cpu type or after another deadly
+ * hard/software error.
*/
+ show_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_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);
+ set_cp0_cause(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(KERN_INFO "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:
+ break;
+ }
+}
+
+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("cp0_cacheerr == %08x\n", reg_val);
+
+ printk("Decoded CP0_CACHEERR: %s cache fault in %s reference.\n",
+ reg_val&(1<<30)?"secondary":"primary",
+ reg_val&(1<<31)?"data":"insn");
+ printk("Error bits: %s%s%s%s%s%s%s\n",
+ reg_val&(1<<29)?"ED ":"",
+ reg_val&(1<<28)?"ET ":"",
+ reg_val&(1<<26)?"EE ":"",
+ reg_val&(1<<25)?"EB ":"",
+ reg_val&(1<<24)?"EI ":"",
+ reg_val&(1<<23)?"E1 ":"",
+ reg_val&(1<<22)?"E0 ":"");
+ printk("IDX: 0x%08x\n", reg_val&((1<<22)-1));
+
+ if (reg_val&(1<<22))
+ printk("DErrAddr0: 0x%08x\n", read_32bit_cp0_set1_register(CP0_S1_DERRADDR0));
+
+ if (reg_val&(1<<23))
+ printk("DErrAddr1: 0x%08x\n", read_32bit_cp0_set1_register(CP0_S1_DERRADDR1));
+
+
+ panic("Can't handle the cache error - panic!");
+}
+
unsigned long exception_handlers[32];
/*
@@ -713,23 +805,36 @@
* 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 ||
@@ -737,101 +842,121 @@
EISA_bus = 1;
/* Some firmware leaves the BEV flag set, clear it. */
- set_cp0_status(ST0_BEV, 0);
+ clear_cp0_status(ST0_BEV);
/* Copy the generic exception handler code to it's final destination. */
memcpy((void *)(KSEG0 + 0x80), &except_vec1_generic, 0x80);
memcpy((void *)(KSEG0 + 0x100), &except_vec2_generic, 0x80);
memcpy((void *)(KSEG0 + 0x180), &except_vec3_generic, 0x80);
-
+ flush_icache_range(KSEG0 + 0x80, KSEG0 + 0x200);
/*
* 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_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_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_NEVADA:
- if(mips_cputype == CPU_NEVADA) {
+ 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_cputype == CPU_R4600)
+ } else if (mips_cpu.cputype == CPU_R4600)
memcpy((void *)KSEG0, &except_vec0_r4600, 0x80);
else
memcpy((void *)KSEG0, &except_vec0_r4000, 0x80);
/* 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);
+ /*
+ * 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_SB1:
+ /* XXX - This should be folded in to the "cleaner" handling, above */
+ memcpy((void *)KSEG0, &except_vec0_r4000, 0x80);
+ memcpy((void *)(KSEG0 + 0x180), &except_vec3_r4000, 0x80);
+ save_fp_context = _save_fp_context;
+ restore_fp_context = _restore_fp_context;
+ /* Enable timer interrupt and scd mapped interrupt in status register */
+ clear_cp0_status(0xf000);
+ set_cp0_status(0xc00);
+ break;
case CPU_R6000:
case CPU_R6000A:
-#if 0
+ save_fp_context = _save_fp_context;
+ restore_fp_context = _restore_fp_context;
+
/*
* The R6000 is the only R-series CPU that features a machine
* check exception (similar to the R4000 cache error) and
@@ -840,9 +965,8 @@
* 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);
-#endif
+ //set_except_vector(14, handle_mc);
+ //set_except_vector(15, handle_ndc);
case CPU_R2000:
case CPU_R3000:
case CPU_R3000A:
@@ -851,12 +975,17 @@
case CPU_R3052:
case CPU_R3081:
case CPU_R3081E:
+ case CPU_TX3912:
+ case CPU_TX3922:
+ case CPU_TX3927:
+ 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;
case CPU_R8000:
printk("Detected unsupported CPU type %s.\n",
- cpu_names[mips_cputype]);
+ cpu_names[mips_cpu.cputype]);
panic("Can't handle CPU");
break;
@@ -868,5 +997,6 @@
atomic_inc(&init_mm.mm_count); /* XXX UP? */
current->active_mm = &init_mm;
- current_pgd = init_mm.pgd;
+ write_32bit_cp0_register(CP0_CONTEXT, smp_processor_id()<<23);
+ current_pgd[0] = init_mm.pgd;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)