diff options
-rw-r--r-- | sys/arch/mips64/include/_types.h | 3 | ||||
-rw-r--r-- | sys/arch/mips64/include/cpu.h | 8 | ||||
-rw-r--r-- | sys/arch/mips64/include/proc.h | 15 | ||||
-rw-r--r-- | sys/arch/mips64/mips64/cpu.c | 6 | ||||
-rw-r--r-- | sys/arch/mips64/mips64/fp_emulate.c | 506 | ||||
-rw-r--r-- | sys/arch/mips64/mips64/mips64_machdep.c | 34 | ||||
-rw-r--r-- | sys/arch/mips64/mips64/trap.c | 175 | ||||
-rw-r--r-- | sys/arch/mips64/mips64/vm_machdep.c | 6 |
8 files changed, 725 insertions, 28 deletions
diff --git a/sys/arch/mips64/include/_types.h b/sys/arch/mips64/include/_types.h index bd59a97e6e8..e22ed5c01bf 100644 --- a/sys/arch/mips64/include/_types.h +++ b/sys/arch/mips64/include/_types.h @@ -1,4 +1,4 @@ -/* $OpenBSD: _types.h,v 1.7 2009/11/04 19:14:10 kettenis Exp $ */ +/* $OpenBSD: _types.h,v 1.8 2010/11/24 21:16:26 miod Exp $ */ /*- * Copyright (c) 1990, 1993 @@ -135,5 +135,6 @@ typedef struct label_t { /* Feature test macros */ #define __HAVE_TIMECOUNTER +#define __HAVE_EXEC_MD_MAP #endif /* _MIPS64__TYPES_H_ */ diff --git a/sys/arch/mips64/include/cpu.h b/sys/arch/mips64/include/cpu.h index a7843f21435..2ef12590d9d 100644 --- a/sys/arch/mips64/include/cpu.h +++ b/sys/arch/mips64/include/cpu.h @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.h,v 1.68 2010/10/24 15:40:03 miod Exp $ */ +/* $OpenBSD: cpu.h,v 1.69 2010/11/24 21:16:26 miod Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -279,11 +279,13 @@ extern vaddr_t uncached_base; #define BREAK_BRKPT_VAL 514 #define BREAK_SOVER_VAL 515 #define BREAK_DDB_VAL 516 +#define BREAK_FPUEMUL_VAL 517 #define BREAK_KDB (BREAK_INSTR | (BREAK_KDB_VAL << BREAK_VAL_SHIFT)) #define BREAK_SSTEP (BREAK_INSTR | (BREAK_SSTEP_VAL << BREAK_VAL_SHIFT)) #define BREAK_BRKPT (BREAK_INSTR | (BREAK_BRKPT_VAL << BREAK_VAL_SHIFT)) #define BREAK_SOVER (BREAK_INSTR | (BREAK_SOVER_VAL << BREAK_VAL_SHIFT)) #define BREAK_DDB (BREAK_INSTR | (BREAK_DDB_VAL << BREAK_VAL_SHIFT)) +#define BREAK_FPUEMUL (BREAK_INSTR | (BREAK_FPUEMUL_VAL << BREAK_VAL_SHIFT)) /* * The floating point version and status registers. @@ -550,6 +552,7 @@ void cpu_startclock(struct cpu_info *); extern vaddr_t CpuCacheAliasMask; +struct exec_package; struct tlb_entry; struct user; @@ -604,10 +607,13 @@ int tlb_update(vaddr_t, unsigned); void tlb_read(int, struct tlb_entry *); void build_trampoline(vaddr_t, vaddr_t); +int exec_md_map(struct proc *, struct exec_package *); void savectx(struct user *, int); void enable_fpu(struct proc *); void save_fpu(void); +int fpe_branch_emulate(struct proc *, struct trap_frame *, uint32_t, + vaddr_t); int guarded_read_4(paddr_t, uint32_t *); int guarded_write_4(paddr_t, uint32_t); diff --git a/sys/arch/mips64/include/proc.h b/sys/arch/mips64/include/proc.h index b78d8f109e8..f4d31e7201a 100644 --- a/sys/arch/mips64/include/proc.h +++ b/sys/arch/mips64/include/proc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: proc.h,v 1.6 2010/11/24 21:01:02 miod Exp $ */ +/* $OpenBSD: proc.h,v 1.7 2010/11/24 21:16:26 miod Exp $ */ /* * Copyright (c) 1992, 1993 @@ -50,6 +50,11 @@ struct mdproc { vaddr_t md_ss_addr; /* single step address */ uint32_t md_ss_instr; /* saved single step instruction */ + /* fpu emulation fields */ + vaddr_t md_fppgva; /* vaddr of the branch emulation page */ + vaddr_t md_fpbranchva; /* vaddr of fp branch destination */ + vaddr_t md_fpslotva; /* initial vaddr of delay slot */ + /* The following is RM7000 dependent, but kept in for compatibility */ int md_pc_ctrl; /* performance counter control */ int md_pc_count; /* performance counter */ @@ -59,7 +64,13 @@ struct mdproc { int md_watch_m; }; -/* md_flags */ +/* + * Values for md_flags. + * MDP_FPUSED has two meanings: if the floating point hardware (coprocessor + * #1) is available, it means it has been used; if there is no floating + * point hardware, it means the process is currently running a duplicated + * delay slot, created by the branch emulation logic. + */ #define MDP_FPUSED 0x00000001 /* floating point coprocessor used */ #define MDP_PERF 0x00010000 /* Performance counter used */ #define MDP_WATCH1 0x00020000 /* Watch register 1 used */ diff --git a/sys/arch/mips64/mips64/cpu.c b/sys/arch/mips64/mips64/cpu.c index 640bd9796e9..fe8fe1a4e52 100644 --- a/sys/arch/mips64/mips64/cpu.c +++ b/sys/arch/mips64/mips64/cpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.c,v 1.35 2010/09/13 21:59:07 syuu Exp $ */ +/* $OpenBSD: cpu.c,v 1.36 2010/11/24 21:16:28 miod Exp $ */ /* * Copyright (c) 1997-2004 Opsycon AB (www.opsycon.se) @@ -335,6 +335,7 @@ cpu_switchto(struct proc *oldproc, struct proc *newproc) void enable_fpu(struct proc *p) { +#ifndef FPUEMUL struct cpu_info *ci = curcpu(); if (p->p_md.md_regs->sr & SR_FR_32) @@ -345,11 +346,13 @@ enable_fpu(struct proc *p) ci->ci_fpuproc = p; p->p_md.md_regs->sr |= SR_COP_1_BIT; p->p_md.md_flags |= MDP_FPUSED; +#endif } void save_fpu(void) { +#ifndef FPUEMUL struct cpu_info *ci = curcpu(); struct proc *p; @@ -359,6 +362,7 @@ save_fpu(void) MipsSaveCurFPState(p); else MipsSaveCurFPState16(p); +#endif } #ifdef MULTIPROCESSOR diff --git a/sys/arch/mips64/mips64/fp_emulate.c b/sys/arch/mips64/mips64/fp_emulate.c index a1de323ac48..aaec0c96f20 100644 --- a/sys/arch/mips64/mips64/fp_emulate.c +++ b/sys/arch/mips64/mips64/fp_emulate.c @@ -1,4 +1,4 @@ -/* $OpenBSD: fp_emulate.c,v 1.2 2010/10/27 20:05:12 miod Exp $ */ +/* $OpenBSD: fp_emulate.c,v 1.3 2010/11/24 21:16:28 miod Exp $ */ /* * Copyright (c) 2010 Miodrag Vallat. @@ -17,11 +17,9 @@ */ /* - * Floating Point completion code (MI softfloat code control engine). + * Floating Point completion/emulation code (MI softfloat code control engine). * * Supports all MIPS IV COP1 and COP1X floating-point instructions. - * Floating-point load and store instructions, as well as branch instructions, - * are not handled, as they should not require completion code. */ #include <sys/param.h> @@ -48,6 +46,12 @@ int fpu_emulate_cop1x(struct trap_frame *, uint32_t); uint64_t fpu_load(struct trap_frame *, uint, uint); void fpu_store(struct trap_frame *, uint, uint, uint64_t); +#ifdef FPUEMUL +int nofpu_emulate_cop1(struct trap_frame *, uint32_t, union sigval *); +int nofpu_emulate_cop1x(struct trap_frame *, uint32_t, union sigval *); +int nofpu_emulate_loadstore(struct trap_frame *, uint32_t, union sigval *); +int nofpu_emulate_movci(struct trap_frame *, uint32_t); +#endif typedef int (fpu_fn3)(struct trap_frame *, uint, uint, uint, uint); typedef int (fpu_fn4)(struct trap_frame *, uint, uint, uint, uint, uint); @@ -125,10 +129,15 @@ MipsFPTrap(struct trap_frame *tf) int fault_type = SI_NOINFO; int update_pcb = 0; int emulate = 0; +#ifdef FPUEMUL + int skip_insn = 1; +#else uint32_t sr; +#endif KDASSERT(tf == p->p_md.md_regs); +#ifndef FPUEMUL /* * Enable FPU, and read its status register. */ @@ -148,6 +157,15 @@ MipsFPTrap(struct trap_frame *tf) sig = SIGFPE; goto deliver; } +#else +#ifdef CPU_OCTEON + /* + * SR_FR_32 is hardwired to zero on Octeon; make sure it is + * set in the emulation view of the FPU state. + */ + tf->sr |= SR_FR_32; +#endif +#endif /* FPUEMUL */ /* * Get the faulting instruction. This should not fail, and @@ -185,9 +203,40 @@ MipsFPTrap(struct trap_frame *tf) * Not a FPU instruction. */ break; +#ifdef FPUEMUL + case OP_SPECIAL: + switch (inst.FRType.func) { + default: + /* + * Not a FPU instruction. + */ + break; + case OP_MOVCI: + /* + * This instruction should not require emulation, + * unless there is no FPU. + */ + emulate = 1; + break; + } + break; + case OP_LDC1: + case OP_LWC1: + case OP_SDC1: + case OP_SWC1: + /* + * These instructions should not require emulation, + * unless there is no FPU. + */ + emulate = 1; + break; +#endif case OP_COP1: switch (inst.RType.rs) { case OP_BC: +#ifdef FPUEMUL + skip_insn = 0; +#endif case OP_MF: case OP_DMF: case OP_CF: @@ -198,6 +247,9 @@ MipsFPTrap(struct trap_frame *tf) * These instructions should not require emulation, * unless there is no FPU. */ +#ifdef FPUEMUL + emulate = 1; +#endif break; default: emulate = 1; @@ -207,6 +259,25 @@ MipsFPTrap(struct trap_frame *tf) case OP_COP1X: switch (inst.FQType.op4) { default: + switch (inst.FRType.func) { +#ifdef FPUEMUL + case OP_LDXC1: + case OP_LWXC1: + case OP_SDXC1: + case OP_SWXC1: + case OP_PREFX: + /* + * These instructions should not require + * emulation, unless there is no FPU. + */ + emulate = 1; +#endif + default: + /* + * Not a valid instruction. + */ + break; + } break; case OP_MADD: case OP_MSUB: @@ -219,8 +290,10 @@ MipsFPTrap(struct trap_frame *tf) } if (emulate) { +#ifndef FPUEMUL KASSERT(p == ci->ci_fpuproc); save_fpu(); +#endif update_pcb = 1; sig = fpu_emulate(tf, insn, &sv); @@ -253,25 +326,39 @@ deliver: fault_type = FPE_FLTOVF; else /* if (excbits & FP_X_IMP) */ fault_type = FPE_FLTRES; + + break; +#ifdef FPUEMUL + case SIGBUS: + fault_type = BUS_ADRALN; + break; + case SIGSEGV: + fault_type = SEGV_MAPERR; break; +#endif } /* * Skip the instruction, unless we are delivering SIGILL. */ - - if (sig != SIGILL) { - if (tf->cause & CR_BR_DELAY) { - /* - * Note that it doesn't matter, at this point, - * that we pass the updated FSR value, as it is - * only used to decide whether to branch or not - * if the faulting instruction was BC1[FT]. - */ - tf->pc = MipsEmulateBranch(tf, tf->pc, fsr, 0); - } else - tf->pc += 4; +#ifdef FPUEMUL + if (skip_insn) { +#endif + if (sig != SIGILL) { + if (tf->cause & CR_BR_DELAY) { + /* + * Note that it doesn't matter, at this point, + * that we pass the updated FSR value, as it is + * only used to decide whether to branch or not + * if the faulting instruction was BC1[FT]. + */ + tf->pc = MipsEmulateBranch(tf, tf->pc, fsr, 0); + } else + tf->pc += 4; + } +#ifdef FPUEMUL } +#endif /* * Update the FPU status register. @@ -287,12 +374,17 @@ deliver: fsr &= ~FPCSR_C_MASK; if (update_pcb) tf->fsr = fsr; +#ifndef FPUEMUL __asm__ __volatile__ ("ctc1 %0, $31" :: "r" (fsr)); /* disable fpu before returning to trap() */ setsr(sr); +#endif if (sig != 0) { - sv.sival_ptr = (void *)pc; +#ifdef FPUEMUL + if (sig != SIGBUS && sig != SIGSEGV) +#endif + sv.sival_ptr = (void *)pc; KERNEL_PROC_LOCK(p); trapsignal(p, sig, 0, fault_type, sv); KERNEL_PROC_UNLOCK(p); @@ -314,10 +406,56 @@ fpu_emulate(struct trap_frame *tf, uint32_t insn, union sigval *sv) switch (inst.FRType.op) { default: break; +#ifdef FPUEMUL + case OP_SPECIAL: + return nofpu_emulate_movci(tf, insn); + case OP_LDC1: + case OP_LWC1: + case OP_SDC1: + case OP_SWC1: + return nofpu_emulate_loadstore(tf, insn, sv); +#endif case OP_COP1: - return fpu_emulate_cop1(tf, insn); + switch (inst.RType.rs) { +#ifdef FPUEMUL + case OP_MF: + case OP_DMF: + case OP_CF: + case OP_MT: + case OP_DMT: + case OP_CT: + case OP_BC: + return nofpu_emulate_cop1(tf, insn, sv); +#endif + default: + return fpu_emulate_cop1(tf, insn); + } + break; case OP_COP1X: - return fpu_emulate_cop1x(tf, insn); + switch (inst.FQType.op4) { +#ifdef FPUEMUL + default: + switch (inst.FRType.func) { + case OP_LDXC1: + case OP_LWXC1: + case OP_SDXC1: + case OP_SWXC1: + case OP_PREFX: + return nofpu_emulate_cop1x(tf, insn, sv); + default: + break; + } + break; + case OP_MADD: + case OP_MSUB: + case OP_NMADD: + case OP_NMSUB: + return fpu_emulate_cop1x(tf, insn); +#else + default: + return fpu_emulate_cop1x(tf, insn); +#endif + } } return SIGILL; @@ -1312,3 +1450,333 @@ fpu_trunc_w(struct trap_frame *tf, uint fmt, uint ft, uint fs, uint fd) /* round towards zero */ return fpu_int_w(tf, fmt, ft, fs, fd, FP_RZ); } + +#ifdef FPUEMUL + +/* + * Emulate a COP1 non-FPU instruction. + */ +int +nofpu_emulate_cop1(struct trap_frame *tf, uint32_t insn, union sigval *sv) +{ + register_t *regs = (register_t *)tf; + InstFmt inst; + int32_t cval; + + inst = *(InstFmt *)&insn; + + switch (inst.RType.rs) { + case OP_MF: + if (inst.FRType.fd != 0 || inst.FRType.func != 0) + return SIGILL; + if (inst.FRType.ft != ZERO) + regs[inst.FRType.ft] = + (int32_t)regs[FPBASE + inst.FRType.fs]; + break; + case OP_DMF: + if (inst.FRType.fd != 0 || inst.FRType.func != 0) + return SIGILL; + if ((tf->sr & SR_FR_32) != 0 || (inst.FRType.fs & 1) == 0) { + if (inst.FRType.ft != ZERO) + regs[inst.FRType.ft] = + fpu_load(tf, FMT_L, inst.FRType.fs); + } + break; + case OP_CF: + if (inst.FRType.fd != 0 || inst.FRType.func != 0) + return SIGILL; + if (inst.FRType.ft != ZERO) { + switch (inst.FRType.fs) { + case 0: /* FPC_ID */ + cval = MIPS_SOFT << 8; + break; + case 31: /* FPC_CSR */ + cval = (int32_t)tf->fsr; + break; + default: + cval = 0; + break; + } + regs[inst.FRType.ft] = (int64_t)cval; + } + break; + case OP_MT: + if (inst.FRType.fd != 0 || inst.FRType.func != 0) + return SIGILL; + regs[FPBASE + inst.FRType.fs] = (int32_t)regs[inst.FRType.ft]; + break; + case OP_DMT: + if (inst.FRType.fd != 0 || inst.FRType.func != 0) + return SIGILL; + if ((tf->sr & SR_FR_32) != 0 || (inst.FRType.fs & 1) == 0) { + fpu_store(tf, FMT_L, inst.FRType.fs, + regs[inst.FRType.ft]); + } + break; + case OP_CT: + if (inst.FRType.fd != 0 || inst.FRType.func != 0) + return SIGILL; + cval = (int32_t)regs[inst.FRType.ft]; + switch (inst.FRType.fs) { + case 31: /* FPC_CSR */ + cval &= ~FPCSR_C_E; + tf->fsr = cval; + break; + case 0: /* FPC_ID */ + default: + break; + } + break; + case OP_BC: + { + uint cc, nd, istf; + int condition; + vaddr_t dest; + uint32_t dinsn; + + cc = (inst.RType.rt & COPz_BC_CC_MASK) >> COPz_BC_CC_SHIFT; + nd = inst.RType.rt & COPz_BCL_TF_MASK; + istf = inst.RType.rt & COPz_BC_TF_MASK; + condition = tf->fsr & FPCSR_CONDVAL(cc); + if ((!condition && !istf) /*bc1f*/ || + (condition && istf) /*bc1t*/) { + /* + * Branch taken: if the delay slot is not a nop, + * copy the delay slot instruction to the dedicated + * relocation page, in order to be able to have the + * cpu process it and give control back to the + * kernel, for us to redirect to the branch + * destination. + */ + /* inline MipsEmulateBranch(tf, tf->pc, tf->fsr, insn)*/ + dest = tf->pc + 4 + ((short)inst.IType.imm << 2); + if (copyin((const void *)(tf->pc + 4), &dinsn, + sizeof dinsn)) { + sv->sival_ptr = (void *)(tf->pc + 4); + return SIGSEGV; + } + if (dinsn == 0x00000000 /* nop */ || + dinsn == 0x00000040 /* ssnop */) { + tf->pc = dest; + } else { + if (fpe_branch_emulate(curproc, tf, dinsn, + dest) != 0) + return SIGILL; + } + } else { + /* + * Branch not taken: skip the instruction, and + * skip the delay slot if it was a `branch likely' + * instruction. + */ + tf->pc += 4; + if (nd) + tf->pc += 4; + } + } + break; + } + + return 0; +} + +/* + * Emulate a COP1X non-FPU instruction. + */ +int +nofpu_emulate_cop1x(struct trap_frame *tf, uint32_t insn, union sigval *sv) +{ + register_t *regs = (register_t *)tf; + InstFmt inst; + vaddr_t va; + uint64_t ddata; + uint32_t wdata; + + inst = *(InstFmt *)&insn; + switch (inst.FRType.func) { + case OP_LDXC1: + if (inst.FQType.fs != 0) + return SIGILL; + va = (vaddr_t)regs[inst.FQType.fr] + + (vaddr_t)regs[inst.FQType.ft]; + if ((va & 0x07) != 0) { + sv->sival_ptr = (void *)va; + return SIGBUS; + } + if (copyin((const void *)va, &ddata, sizeof ddata) != 0) { + sv->sival_ptr = (void *)va; + return SIGSEGV; + } + if ((tf->sr & SR_FR_32) != 0 || (inst.FQType.fd & 1) == 0) + fpu_store(tf, FMT_L, inst.FQType.fd, ddata); + break; + case OP_LWXC1: + if (inst.FQType.fs != 0) + return SIGILL; + va = (vaddr_t)regs[inst.FQType.fr] + + (vaddr_t)regs[inst.FQType.ft]; + if ((va & 0x03) != 0) { + sv->sival_ptr = (void *)va; + return SIGBUS; + } +#ifdef __MIPSEB__ + va ^= 4; +#endif + if (copyin((const void *)va, &wdata, sizeof wdata) != 0) { + sv->sival_ptr = (void *)va; + return SIGSEGV; + } + regs[FPBASE + inst.FQType.fd] = wdata; + break; + case OP_SDXC1: + if (inst.FQType.fd != 0) + return SIGILL; + va = (vaddr_t)regs[inst.FQType.fr] + + (vaddr_t)regs[inst.FQType.ft]; + if ((va & 0x07) != 0) { + sv->sival_ptr = (void *)va; + return SIGBUS; + } + if ((tf->sr & SR_FR_32) != 0 || (inst.FQType.fs & 1) == 0) + ddata = fpu_load(tf, FMT_L, inst.FQType.fs); + else { + /* undefined behaviour, don't expose stack content */ + ddata = 0; + } + if (copyout(&ddata, (void *)va, sizeof ddata) != 0) { + sv->sival_ptr = (void *)va; + return SIGSEGV; + } + break; + case OP_SWXC1: + if (inst.FQType.fd != 0) + return SIGILL; + va = (vaddr_t)regs[inst.FQType.fr] + + (vaddr_t)regs[inst.FQType.ft]; + if ((va & 0x03) != 0) { + sv->sival_ptr = (void *)va; + return SIGBUS; + } +#ifdef __MIPSEB__ + va ^= 4; +#endif + wdata = regs[FPBASE + inst.FQType.fs]; + if (copyout(&wdata, (void *)va, sizeof wdata) != 0) { + sv->sival_ptr = (void *)va; + return SIGSEGV; + } + break; + case OP_PREFX: + /* nothing to do */ + break; + } + + return 0; +} + +/* + * Emulate a load/store instruction on FPU registers. + */ +int +nofpu_emulate_loadstore(struct trap_frame *tf, uint32_t insn, union sigval *sv) +{ + register_t *regs = (register_t *)tf; + InstFmt inst; + vaddr_t va; + uint64_t ddata; + uint32_t wdata; + + inst = *(InstFmt *)&insn; + switch (inst.IType.op) { + case OP_LDC1: + va = (vaddr_t)regs[inst.IType.rs] + (int16_t)inst.IType.imm; + if ((va & 0x07) != 0) { + sv->sival_ptr = (void *)va; + return SIGBUS; + } + if (copyin((const void *)va, &ddata, sizeof ddata) != 0) { + sv->sival_ptr = (void *)va; + return SIGSEGV; + } + if ((tf->sr & SR_FR_32) != 0 || (inst.IType.rt & 1) == 0) + fpu_store(tf, FMT_L, inst.IType.rt, ddata); + break; + case OP_LWC1: + va = (vaddr_t)regs[inst.IType.rs] + (int16_t)inst.IType.imm; + if ((va & 0x03) != 0) { + sv->sival_ptr = (void *)va; + return SIGBUS; + } +#ifdef __MIPSEB__ + va ^= 4; +#endif + if (copyin((const void *)va, &wdata, sizeof wdata) != 0) { + sv->sival_ptr = (void *)va; + return SIGSEGV; + } + regs[FPBASE + inst.IType.rt] = wdata; + break; + case OP_SDC1: + va = (vaddr_t)regs[inst.IType.rs] + (int16_t)inst.IType.imm; + if ((va & 0x07) != 0) { + sv->sival_ptr = (void *)va; + return SIGBUS; + } + if ((tf->sr & SR_FR_32) != 0 || (inst.IType.rt & 1) == 0) + ddata = fpu_load(tf, FMT_L, inst.IType.rt); + else { + /* undefined behaviour, don't expose stack content */ + ddata = 0; + } + if (copyout(&ddata, (void *)va, sizeof ddata) != 0) { + sv->sival_ptr = (void *)va; + return SIGSEGV; + } + break; + case OP_SWC1: + va = (vaddr_t)regs[inst.IType.rs] + (int16_t)inst.IType.imm; + if ((va & 0x03) != 0) { + sv->sival_ptr = (void *)va; + return SIGBUS; + } +#ifdef __MIPSEB__ + va ^= 4; +#endif + wdata = regs[FPBASE + inst.IType.rt]; + if (copyout(&wdata, (void *)va, sizeof wdata) != 0) { + sv->sival_ptr = (void *)va; + return SIGSEGV; + } + break; + } + + return 0; +} + +/* + * Emulate MOVF and MOVT. + */ +int +nofpu_emulate_movci(struct trap_frame *tf, uint32_t insn) +{ + register_t *regs = (register_t *)tf; + InstFmt inst; + uint cc, istf; + int condition; + + inst = *(InstFmt *)&insn; + if ((inst.RType.rt & 0x02) != 0 || inst.RType.shamt != 0) + return SIGILL; + + cc = inst.RType.rt >> 2; + istf = inst.RType.rt & COPz_BC_TF_MASK; + condition = tf->fsr & FPCSR_CONDVAL(cc); + if ((!condition && !istf) /*movf*/ || (condition && istf) /*movt*/) { + if (inst.RType.rd != ZERO) + regs[inst.RType.rd] = regs[inst.RType.rs]; + } + + return 0; +} + +#endif /* FPUEMUL */ diff --git a/sys/arch/mips64/mips64/mips64_machdep.c b/sys/arch/mips64/mips64/mips64_machdep.c index 7c322022a25..3cb514bf63c 100644 --- a/sys/arch/mips64/mips64/mips64_machdep.c +++ b/sys/arch/mips64/mips64/mips64_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mips64_machdep.c,v 1.1 2010/10/24 15:40:03 miod Exp $ */ +/* $OpenBSD: mips64_machdep.c,v 1.2 2010/11/24 21:16:28 miod Exp $ */ /* * Copyright (c) 2009, 2010 Miodrag Vallat. @@ -141,7 +141,9 @@ setregs(p, pack, stack, retval) #endif p->p_md.md_regs->sr |= idle_mask & SR_INT_MASK; p->p_md.md_regs->ic = (idle_mask << 8) & IC_INT_MASK; +#ifndef FPUEMUL p->p_md.md_flags &= ~MDP_FPUSED; +#endif if (ci->ci_fpuproc == p) ci->ci_fpuproc = NULL; p->p_md.md_ss_addr = 0; @@ -151,3 +153,33 @@ setregs(p, pack, stack, retval) retval[1] = 0; } + +int +exec_md_map(struct proc *p, struct exec_package *pack) +{ +#ifdef FPUEMUL + int rc; + vaddr_t va; + + /* + * If we are running with FPU instruction emulation, we need + * to allocate a special page in the process' address space, + * in order to be able to emulate delay slot instructions of + * successful conditional branches. + */ + + va = uvm_map_hint(p, UVM_PROT_RX); + rc = uvm_map(&p->p_vmspace->vm_map, &va, PAGE_SIZE, NULL, + UVM_UNKNOWN_OFFSET, 0, + UVM_MAPFLAG(UVM_PROT_NONE, UVM_PROT_ALL, UVM_INH_COPY, + UVM_ADV_NORMAL, UVM_FLAG_COPYONW)); + if (rc != 0) + return rc; +#ifdef DEBUG + printf("%s: p %p fppgva %p\n", __func__, p, va); +#endif + p->p_md.md_fppgva = va; +#endif + + return 0; +} diff --git a/sys/arch/mips64/mips64/trap.c b/sys/arch/mips64/mips64/trap.c index 0cf070b140b..bc4c0d7290f 100644 --- a/sys/arch/mips64/mips64/trap.c +++ b/sys/arch/mips64/mips64/trap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: trap.c,v 1.71 2010/11/24 21:01:03 miod Exp $ */ +/* $OpenBSD: trap.c,v 1.72 2010/11/24 21:16:28 miod Exp $ */ /* * Copyright (c) 1988 University of Utah. @@ -640,6 +640,32 @@ printf("SIG-BUSB @%p pc %p, ra %p\n", trapframe->badvaddr, trapframe->pc, trapfr i = SIGTRAP; break; #endif +#ifdef FPUEMUL + case BREAK_FPUEMUL_VAL: + /* + * If this is a genuine FP emulation break, + * resume execution to our branch destination. + */ + if ((p->p_md.md_flags & MDP_FPUSED) != 0 && + p->p_md.md_fppgva + 4 == (vaddr_t)va) { + struct vm_map *map = &p->p_vmspace->vm_map; + + p->p_md.md_flags &= ~MDP_FPUSED; + locr0->pc = p->p_md.md_fpbranchva; + + /* + * Prevent access to the relocation page. + * XXX needs to be fixed to work with rthreads + */ + uvm_fault_unwire(map, p->p_md.md_fppgva, + p->p_md.md_fppgva + PAGE_SIZE); + (void)uvm_map_protect(map, p->p_md.md_fppgva, + p->p_md.md_fppgva + PAGE_SIZE, + UVM_PROT_NONE, FALSE); + goto out; + } + /* FALLTHROUGH */ +#endif default: typ = TRAP_TRACE; i = SIGTRAP; @@ -730,8 +756,11 @@ printf("SIG-BUSB @%p pc %p, ra %p\n", trapframe->badvaddr, trapframe->pc, trapfr typ = ILL_ILLOPC; break; } - +#ifdef FPUEMUL + MipsFPTrap(trapframe); +#else enable_fpu(p); +#endif goto out; case T_FPE: @@ -773,6 +802,16 @@ printf("SIG-BUSB @%p pc %p, ra %p\n", trapframe->badvaddr, trapframe->pc, trapfr #endif panic("trap"); } +#ifdef FPUEMUL + /* + * If a relocated delay slot causes an exception, blame the + * original delay slot address - userland is not supposed to + * know anything about emulation bowels. + */ + if ((p->p_md.md_flags & MDP_FPUSED) != 0 && + trapframe->badvaddr == p->p_md.md_fppgva) + trapframe->badvaddr = p->p_md.md_fpslotva; +#endif p->p_md.md_regs->pc = trapframe->pc; p->p_md.md_regs->cause = trapframe->cause; p->p_md.md_regs->badvaddr = trapframe->badvaddr; @@ -1361,3 +1400,135 @@ fn_name(vaddr_t addr) #endif /* !DDB */ #endif /* DDB || DEBUG */ + +#ifdef FPUEMUL +/* + * Set up a successful branch emulation. + * The delay slot instruction is copied to a reserved page, followed by a + * trap instruction to get control back, and resume at the branch + * destination. + */ +int +fpe_branch_emulate(struct proc *p, struct trap_frame *tf, uint32_t insn, + vaddr_t dest) +{ + struct vm_map *map = &p->p_vmspace->vm_map; + InstFmt inst; + int rc; + + /* + * Check the delay slot instruction: since it will run as a + * non-delay slot instruction, we want to reject branch instructions + * (which behaviour, when in a delay slot, is undefined anyway). + */ + + inst = *(InstFmt *)&insn; + rc = 0; + switch ((int)inst.JType.op) { + case OP_SPECIAL: + switch ((int)inst.RType.func) { + case OP_JR: + case OP_JALR: + rc = EINVAL; + break; + } + break; + case OP_BCOND: + switch ((int)inst.IType.rt) { + case OP_BLTZ: + case OP_BLTZL: + case OP_BLTZAL: + case OP_BLTZALL: + case OP_BGEZ: + case OP_BGEZL: + case OP_BGEZAL: + case OP_BGEZALL: + rc = EINVAL; + break; + } + break; + case OP_J: + case OP_JAL: + case OP_BEQ: + case OP_BEQL: + case OP_BNE: + case OP_BNEL: + case OP_BLEZ: + case OP_BLEZL: + case OP_BGTZ: + case OP_BGTZL: + rc = EINVAL; + break; + case OP_COP1: + if (inst.RType.rs == OP_BC) /* oh the irony */ + rc = EINVAL; + break; + } + + if (rc != 0) { +#ifdef DEBUG + printf("%s: bogus delay slot insn %08x\n", __func__, insn); +#endif + return rc; + } + + /* + * Temporarily change protection over the page used to relocate + * the delay slot, and fault it in. + */ + + rc = uvm_map_protect(map, p->p_md.md_fppgva, + p->p_md.md_fppgva + PAGE_SIZE, UVM_PROT_RWX, FALSE); + if (rc != 0) { +#ifdef DEBUG + printf("%s: uvm_map_protect on %p failed: %d\n", + __func__, p->p_md.md_fppgva, rc); +#endif + return rc; + } + rc = uvm_fault_wire(map, p->p_md.md_fppgva, + p->p_md.md_fppgva + PAGE_SIZE, UVM_PROT_RWX); + if (rc != 0) { +#ifdef DEBUG + printf("%s: uvm_fault_wire on %p failed: %d\n", + __func__, p->p_md.md_fppgva, rc); +#endif + goto err2; + } + + rc = copyout(&insn, (void *)p->p_md.md_fppgva, sizeof insn); + if (rc != 0) { +#ifdef DEBUG + printf("%s: copyout %p failed %d\n", + __func__, p->p_md.md_fppgva, rc); +#endif + goto err; + } + insn = BREAK_FPUEMUL; + rc = copyout(&insn, (void *)(p->p_md.md_fppgva + 4), sizeof insn); + if (rc != 0) { +#ifdef DEBUG + printf("%s: copyout %p failed %d\n", + __func__, p->p_md.md_fppgva + 4, rc); +#endif + goto err; + } + + (void)uvm_map_protect(map, p->p_md.md_fppgva, + p->p_md.md_fppgva + PAGE_SIZE, UVM_PROT_RX, FALSE); + p->p_md.md_fpbranchva = dest; + p->p_md.md_fpslotva = (vaddr_t)tf->pc + 4; + p->p_md.md_flags |= MDP_FPUSED; + tf->pc = p->p_md.md_fppgva; + pmap_proc_iflush(p, tf->pc, 2 * 4); + + return 0; + +err: + uvm_fault_unwire(map, p->p_md.md_fppgva, p->p_md.md_fppgva + PAGE_SIZE); +err2: + (void)uvm_map_protect(map, p->p_md.md_fppgva, + p->p_md.md_fppgva + PAGE_SIZE, UVM_PROT_NONE, FALSE); + return rc; +} +#endif diff --git a/sys/arch/mips64/mips64/vm_machdep.c b/sys/arch/mips64/mips64/vm_machdep.c index eedf19fc2e2..7ed194dea94 100644 --- a/sys/arch/mips64/mips64/vm_machdep.c +++ b/sys/arch/mips64/mips64/vm_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vm_machdep.c,v 1.23 2010/02/13 14:04:45 miod Exp $ */ +/* $OpenBSD: vm_machdep.c,v 1.24 2010/11/24 21:16:28 miod Exp $ */ /* * Copyright (c) 1988 University of Utah. * Copyright (c) 1992, 1993 @@ -87,6 +87,10 @@ cpu_fork(p1, p2, stack, stacksize, func, arg) save_fpu(); p2->p_md.md_flags = p1->p_md.md_flags & MDP_FORKSAVE; +#ifdef FPUEMUL + p2->p_md.md_fppgva = p1->p_md.md_fppgva; + KASSERT((p2->p_md.md_flags & MDP_FPUSED) == 0); +#endif /* Copy pcb from p1 to p2 */ if (p1 == curproc) { |