diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-07-16 19:10:01 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-07-16 19:10:01 +0000 |
commit | 1ace72167d23543960d517e3d444762cb92bd211 (patch) | |
tree | c5d5aaf9501edcc48dc8dfdca9ed694d0d26763f /arch/mips/kernel | |
parent | cb9368e29107fdf541cb68499a04f67949c131c5 (diff) |
- Reformat ptrace.c for readability.
- Handle lazy fpu context switches correctly for ptrace(2).
- Don't read $fcr31 on context switch, it's pure bloat.
- New processes get $fcr31 initialized to no exceptions.
- First beginnings of floating point support kernel code. For now we
only can handle cvt.w.s and cvt.w.d instructions where the source
register is a NaN, infinity or denorm. This is good enough to get
Mozilla up.
- Don't send SIGFPE on every floating point instruction we don't know
how to handle, rather just complain.
- Cleanup headerfile inclusions in the HPC3 code.
- Fix the definition of PAGE_NONE.
Diffstat (limited to 'arch/mips/kernel')
-rw-r--r-- | arch/mips/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/mips/kernel/ptrace.c | 291 | ||||
-rw-r--r-- | arch/mips/kernel/r4k_switch.S | 22 | ||||
-rw-r--r-- | arch/mips/kernel/softfp.S | 667 | ||||
-rw-r--r-- | arch/mips/kernel/traps.c | 20 |
5 files changed, 852 insertions, 150 deletions
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index 224d57a4e..8bea8ab5b 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -15,7 +15,7 @@ O_TARGET := kernel.o O_OBJS := branch.o process.o signal.o entry.o traps.o ptrace.o vm86.o \ ioport.o pci.o reset.o setup.o syscall.o sysmips.o ipc.o \ r4k_switch.o r4k_misc.o r4k_fpu.o r2300_switch.o r2300_misc.o \ - r2300_fpu.o r6000_fpu.o scall_o32.o unaligned.o + r2300_fpu.o r6000_fpu.o scall_o32.o softfp.o unaligned.o OX_OBJS := mips_ksyms.o ifdef CONFIG_MIPS_FPE_MODULE diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index f9172b726..05fafefe3 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -1,8 +1,14 @@ -/* ptrace.c */ -/* By Ross Biro 1/23/92 */ -/* edited by Linus Torvalds */ -/* further hacked for MIPS by David S. Miller (dm@engr.sgi.com) */ - +/* $Id: ptrace.c,v 1.9 1998/07/16 17:01:52 ralf Exp $ + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1992 Ross Biro + * Copyright (C) Linus Torvalds + * Copyright (C) 1994, 1995, 1996, 1997, 1998 Ralf Baechle + * Copyright (C) 1996 David S. Miller + */ #include <linux/head.h> #include <linux/kernel.h> #include <linux/sched.h> @@ -13,10 +19,12 @@ #include <linux/smp_lock.h> #include <linux/user.h> -#include <asm/uaccess.h> +#include <asm/fp.h> +#include <asm/mipsregs.h> #include <asm/pgtable.h> #include <asm/page.h> #include <asm/system.h> +#include <asm/uaccess.h> /* * This routine gets a long from any process space by following the page @@ -326,163 +334,182 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) } switch (request) { - /* when I and D space are separate, these will need to be fixed. */ - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned long tmp; + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: { + unsigned long tmp; - res = read_long(child, addr, &tmp); - if (res < 0) - goto out; - res = put_user(tmp,(unsigned long *) data); + res = read_long(child, addr, &tmp); + if (res < 0) goto out; + res = put_user(tmp,(unsigned long *) data); + goto out; } /* read the word at location addr in the USER area. */ /* #define DEBUG_PEEKUSR */ - case PTRACE_PEEKUSR: { - struct pt_regs *regs; - unsigned long tmp; + case PTRACE_PEEKUSR: { + struct pt_regs *regs; + unsigned long tmp; - regs = (struct pt_regs *) ((unsigned long) child + - KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs)); - tmp = 0; /* Default return value. */ - if(addr < 32 && addr >= 0) { - tmp = regs->regs[addr]; - } else if(addr >= 32 && addr < 64) { - unsigned long long *fregs; + regs = (struct pt_regs *) ((unsigned long) child + + KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs)); + tmp = 0; /* Default return value. */ + if (addr < 32 && addr >= 0) + tmp = regs->regs[addr]; + else if (addr >= 32 && addr < 64) { + unsigned long long *fregs; - /* We don't want to do a FPU operation here. */ + if (child->used_math) { + if (last_task_used_math == child) { + enable_cp1(); + r4xx0_save_fp(child); + disable_cp1(); + last_task_used_math = NULL; + } fregs = (unsigned long long *) &child->tss.fpu.hard.fp_regs[0]; tmp = (unsigned long) fregs[(addr - 32)]; } else { - addr -= 64; - switch(addr) { - case 0: - tmp = regs->cp0_epc; - break; - case 1: - tmp = regs->cp0_cause; - break; - case 2: - tmp = regs->cp0_badvaddr; - break; - case 3: - tmp = regs->lo; - break; - case 4: - tmp = regs->hi; - break; - case 5: - tmp = child->tss.fpu.hard.control; - break; - case 6: /* implementation / version register */ - tmp = 0; - break; - default: - tmp = 0; - res = -EIO; - goto out; - }; + tmp = -1; /* FP not yet used */ } - res = put_user(tmp, (unsigned long *) data); - goto out; + } else { + addr -= 64; + switch(addr) { + case 0: + tmp = regs->cp0_epc; + break; + case 1: + tmp = regs->cp0_cause; + break; + case 2: + tmp = regs->cp0_badvaddr; + break; + case 3: + tmp = regs->lo; + break; + case 4: + tmp = regs->hi; + break; + case 5: + tmp = child->tss.fpu.hard.control; + break; + case 6: /* implementation / version register */ + tmp = 0; /* XXX */ + break; + default: + tmp = 0; + res = -EIO; + goto out; + } + } + res = put_user(tmp, (unsigned long *) data); + goto out; } - /* when I and D space are separate, this will have to be fixed. */ - case PTRACE_POKETEXT: /* write the word at location addr. */ - case PTRACE_POKEDATA: - res = write_long(child,addr,data); - goto out; + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + res = write_long(child,addr,data); + goto out; - case PTRACE_POKEUSR: { - struct pt_regs *regs; - int res = 0; + case PTRACE_POKEUSR: { + struct pt_regs *regs; + int res = 0; - regs = (struct pt_regs *) ((unsigned long) child + - KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs)); - if(addr < 32 && addr >= 0) { - regs->regs[addr] = data; - } else if(addr >= 32 && addr < 64) { - unsigned long long *fregs; + regs = (struct pt_regs *) ((unsigned long) child + + KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs)); + if (addr < 32 && addr >= 0) + regs->regs[addr] = data; + else if (addr >= 32 && addr < 64) { + unsigned long long *fregs; - /* We don't want to do a FPU operation here. */ - fregs = (unsigned long long *) - &child->tss.fpu.hard.fp_regs[0]; - fregs[(addr - 32)] = (unsigned long long) data; + if (child->used_math) { + if (last_task_used_math == child) { + enable_cp1(); + r4xx0_save_fp(child); + disable_cp1(); + last_task_used_math = NULL; + } } else { - addr -= 64; - switch(addr) { - case 0: - regs->cp0_epc = data; - break; - case 3: - regs->lo = data; - break; - case 4: - regs->hi = data; - break; - case 5: - child->tss.fpu.hard.control = data; - break; - default: - /* The rest are not allowed. */ - res = -EIO; - break; - }; + /* FP not yet used */ + memset(&child->tss.fpu.hard, ~0, + sizeof(child->tss.fpu.hard)); + child->tss.fpu.hard.control = 0; } - goto out; - } - - case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ - case PTRACE_CONT: { /* restart after signal. */ - if ((unsigned long) data > _NSIG) { + fregs = (unsigned long long *) + &child->tss.fpu.hard.fp_regs[0]; + fregs[(addr - 32)] = (unsigned long long) data; + } else { + addr -= 64; + switch (addr) { + case 0: + regs->cp0_epc = data; + break; + case 3: + regs->lo = data; + break; + case 4: + regs->hi = data; + break; + case 5: + child->tss.fpu.hard.control = data; + break; + default: + /* The rest are not allowed. */ res = -EIO; - goto out; - } - if (request == PTRACE_SYSCALL) - child->flags |= PF_TRACESYS; - else - child->flags &= ~PF_TRACESYS; - child->exit_code = data; - wake_up_process(child); - res = data; - goto out; + break; + }; + } + goto out; } -/* - * make the child exit. Best I can do is send it a sigkill. - * perhaps it should be put in the status that it wants to - * exit. - */ - case PTRACE_KILL: { - if (child->state != TASK_ZOMBIE) { - wake_up_process(child); - child->exit_code = SIGKILL; - } - res = 0; + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ + if ((unsigned long) data > _NSIG) { + res = -EIO; goto out; } + if (request == PTRACE_SYSCALL) + child->flags |= PF_TRACESYS; + else + child->flags &= ~PF_TRACESYS; + child->exit_code = data; + wake_up_process(child); + res = data; + goto out; + } - case PTRACE_DETACH: { /* detach a process that was attached. */ - if ((unsigned long) data > _NSIG) { - res = -EIO; - goto out; - } - child->flags &= ~(PF_PTRACED|PF_TRACESYS); + /* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: { + if (child->state != TASK_ZOMBIE) { wake_up_process(child); - child->exit_code = data; - REMOVE_LINKS(child); - child->p_pptr = child->p_opptr; - SET_LINKS(child); - res = 0; - goto out; + child->exit_code = SIGKILL; + } + res = 0; + goto out; } - default: + case PTRACE_DETACH: { /* detach a process that was attached. */ + if ((unsigned long) data > _NSIG) { res = -EIO; goto out; + } + child->flags &= ~(PF_PTRACED|PF_TRACESYS); + wake_up_process(child); + child->exit_code = data; + REMOVE_LINKS(child); + child->p_pptr = child->p_opptr; + SET_LINKS(child); + res = 0; + goto out; + } + + default: + res = -EIO; + goto out; } out: unlock_kernel(); diff --git a/arch/mips/kernel/r4k_switch.S b/arch/mips/kernel/r4k_switch.S index 765de0d85..e8bc524a4 100644 --- a/arch/mips/kernel/r4k_switch.S +++ b/arch/mips/kernel/r4k_switch.S @@ -1,12 +1,12 @@ -/* - * r4k_switch.S: R4xx0 specific task switching code. +/* $Id: r4k_switch.S,v 1.3 1998/04/05 11:23:52 ralf Exp $ * - * Copyright (C) 1994, 1995, 1996 by Ralf Baechle and Andreas Busse + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. * - * Multi-cpu abstraction and macros for easier reading: + * Copyright (C) 1994, 1995, 1996, 1998 by Ralf Baechle * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) - * - * $Id: r4k_switch.S,v 1.3 1998/04/04 13:59:38 ralf Exp $ + * Copyright (C) 1994, 1995, 1996, by Andreas Busse */ #include <asm/asm.h> #include <asm/bootinfo.h> @@ -28,12 +28,8 @@ .set mips3 .align 5 LEAF(r4xx0_resume) - mfc0 t1, CP0_STATUS # fp exception boundary - sll t0, t1, 2 - bgez t0, 1f - nop - cfc1 zero, fcr31 -1: sw t1, THREAD_STATUS($28) + mfc0 t1, CP0_STATUS + sw t1, THREAD_STATUS($28) CPU_SAVE_NONSCRATCH($28) sw ra, THREAD_REG31($28) @@ -114,7 +110,7 @@ LEAF(r4xx0_save_fp) * We initialize fcr31 to rounding to nearest, no exceptions. */ -#define FPU_DEFAULT 0x00000600 +#define FPU_DEFAULT 0x00000000 LEAF(r4xx0_init_fpu) mfc0 t0, CP0_STATUS diff --git a/arch/mips/kernel/softfp.S b/arch/mips/kernel/softfp.S new file mode 100644 index 000000000..85ab779a9 --- /dev/null +++ b/arch/mips/kernel/softfp.S @@ -0,0 +1,667 @@ +/* $Id: softfp.S,v 1.1 1998/07/14 09:33:48 ralf Exp $ + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1998 by Ralf Baechle + * + * For now it's just a crude hack good enough to run certain fp programs like + * Mozilla. + * XXX: Handle MIPS II/III/IV/V enhancements, exceptions, ... + */ +#include <asm/regdef.h> +#include <asm/asm.h> + +#ifndef __KERNEL__ +#define printk printf +#endif + +#define LOCK_KERNEL +#define UNLOCK_KERNEL + +/* + * This duplicates definitions from <linux/kernel.h>. + */ +#define KERN_EMERG "<0>" /* system is unusable */ +#define KERN_ALERT "<1>" /* action must be taken immediately */ +#define KERN_CRIT "<2>" /* critical conditions */ +#define KERN_ERR "<3>" /* error conditions */ +#define KERN_WARNING "<4>" /* warning conditions */ +#define KERN_NOTICE "<5>" /* normal but significant condition */ +#define KERN_INFO "<6>" /* informational */ +#define KERN_DEBUG "<7>" /* debug-level messages */ + +/* + * This duplicates definitions from <asm/signal.h> + */ +#define SIGILL 4 /* Illegal instruction (ANSI). */ + +/* + * Definitions about the instruction format + */ +#define fd_shift 6 +#define fr_shift 21 +#define fs_shift 11 +#define ft_shift 16 + +/* + * NaNs as use by the MIPS architecture + */ +#define S_QNaN 0x7fbfffff +#define D_QNaN 0x7ff7ffffffffffff +#define W_QNaN 0x7fffffff +#define L_QNaN 0x7fffffffffffffff + +/* + * Checking for NaNs + */ +#define S_is_QNaN(reg,res) \ + sll res, reg, S_F_size - S_F_bits +#define D_is_QNaN(reg1,reg2,res) \ + sll res, reg1, (D_F_size - 32) - (D_F_bits - 32); \ + or res, reg2 + +/* + * Checking for Denorms + */ +#define S_is_Denorm(reg,res) \ + li res, 1 << (S_F_bits - 1); \ + and reg, res + +/* + * Some constants that define the properties of single precission numbers. + */ +#define S_M_prec 24 +#define S_E_max 127 +#define S_E_min -126 +#define S_E_bias 127 +#define S_E_bits 8 +#define S_F_bits 23 +#define S_F_size 32 + +/* Set temp0, if exponent of reg is S_E_max + 1. */ +#define S_is_E_max(reg,temp0,temp1) \ + li temp0, (S_E_max + 1 + S_E_bias) << S_F_bits; \ + and temp1, temp0, reg; \ + seq temp0, temp1 /* temp0 != 0 if NaN */ + +/* Clear temp0, if exponent of reg is S_E_min - 1. */ +#define S_is_E_min(reg,temp0) \ + li temp0, (S_E_min - 1 + S_E_bias) << S_F_bits; \ + and temp0, reg /* temp0 == 0 if denorm or zero */ + +/* Set temp0 if reg is a NaN assuming S_is_E_max is true */ +#define S_get_F(reg,temp0) \ + li temp0, (1 << S_F_bits) - 1; \ + and temp0, reg /* temp0 != 0 if NaN */ + +/* Set res if fraction of reg is != 0. */ +#define S_is_Inf(reg,res) \ + li res, (1 << S_F_bits) - 1; \ + and res, reg /* temp0 == 0 if Inf */ + + +/* + * Some constants that define the properties of double precission numbers. + */ +#define D_M_prec 53 +#define D_E_max 1023 +#define D_E_min -1022 +#define D_E_bias 1023 +#define D_E_bits 8 +#define D_F_bits 52 +#define D_F_size 64 + +/* Set temp0, if exponent of reg1/reg2 is D_E_max. */ +#define D_is_E_max(reg1,reg2,temp0,temp1) \ + li temp0, (D_E_max + 1 + D_E_bias) << (D_F_bits - 32); \ + and temp1, temp0, reg1; \ + seq temp0, temp1 /* temp0 != 0 if NaN */ + +/* Clear temp0, if exponent of reg is D_E_min. */ +#define D_is_E_min(reg1,reg2,res) \ + li res, (D_E_min + 1 + D_E_bias) << (D_F_bits - 32); \ + and res, reg1 /* temp0 == 0 if NaN or zero */ + +/* Set res if reg is a NaN assuming S_is_E_max is true */ +#define D_get_F(reg1,reg2,res) \ + li res, (1 << (D_F_bits - 32)) - 1; \ + and res, reg1 /* temp0 != 0 if NaN */ + +/* Set temp0 if reg1/reg2 is a NaN */ +#define D_is_NAN(reg1,reg2,temp0,temp1) \ + li temp0, (1 << (D_F_bits - 32) - 1; \ + and temp0, reg1; \ + or temp0, reg2; \ + sne temp0, zero, temp0 /* temp0 != 0 if NaN */ + +/* Set res if fraction of reg1/reg2 is != 0. */ +#define D_is_Inf(reg1,reg2,res) \ + li res, (1 << (D_F_bits - 32)) - 1; \ + and res, reg1; \ + or res, reg2 /* temp0 == 0 if Inf */ + +/* Complain about yet unhandled instruction. */ +#define BITCH(insn) \ +insn: LOCK_KERNEL; \ + la a1, 8f; \ + TEXT(#insn); \ + la a1, nosim; \ + UNLOCK_KERNEL; \ + j done + + .data +nosim: .asciz KERN_DEBUG "Don't know how to simulate %s instruction\n" + .previous + +/* + * When we come here, we've saved some of the integer registers and + * reenabled interrupts. + */ +LEAF(simfp) + .set noreorder + .cpload $25 + .set reorder + + subu sp, 16 + .cprestore 20 + sw ra, 16(sp) + + /* For now we assume that we get the opcode to simulate passed in as + an argument. */ + move t0, a0 + + /* + * First table lookup using insn[5:0] + */ + la t1, lowtab + andi t2, t0, 0x3f + sll t2, t2, 2 + addu t1, t2 + lw t1, (t1) + jr t1 + END(simfp) + +/* + * We only decode the lower 3 of the 5 bit in the fmt field. That way we + * can keep the jump table significantly shorter. + */ +#define FMT_switch(insn,opc,temp0,temp1) \ +insn: srl temp0, opc, 19; \ + andi temp0, 0x1c; \ + la temp1, insn ## .tab; \ + addu temp0, temp1; \ + lw temp0, (temp0); \ + jr temp0; \ + \ + .data; \ +insn ## .tab: \ + .word insn ## .s, insn ## .d, unimp, unimp; \ + .word insn ## .w, insn ## .l, unimp, unimp; \ + .previous + + BITCH(add) + BITCH(sub) + BITCH(mul) + BITCH(div) + BITCH(sqrt) + BITCH(abs) + BITCH(mov) + BITCH(neg) + BITCH(round.l) + BITCH(trunc.l) + BITCH(ceil.l) + BITCH(floor.l) + BITCH(round.w) + BITCH(trunc.w) + BITCH(ceil.w) + BITCH(floor.w) + BITCH(cvt.s) + BITCH(cvt.d) + +/* ------------------------------------------------------------------------ */ + +FMT_switch(cvt.w,t0,t1,t2) + +/* Convert a single fp to a fixed point integer. */ +cvt.w.s: + srl t1, t0, fs_shift # Get source register + andi t1, 31 + jal s_get_fpreg + + S_is_E_max(t1,t2,t3) + beqz t2, 3f + /* Might be a NaN or Inf. */ + S_get_F(t1,t2) + beqz t2, 2f + + /* It's a NaN. IEEE says undefined. */ + /* Is it a QNaN? Then the result is a QNaN as well. */ + S_is_QNaN(t1,t2) + bltz t2, 1f + + /* XXX Ok, it's a SNaN. Signal invalid exception, if enabled. + For now we don't signal and supply a QNaN for result. */ + +1: li t2, W_QNaN + srl t1, t0, fd_shift # Put result register + andi t1, 31 + jal s_put_fpreg + j done +2: + + S_is_Inf(t1,t2) + bnez t2, 2f + + /* It's +/- Inf. Set register to +/- max. integer. */ + /* XXX Send invalid operation exception instead, if enabled. */ + srl t1, t1, 31 # Extract sign bit + li t2, 0x7fffffff + addu t2, t1 + + srl t1, t0, fd_shift # Put result register + andi t1, 31 + jal s_put_fpreg + j done +2: +3: + + /* But then it might be a denorm or zero? */ + S_is_E_min(t1,t2) + bnez t2, 2f + + /* Ok, it's a denorm or zero. */ + S_get_F(t1,t2) + beqz t2, 1f + + /* It's a denorm. */ + /* XXX Should be signaling inexact exception, if enabled. */ + /* Fall through. */ +1: + /* Yes, it is a denorm or zero. Supply a zero as result. */ + move t2, zero + srl t1, t0, fd_shift # Put result register + andi t1, 31 + jal s_put_fpreg + j done +2: + + /* XXX Ok, it's a normal number. We don't handle that case yet. + If we have fp hardware this case is unreached. Add this for + full fp simulation. */ + + /* Done, return. */ + lw ra, 16(sp) + addu sp, 16 + jr ra + +/* Convert a double fp to a fixed point integer. */ +cvt.w.d: + srl t1, t0, fs_shift # Get source register + andi t1, 31 + jal d_get_fpreg + + D_is_E_max(t1,t2,t3,t4) + beqz t3, 3f + + /* Might be a NaN or Inf. */ + D_get_F(t1,t2,t3) + or t3, t2 + beqz t3, 2f + + /* It's a NaN. IEEE says undefined. */ + /* Is it a QNaN? Then the result is a QNaN as well. */ + D_is_QNaN(t1,t2,t3) + bltz t3, 1f + + /* XXX Ok, it's a SNaN. Signal invalid exception, if enabled. + For now we don't signal and supply a QNaN for result. */ + +1: li t2, W_QNaN + srl t1, t0, fd_shift # Put result register + andi t1, 31 + jal s_put_fpreg + j done +2: + + D_is_Inf(t1,t2,t3) + bnez t3, 2f + + /* It's +/- Inf. Set register to +/- max. integer. */ + /* XXX Send invalid operation exception instead, if enabled. */ + srl t1, t1, 31 # Extract sign bit + li t2, 0x7fffffff + addu t2, t1 + + srl t1, t0, fd_shift # Put result register + andi t1, 31 + jal s_put_fpreg + j done +2: +3: + + /* But then it might be a denorm or zero? */ + D_is_E_min(t1,t2,t3) + bnez t3, 2f + + /* Ok, it's a denorm or zero. */ + D_get_F(t1,t2,t3) + or t3, t2 + beqz t3, 1f + + /* It's a denorm. */ + /* XXX Should be signaling inexact exception, if enabled. */ + /* Fall through. */ +1: + /* Yes, it is a denorm or zero. Supply a zero as result. */ + move t2, zero + srl t1, t0, fd_shift # Put result register + andi t1, 31 + jal s_put_fpreg + j done +2: + + /* XXX Ok, it's a normal number. We don't handle that case yet. + If we have fp hardware this case is only reached if the value + of the source register exceeds the range which is representable + in a single precission register. For now we kludge by returning + +/- maxint and don't signal overflow. */ + + srl t1, t1, 31 # Extract sign bit + li t2, 0x7fffffff + addu t2, t1 + + srl t1, t0, fd_shift # Put result register + andi t1, 31 + jal s_put_fpreg + + /* Done, return. */ + lw ra, 16(sp) + addu sp, 16 + jr ra + +cvt.w.w = unimp # undefined result +cvt.w.l = unimp # undefined result + +/* MIPS III extension, no need to handle for 32bit OS. */ +cvt.l = unimp + +/* ------------------------------------------------------------------------ */ + + BITCH(c.f) + BITCH(c.un) + BITCH(c.eq) + BITCH(c.ueq) + BITCH(c.olt) + BITCH(c.ult) + BITCH(c.ole) + BITCH(c.ule) + BITCH(c.sf) + BITCH(c.ngle) + BITCH(c.seq) + BITCH(c.ngl) + BITCH(c.lt) + BITCH(c.nge) + BITCH(c.le) + BITCH(c.ngt) + +/* Get the single precission register which's number is in t1. */ +s_get_fpreg: + .set noat + sll AT, t1, 2 + sll t1, 3 + addu t1, AT + la AT, 1f + addu AT, t1 + jr AT + .set at + +1: mfc1 t1, $0 + jr ra + mfc1 t1, $1 + jr ra + mfc1 t1, $2 + jr ra + mfc1 t1, $3 + jr ra + mfc1 t1, $4 + jr ra + mfc1 t1, $5 + jr ra + mfc1 t1, $6 + jr ra + mfc1 t1, $7 + jr ra + mfc1 t1, $8 + jr ra + mfc1 t1, $9 + jr ra + mfc1 t1, $10 + jr ra + mfc1 t1, $11 + jr ra + mfc1 t1, $12 + jr ra + mfc1 t1, $13 + jr ra + mfc1 t1, $14 + jr ra + mfc1 t1, $15 + jr ra + mfc1 t1, $16 + jr ra + mfc1 t1, $17 + jr ra + mfc1 t1, $18 + jr ra + mfc1 t1, $19 + jr ra + mfc1 t1, $20 + jr ra + mfc1 t1, $21 + jr ra + mfc1 t1, $22 + jr ra + mfc1 t1, $23 + jr ra + mfc1 t1, $24 + jr ra + mfc1 t1, $25 + jr ra + mfc1 t1, $26 + jr ra + mfc1 t1, $27 + jr ra + mfc1 t1, $28 + jr ra + mfc1 t1, $29 + jr ra + mfc1 t1, $30 + jr ra + mfc1 t1, $31 + jr ra + +/* + * Put the value in t2 into the single precission register which's number + * is in t1. + */ +s_put_fpreg: + .set noat + sll AT, t1, 2 + sll t1, 3 + addu t1, AT + la AT, 1f + addu AT, t1 + jr AT + .set at + +1: mtc1 t2, $0 + jr ra + mtc1 t2, $1 + jr ra + mtc1 t2, $2 + jr ra + mtc1 t2, $3 + jr ra + mtc1 t2, $4 + jr ra + mtc1 t2, $5 + jr ra + mtc1 t2, $6 + jr ra + mtc1 t2, $7 + jr ra + mtc1 t2, $8 + jr ra + mtc1 t2, $9 + jr ra + mtc1 t2, $10 + jr ra + mtc1 t2, $11 + jr ra + mtc1 t2, $12 + jr ra + mtc1 t2, $13 + jr ra + mtc1 t2, $14 + jr ra + mtc1 t2, $15 + jr ra + mtc1 t2, $16 + jr ra + mtc1 t2, $17 + jr ra + mtc1 t2, $18 + jr ra + mtc1 t2, $19 + jr ra + mtc1 t2, $20 + jr ra + mtc1 t2, $21 + jr ra + mtc1 t2, $22 + jr ra + mtc1 t2, $23 + jr ra + mtc1 t2, $24 + jr ra + mtc1 t2, $25 + jr ra + mtc1 t2, $26 + jr ra + mtc1 t2, $27 + jr ra + mtc1 t2, $28 + jr ra + mtc1 t2, $29 + jr ra + mtc1 t2, $30 + jr ra + mtc1 t2, $31 + jr ra + +/* Get the double precission register which's number is in t1 into t1/t2. */ +d_get_fpreg: + .set noat + sll t1, 3 + la AT, 1f + addu AT, t1 + jr AT + .set at + +1: mfc1 t1, $0 + mfc1 t2, $1 + jr ra + mfc1 t1, $2 + mfc1 t2, $3 + jr ra + mfc1 t1, $4 + mfc1 t2, $5 + jr ra + mfc1 t1, $6 + mfc1 t2, $7 + jr ra + mfc1 t1, $8 + mfc1 t2, $9 + jr ra + mfc1 t1, $10 + mfc1 t2, $11 + jr ra + mfc1 t1, $12 + mfc1 t2, $13 + jr ra + mfc1 t1, $14 + mfc1 t2, $15 + jr ra + mfc1 t1, $16 + mfc1 t2, $17 + jr ra + mfc1 t1, $18 + mfc1 t2, $19 + jr ra + mfc1 t1, $20 + mfc1 t2, $21 + jr ra + mfc1 t1, $22 + mfc1 t2, $23 + jr ra + mfc1 t1, $24 + mfc1 t2, $25 + jr ra + mfc1 t1, $26 + mfc1 t2, $27 + jr ra + mfc1 t1, $28 + mfc1 t2, $29 + jr ra + mfc1 t1, $30 + mfc1 t2, $31 + jr ra + +/* + * Send an invalid operation exception. + */ +invalid: + lw ra, 16(sp) + addu sp, 16 + jr ra + +/* + * Done, just skip over the current instruction + */ +done: + lw ra, 16(sp) + addu sp, 16 + jr ra + +unimp: + /* We've run into an yet unknown instruction. This happens either + on new, yet unsupported CPU types or when the faulting instruction + is being executed for cache but has been overwritten in memory. */ + LOCK_KERNEL + move a0, t0 + PRINT(KERN_DEBUG "FP support: unknown fp op %08lx, ") + PRINT("please mail to ralf@gnu.org.\n") + + li a0, SIGILL # Die, sucker ... + move a1, $28 + jal force_sig + UNLOCK_KERNEL + + lw ra, 16(sp) + addu sp, 16 + jr ra + +/* + * Jump table for the lowest 6 bits of a cp1 instruction. + */ + .data +lowtab: .word add, sub, mul, div, sqrt, abs, mov, neg + .word round.l,trunc.l,ceil.l,floor.l,round.w,trunc.w,ceil.w,floor.w + .word unimp, unimp, unimp, unimp, unimp, unimp, unimp, unimp + .word unimp, unimp, unimp, unimp, unimp, unimp, unimp, unimp + .word cvt.s, cvt.d, unimp, unimp, cvt.w, cvt.l, unimp, unimp + .word unimp, unimp, unimp, unimp, unimp, unimp, unimp, unimp + .word c.f, c.un, c.eq, c.ueq, c.olt, c.ult, c.ole, c.ule + .word c.sf, c.ngle,c.seq, c.ngl, c.lt, c.nge, c.le, c.ngt diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 7d5a703f6..8c941c4ee 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -1,4 +1,4 @@ -/* $Id: traps.c,v 1.12 1998/06/30 00:21:53 ralf Exp $ +/* $Id: traps.c,v 1.13 1998/07/10 01:14:48 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -226,6 +226,9 @@ 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; + #ifdef CONFIG_MIPS_FPE_MODULE if (fpe_handler != NULL) { fpe_handler(regs, fcr31); @@ -246,13 +249,22 @@ void do_fpe(struct pt_regs *regs, unsigned long fcr31) : "r" (fcr31)); goto out; } - printk("Unimplemented exception at 0x%08lx in %s.\n", - regs->cp0_epc, current->comm); + pc = regs->cp0_epc + ((regs->cp0_cause & CAUSEF_BD) ? 4 : 0); + if (get_user(insn, (unsigned int *)pc)) { + /* XXX Can this happen? */ + 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); } if (compute_return_epc(regs)) goto out; - force_sig(SIGFPE, current); + //force_sig(SIGFPE, current); + printk(KERN_DEBUG "Should send SIGFPE to %s\n", current->comm); + out: unlock_kernel(); } |