summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/arch/mips64/include/_types.h3
-rw-r--r--sys/arch/mips64/include/cpu.h8
-rw-r--r--sys/arch/mips64/include/proc.h15
-rw-r--r--sys/arch/mips64/mips64/cpu.c6
-rw-r--r--sys/arch/mips64/mips64/fp_emulate.c506
-rw-r--r--sys/arch/mips64/mips64/mips64_machdep.c34
-rw-r--r--sys/arch/mips64/mips64/trap.c175
-rw-r--r--sys/arch/mips64/mips64/vm_machdep.c6
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) {