diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-11-25 04:49:46 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-11-25 04:49:46 +0000 |
commit | 4c55adaa6d06e5533aebaceea7640ecf10952231 (patch) | |
tree | 0f2e4190d1e9db004e50c7f33a1fe46337fd1885 /arch/mips/kernel/traps.c | |
parent | 4c86076eb07eb647b1080355e18dc9d4c49bf7e1 (diff) |
Kernel FPU emulator, chain saw edition.
Diffstat (limited to 'arch/mips/kernel/traps.c')
-rw-r--r-- | arch/mips/kernel/traps.c | 111 |
1 files changed, 85 insertions, 26 deletions
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index ebfac0cd2..0abd55e78 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -16,12 +16,14 @@ #include <linux/smp_lock.h> #include <linux/spinlock.h> +#include <asm/bootinfo.h> #include <asm/branch.h> +#include <asm/cpu.h> #include <asm/cachectl.h> +#include <asm/inst.h> #include <asm/jazz.h> #include <asm/pgtable.h> #include <asm/io.h> -#include <asm/bootinfo.h> #include <asm/watch.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -66,6 +68,8 @@ extern asmlinkage void handle_fpe(void); extern asmlinkage void handle_watch(void); extern asmlinkage void handle_reserved(void); +extern int fpu_emulator_cop1Handler(int, struct pt_regs *); + static char *cpu_names[] = CPU_NAMES; char watch_available = 0; @@ -318,9 +322,11 @@ int unregister_fpe(void (*handler)(struct pt_regs *regs, unsigned int fcr31)) */ void do_fpe(struct pt_regs *regs, unsigned long fcr31) { - unsigned long pc; - unsigned int insn; - extern void simfp(unsigned int); + +#ifdef CONFIG_MIPS_FPU_EMULATOR + if(!(mips_cpu.options & MIPS_CPU_FPU)) + panic("Floating Point Exception with No FPU"); +#endif #ifdef CONFIG_MIPS_FPE_MODULE if (fpe_handler != NULL) { @@ -328,12 +334,50 @@ void do_fpe(struct pt_regs *regs, unsigned long fcr31) return; } #endif - if (fcr31 & 0x20000) { + + if (fcr31 & FPU_CSR_UNI_X) { +#ifdef CONFIG_MIPS_FPU_EMULATOR + extern void save_fp(struct task_struct *); + extern void restore_fp(struct task_struct *); + int sig; + /* + * Unimplemented operation exception. If we've got the + * Full software emulator on-board, let's use it... + * + * Force FPU to dump state into task/thread context. + * We're moving a lot of data here for what is probably + * a single instruction, but the alternative is to + * pre-decode the FP register operands before invoking + * the emulator, which seems a bit extreme for what + * should be an infrequent event. + */ + save_fp(current); + + /* Run the emulator */ + sig = fpu_emulator_cop1Handler(0, regs); + + /* + * We can't allow the emulated instruction to leave the + * Unimplemented Operation bit set in the FCR31 fp-register. + */ + current->thread.fpu.soft.sr &= ~FPU_CSR_UNI_X; + + /* Restore the hardware register state */ + restore_fp(current); + + /* If something went wrong, signal */ + if (sig) + force_sig(sig, current); +#else + /* Else use mini-emulator */ + + extern void simfp(int); + unsigned long pc; + unsigned int insn; + /* Retry instruction with flush to zero ... */ if (!(fcr31 & (1<<24))) { - printk("Setting flush to zero for %s.\n", - current->comm); - fcr31 &= ~0x20000; + fcr31 &= ~FPU_CSR_UNI_X; fcr31 |= (1<<24); __asm__ __volatile__( "ctc1\t%0,$31" @@ -342,20 +386,13 @@ 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)) { - /* XXX Can this happen? */ + if (get_user(insn, (unsigned int *)pc)) force_sig(SIGSEGV, current); - } - - printk(KERN_DEBUG "Unimplemented exception for insn %08x at 0x%08lx in %s.\n", - insn, regs->cp0_epc, current->comm); - simfp(insn); +#endif /* !CONFIG_MIPS_FPU_EMULATOR */ } - if (compute_return_epc(regs)) - return; - //force_sig(SIGFPE, current); - printk(KERN_DEBUG "Should send SIGFPE to %s\n", current->comm); + if (!compute_return_epc(regs)) + force_sig(SIGFPE, current); } static inline int get_insn_opcode(struct pt_regs *regs, unsigned int *opcode) @@ -530,11 +567,33 @@ void do_cpu(struct pt_regs *regs) unsigned int cpid; extern void lazy_fpu_switch(void*); extern void init_fpu(void); - +#ifdef CONFIG_MIPS_FPU_EMULATOR + void fpu_emulator_init_fpu(void); + int sig; +#endif cpid = (regs->cp0_cause >> CAUSEB_CE) & 3; if (cpid != 1) goto bad_cid; +#ifdef CONFIG_MIPS_FPU_EMULATOR + if(!(mips_cpu.options & MIPS_CPU_FPU)) { + if (last_task_used_math != current) { + if(!current->used_math) { + fpu_emulator_init_fpu(); + current->used_math = 1; + } + } + sig = fpu_emulator_cop1Handler(0, regs); + last_task_used_math = current; + if(sig) { + force_sig(sig, current); + } + return; + } +#else + if(!(mips_cpu.options & MIPS_CPU_FPU)) goto bad_cid; +#endif + regs->cp0_status |= ST0_CU1; if (last_task_used_math == current) return; @@ -599,7 +658,7 @@ static inline void watch_init(unsigned long cputype) static inline void setup_dedicated_int(void) { extern void except_vec4(void); - switch(mips_cputype) { + switch(mips_cpu.cputype) { case CPU_NEVADA: memcpy((void *)(KSEG0 + 0x200), except_vec4, 8); set_cp0_cause(CAUSEF_IV, CAUSEF_IV); @@ -655,7 +714,7 @@ void __init trap_init(void) * Only some CPUs have the watch exceptions or a dedicated * interrupt vector. */ - watch_init(mips_cputype); + watch_init(mips_cpu.cputype); setup_dedicated_int(); set_except_vector(1, handle_mod); @@ -684,7 +743,7 @@ void __init trap_init(void) /* * Handling the following exceptions depends mostly of the cpu type */ - switch(mips_cputype) { + switch(mips_cpu.cputype) { case CPU_R10000: /* * The R10000 is in most aspects similar to the R4400. It @@ -714,9 +773,9 @@ void __init trap_init(void) case CPU_R5432: case CPU_NEVADA: case CPU_RM7000: - if(mips_cputype == CPU_NEVADA) { + 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); @@ -759,7 +818,7 @@ void __init trap_init(void) 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; |