#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/signalvar.h>
#include <machine/cpu.h>
#include <mips64/mips_cpu.h>
#include <machine/fpu.h>
#include <machine/frame.h>
#include <machine/ieee.h>
#include <machine/ieeefp.h>
#include <machine/mips_opcode.h>
#include <machine/regnum.h>
#include <lib/libkern/softfloat.h>
#if defined(DEBUG) && defined(DDB)
#include <machine/db_machdep.h>
#endif
int fpu_emulate(struct proc *, struct trapframe *, uint32_t,
union sigval *);
int fpu_emulate_cop1(struct proc *, struct trapframe *, uint32_t);
int fpu_emulate_cop1x(struct proc *, struct trapframe *, uint32_t);
uint64_t
fpu_load(struct proc *, struct trapframe *, uint, uint);
void fpu_store(struct proc *, struct trapframe *, uint, uint, uint64_t);
#ifdef FPUEMUL
int nofpu_emulate_cop1(struct proc *, struct trapframe *, uint32_t,
union sigval *);
int nofpu_emulate_cop1x(struct proc *, struct trapframe *, uint32_t,
union sigval *);
int nofpu_emulate_loadstore(struct proc *, struct trapframe *, uint32_t,
union sigval *);
int nofpu_emulate_movci(struct trapframe *, uint32_t);
#endif
typedef int (fpu_fn3)(struct proc *, struct trapframe *, uint, uint, uint,
uint);
typedef int (fpu_fn4)(struct proc *, struct trapframe *, uint, uint, uint,
uint, uint);
fpu_fn3 fpu_abs;
fpu_fn3 fpu_add;
int fpu_c(struct proc *, struct trapframe *, uint, uint, uint, uint, uint);
fpu_fn3 fpu_ceil_l;
fpu_fn3 fpu_ceil_w;
fpu_fn3 fpu_cvt_d;
fpu_fn3 fpu_cvt_l;
fpu_fn3 fpu_cvt_s;
fpu_fn3 fpu_cvt_w;
fpu_fn3 fpu_div;
fpu_fn3 fpu_floor_l;
fpu_fn3 fpu_floor_w;
int fpu_int_l(struct proc *, struct trapframe *, uint, uint, uint, uint,
uint);
int fpu_int_w(struct proc *, struct trapframe *, uint, uint, uint, uint,
uint);
fpu_fn4 fpu_madd;
fpu_fn4 fpu_msub;
fpu_fn3 fpu_mov;
fpu_fn3 fpu_movcf;
fpu_fn3 fpu_movn;
fpu_fn3 fpu_movz;
fpu_fn3 fpu_mul;
fpu_fn3 fpu_neg;
fpu_fn4 fpu_nmadd;
fpu_fn4 fpu_nmsub;
fpu_fn3 fpu_recip;
fpu_fn3 fpu_round_l;
fpu_fn3 fpu_round_w;
fpu_fn3 fpu_rsqrt;
fpu_fn3 fpu_sqrt;
fpu_fn3 fpu_sub;
fpu_fn3 fpu_trunc_l;
fpu_fn3 fpu_trunc_w;
#define FMT_S 0x00
#define FMT_D 0x01
#define FMT_W 0x04
#define FMT_L 0x05
#define float32_is_nan(a) \
(0xff000000 < (a << 1))
#define float32_is_signaling_nan(a) \
((((a >> 22) & 0x1ff) == 0x1fe) && (a & 0x003fffff))
#define ONE_F32 (float32)(SNG_EXP_BIAS << SNG_FRACBITS)
#define ONE_F64 (float64)((uint64_t)DBL_EXP_BIAS << DBL_FRACBITS)
static inline uint32_t
getfsr(void)
{
uint32_t fsr;
__asm__ volatile (
" .set push\n"
" .set hardfloat\n"
" cfc1 %0, $31\n"
" cfc1 %0, $31\n"
" .set pop\n"
: "=r" (fsr));
return fsr;
}
static inline void
setfsr(uint32_t fsr)
{
__asm__ volatile (
" .set push\n"
" .set hardfloat\n"
" ctc1 %0, $31\n"
" .set pop\n"
: : "r" (fsr));
}
void
MipsFPTrap(struct trapframe *tf)
{
struct cpu_info *ci = curcpu();
struct proc *p = ci->ci_curproc;
union sigval sv;
vaddr_t pc;
register_t sr;
uint32_t fsr, excbits;
uint32_t branch = 0;
uint32_t insn;
InstFmt inst;
int sig = 0;
int fault_type = SI_NOINFO;
int update_pcb = 0;
int emulate = 0;
int skip_insn = 1;
KDASSERT(tf == p->p_md.md_regs);
pc = (vaddr_t)tf->pc;
if (tf->cause & CR_BR_DELAY)
pc += 4;
if (CPU_HAS_FPU(ci)) {
sr = getsr();
setsr(sr | SR_COP_1_BIT);
fsr = getfsr();
if ((fsr & FPCSR_C_E) == 0) {
sig = SIGFPE;
goto deliver;
}
} else {
#ifdef CPU_OCTEON
tf->sr |= SR_FR_32;
#endif
fsr = tf->fsr;
}
if (copyinsn(p, pc, &insn) != 0) {
sig = SIGBUS;
fault_type = BUS_OBJERR;
sv.sival_ptr = (void *)pc;
goto deliver;
}
inst = *(InstFmt *)&insn;
if (tf->cause & CR_BR_DELAY) {
if (copyinsn(p, tf->pc, &branch) != 0) {
sig = SIGBUS;
fault_type = BUS_OBJERR;
sv.sival_ptr = (void *)tf->pc;
goto deliver;
}
}
#ifdef DEBUG
#ifdef DDB
printf("%s: unimplemented FPU completion, fsr 0x%08x\n0x%lx: ",
p->p_p->ps_comm, fsr, pc);
dbmd_print_insn(insn, pc, printf);
#else
printf("%s: unimplemented FPU completion, insn 0x%08x fsr 0x%08x\n",
p->p_p->ps_comm, insn, fsr);
#endif
#endif
switch (inst.FRType.op) {
default:
break;
#ifdef FPUEMUL
case OP_SPECIAL:
switch (inst.FRType.func) {
default:
break;
case OP_MOVCI:
if (!CPU_HAS_FPU(ci))
emulate = 1;
break;
}
break;
case OP_LDC1:
case OP_LWC1:
case OP_SDC1:
case OP_SWC1:
if (!CPU_HAS_FPU(ci))
emulate = 1;
break;
#endif
case OP_COP1:
switch (inst.RType.rs) {
case OP_BC:
skip_insn = 0;
case OP_MF:
case OP_DMF:
case OP_CF:
case OP_MT:
case OP_DMT:
case OP_CT:
if (!CPU_HAS_FPU(ci))
emulate = 1;
break;
default:
emulate = 1;
break;
}
break;
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:
if (!CPU_HAS_FPU(ci))
emulate = 1;
break;
#endif
default:
break;
}
break;
case OP_MADD:
case OP_MSUB:
case OP_NMADD:
case OP_NMSUB:
emulate = 1;
break;
}
break;
}
if (emulate) {
if (CPU_HAS_FPU(ci)) {
KASSERT(p == ci->ci_fpuproc);
save_fpu();
}
update_pcb = 1;
sig = fpu_emulate(p, tf, insn, &sv);
fsr = tf->fsr;
if (sig == 0) {
excbits = (fsr & FPCSR_C_MASK) >> FPCSR_C_SHIFT;
excbits &= (fsr & FPCSR_E_MASK) >> FPCSR_E_SHIFT;
if (excbits != 0)
sig = SIGFPE;
}
} else {
sig = SIGILL;
fault_type = ILL_ILLOPC;
}
deliver:
switch (sig) {
case SIGFPE:
excbits = (fsr & FPCSR_C_MASK) >> FPCSR_C_SHIFT;
excbits &= (fsr & FPCSR_E_MASK) >> FPCSR_E_SHIFT;
if (excbits & FP_X_INV)
fault_type = FPE_FLTINV;
else if (excbits & FP_X_DZ)
fault_type = FPE_INTDIV;
else if (excbits & FP_X_OFL)
fault_type = FPE_FLTUND;
else if (excbits & FP_X_UFL)
fault_type = FPE_FLTOVF;
else
fault_type = FPE_FLTRES;
break;
#ifdef FPUEMUL
case SIGBUS:
if (fault_type == SI_NOINFO)
fault_type = BUS_ADRALN;
break;
case SIGSEGV:
if (fault_type == SI_NOINFO)
fault_type = SEGV_MAPERR;
break;
#endif
}
if (CPU_HAS_FPU(ci) || skip_insn) {
if (sig != SIGILL) {
if (tf->cause & CR_BR_DELAY) {
tf->pc = MipsEmulateBranch(tf, tf->pc, fsr,
branch);
} else
tf->pc += 4;
}
}
fsr &= ~FPCSR_C_E;
excbits = (fsr & FPCSR_C_MASK) >> FPCSR_C_SHIFT;
fsr |= excbits << FPCSR_F_SHIFT;
fsr &= ~FPCSR_C_MASK;
if (update_pcb)
tf->fsr = fsr;
if (CPU_HAS_FPU(ci)) {
setfsr(fsr);
setsr(sr);
}
if (sig != 0) {
if (sig != SIGBUS && sig != SIGSEGV)
sv.sival_ptr = (void *)pc;
trapsignal(p, sig, 0, fault_type, sv);
}
}
int
fpu_emulate(struct proc *p, struct trapframe *tf, uint32_t insn,
union sigval *sv)
{
InstFmt inst;
tf->zero = 0;
inst = *(InstFmt *)&insn;
if (CPU_HAS_FPU(p->p_cpu)) {
switch (inst.FRType.op) {
default:
break;
case OP_COP1:
return fpu_emulate_cop1(p, tf, insn);
case OP_COP1X:
return fpu_emulate_cop1x(p, tf, insn);
}
return SIGILL;
}
#ifdef FPUEMUL
switch (inst.FRType.op) {
default:
break;
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(p, tf, insn, sv);
case OP_COP1:
switch (inst.RType.rs) {
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(p, tf, insn, sv);
default:
return fpu_emulate_cop1(p, tf, insn);
}
break;
case OP_COP1X:
switch (inst.FQType.op4) {
default:
switch (inst.FRType.func) {
case OP_LDXC1:
case OP_LWXC1:
case OP_SDXC1:
case OP_SWXC1:
case OP_PREFX:
return nofpu_emulate_cop1x(p, tf, insn, sv);
default:
break;
}
break;
case OP_MADD:
case OP_MSUB:
case OP_NMADD:
case OP_NMSUB:
return fpu_emulate_cop1x(p, tf, insn);
}
}
#endif
return SIGILL;
}
int
fpu_emulate_cop1(struct proc *p, struct trapframe *tf, uint32_t insn)
{
InstFmt inst;
uint ft, fs, fd;
fpu_fn3 *fpu_op;
static fpu_fn3 *const fpu_ops1[1 << 6] = {
fpu_add,
fpu_sub,
fpu_mul,
fpu_div,
fpu_sqrt,
fpu_abs,
fpu_mov,
fpu_neg,
fpu_round_l,
fpu_trunc_l,
fpu_ceil_l,
fpu_floor_l,
fpu_round_w,
fpu_trunc_w,
fpu_ceil_w,
fpu_floor_w,
NULL,
fpu_movcf,
fpu_movz,
fpu_movn,
NULL,
fpu_recip,
fpu_rsqrt,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
fpu_cvt_s,
fpu_cvt_d,
NULL,
NULL,
fpu_cvt_w,
fpu_cvt_l,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
(fpu_fn3 *)fpu_c,
(fpu_fn3 *)fpu_c,
(fpu_fn3 *)fpu_c,
(fpu_fn3 *)fpu_c,
(fpu_fn3 *)fpu_c,
(fpu_fn3 *)fpu_c,
(fpu_fn3 *)fpu_c,
(fpu_fn3 *)fpu_c,
(fpu_fn3 *)fpu_c,
(fpu_fn3 *)fpu_c,
(fpu_fn3 *)fpu_c,
(fpu_fn3 *)fpu_c,
(fpu_fn3 *)fpu_c,
(fpu_fn3 *)fpu_c,
(fpu_fn3 *)fpu_c,
(fpu_fn3 *)fpu_c
};
inst = *(InstFmt *)&insn;
fpu_op = fpu_ops1[inst.FRType.func];
if (fpu_op == NULL)
return SIGILL;
if ((insn & (1 << 25)) == 0)
return SIGILL;
switch (inst.FRType.fmt) {
default:
return SIGILL;
case FMT_S:
case FMT_D:
case FMT_W:
case FMT_L:
break;
}
ft = inst.FRType.ft;
fs = inst.FRType.fs;
fd = inst.FRType.fd;
if ((tf->sr & SR_FR_32) == 0) {
if ((ft | fs | fd) & 1)
return SIGILL;
}
if (fpu_op == (fpu_fn3 *)&fpu_c)
return
fpu_c(p, tf, inst.FRType.fmt, ft, fs, fd, inst.FRType.func);
else
return (*fpu_op)(p, tf, inst.FRType.fmt, ft, fs, fd);
}
int
fpu_emulate_cop1x(struct proc *p, struct trapframe *tf, uint32_t insn)
{
InstFmt inst;
uint fr, ft, fs, fd;
fpu_fn4 *fpu_op;
static fpu_fn4 *const fpu_ops1x[1 << 3] = {
NULL,
NULL,
NULL,
NULL,
fpu_madd,
fpu_msub,
fpu_nmadd,
fpu_nmsub
};
inst = *(InstFmt *)&insn;
fpu_op = fpu_ops1x[inst.FQType.op4];
if (fpu_op == NULL)
return SIGILL;
switch (inst.FQType.fmt3) {
default:
return SIGILL;
case FMT_S:
case FMT_D:
case FMT_W:
case FMT_L:
break;
}
fr = inst.FQType.fr;
ft = inst.FQType.ft;
fs = inst.FQType.fs;
fd = inst.FQType.fd;
if ((tf->sr & SR_FR_32) == 0) {
if ((fr | ft | fs | fd) & 1)
return SIGILL;
}
return (*fpu_op)(p, tf, inst.FRType.fmt, fr, ft, fs, fd);
}
uint64_t
fpu_load(struct proc *p, struct trapframe *tf, uint fmt, uint regno)
{
uint64_t tmp, tmp2;
tmp = ((uint64_t *)p->p_md.md_regs)[FPBASE + regno];
if (tf->sr & SR_FR_32) {
switch (fmt) {
case FMT_D:
case FMT_L:
break;
case FMT_S:
case FMT_W:
tmp &= 0xffffffff;
break;
}
} else {
tmp &= 0xffffffff;
switch (fmt) {
case FMT_D:
case FMT_L:
tmp2 =
((uint64_t *)p->p_md.md_regs)[FPBASE + regno + 1];
tmp |= tmp2 << 32;
break;
case FMT_S:
case FMT_W:
break;
}
}
return tmp;
}
void
fpu_store(struct proc *p, struct trapframe *tf, uint fmt, uint regno,
uint64_t rslt)
{
if (tf->sr & SR_FR_32) {
((uint64_t *)p->p_md.md_regs)[FPBASE + regno] = rslt;
} else {
((uint64_t *)p->p_md.md_regs)[FPBASE + regno] =
rslt & 0xffffffff;
((uint64_t *)p->p_md.md_regs)[FPBASE + regno + 1] =
(rslt >> 32) & 0xffffffff;
}
}
int
fpu_int_l(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd, uint rm)
{
uint64_t raw;
uint32_t oldrm;
if (ft != 0)
return SIGILL;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
raw = fpu_load(p, tf, fmt, fs);
oldrm = tf->fsr & FPCSR_RM_MASK;
tf->fsr = (tf->fsr & ~FPCSR_RM_MASK) | rm;
if (fmt == FMT_S)
raw = float32_to_int64((float32)raw);
else
raw = float64_to_int64((float64)raw);
tf->fsr = (tf->fsr & ~FPCSR_RM_MASK) | oldrm;
if ((tf->fsr & (FPCSR_C_V | FPCSR_E_V)) != (FPCSR_C_V | FPCSR_E_V))
fpu_store(p, tf, fmt, fd, raw);
return 0;
}
int
fpu_int_w(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd, uint rm)
{
uint64_t raw;
uint32_t oldrm;
if (ft != 0)
return SIGILL;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
raw = fpu_load(p, tf, fmt, fs);
oldrm = tf->fsr & FPCSR_RM_MASK;
tf->fsr = (tf->fsr & ~FPCSR_RM_MASK) | rm;
if (fmt == FMT_S)
raw = float32_to_int32((float32)raw);
else
raw = float64_to_int32((float64)raw);
tf->fsr = (tf->fsr & ~FPCSR_RM_MASK) | oldrm;
if ((tf->fsr & (FPCSR_C_V | FPCSR_E_V)) != (FPCSR_C_V | FPCSR_E_V))
fpu_store(p, tf, fmt, fd, raw);
return 0;
}
int
fpu_abs(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
uint64_t raw;
if (ft != 0)
return SIGILL;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
raw = fpu_load(p, tf, fmt, fs);
if (fmt == FMT_S) {
float32 f32 = (float32)raw;
if (float32_is_nan(f32)) {
float_set_invalid();
} else {
f32 &= ~(1L << 31);
raw = (uint64_t)f32;
}
} else {
float64 f64 = (float64)raw;
if (float64_is_nan(f64)) {
float_set_invalid();
} else {
f64 &= ~(1L << 63);
raw = (uint64_t)f64;
}
}
fpu_store(p, tf, fmt, fd, raw);
return 0;
}
int
fpu_add(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
uint64_t raw1, raw2, rslt;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
raw1 = fpu_load(p, tf, fmt, fs);
raw2 = fpu_load(p, tf, fmt, ft);
if (fmt == FMT_S) {
float32 f32 = float32_add((float32)raw1, (float32)raw2);
rslt = (uint64_t)f32;
} else {
float64 f64 = float64_add((float64)raw1, (float64)raw2);
rslt = (uint64_t)f64;
}
fpu_store(p, tf, fmt, fd, rslt);
return 0;
}
int
fpu_c(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd, uint op)
{
uint64_t raw1, raw2;
uint cc, lt, eq, uo;
if ((fd & 0x03) != 0)
return SIGILL;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
lt = eq = uo = 0;
cc = fd >> 2;
raw1 = fpu_load(p, tf, fmt, fs);
raw2 = fpu_load(p, tf, fmt, ft);
if (fmt == FMT_S) {
float32 f32a = (float32)raw1;
float32 f32b = (float32)raw2;
if (float32_is_nan(f32a)) {
uo = 1 << 0;
if (float32_is_signaling_nan(f32a))
op |= 0x08;
}
if (float32_is_nan(f32b)) {
uo = 1 << 0;
if (float32_is_signaling_nan(f32b))
op |= 0x08;
}
if (uo == 0) {
if (float32_eq(f32a, f32b))
eq = 1 << 1;
else if (float32_lt(f32a, f32b))
lt = 1 << 2;
}
} else {
float64 f64a = (float64)raw1;
float64 f64b = (float64)raw2;
if (float64_is_nan(f64a)) {
uo = 1 << 0;
if (float64_is_signaling_nan(f64a))
op |= 0x08;
}
if (float64_is_nan(f64b)) {
uo = 1 << 0;
if (float64_is_signaling_nan(f64b))
op |= 0x08;
}
if (uo == 0) {
if (float64_eq(f64a, f64b))
eq = 1 << 1;
else if (float64_lt(f64a, f64b))
lt = 1 << 2;
}
}
if (uo && (op & 0x08)) {
float_set_invalid();
if (tf->fsr & FPCSR_E_V) {
goto skip;
}
}
if ((uo | eq | lt) & op)
tf->fsr |= FPCSR_CONDVAL(cc);
else
tf->fsr &= ~FPCSR_CONDVAL(cc);
skip:
return 0;
}
int
fpu_ceil_l(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
return fpu_int_l(p, tf, fmt, ft, fs, fd, FP_RP);
}
int
fpu_ceil_w(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
return fpu_int_w(p, tf, fmt, ft, fs, fd, FP_RP);
}
int
fpu_cvt_d(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
uint64_t raw;
if (ft != 0)
return SIGILL;
if (fmt == FMT_D)
return SIGILL;
raw = fpu_load(p, tf, fmt, fs);
switch (fmt) {
case FMT_L:
raw = int64_to_float64((int64_t)raw);
break;
case FMT_S:
raw = float32_to_float64((float32)raw);
break;
case FMT_W:
raw = int32_to_float64((int32_t)raw);
break;
}
fpu_store(p, tf, fmt, fd, raw);
return 0;
}
int
fpu_cvt_l(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
uint64_t raw;
uint32_t rm;
if (ft != 0)
return SIGILL;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
rm = tf->fsr & FPCSR_RM_MASK;
raw = fpu_load(p, tf, fmt, fs);
if (fmt == FMT_D) {
if (rm == FP_RZ)
raw = float64_to_int64_round_to_zero((float64)raw);
else
raw = float64_to_int64((float64)raw);
} else {
if (rm == FP_RZ)
raw = float32_to_int64_round_to_zero((float32)raw);
else
raw = float32_to_int64((float32)raw);
}
if ((tf->fsr & (FPCSR_C_V | FPCSR_E_V)) != (FPCSR_C_V | FPCSR_E_V))
fpu_store(p, tf, fmt, fd, raw);
return 0;
}
int
fpu_cvt_s(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
uint64_t raw;
if (ft != 0)
return SIGILL;
if (fmt == FMT_S)
return SIGILL;
raw = fpu_load(p, tf, fmt, fs);
switch (fmt) {
case FMT_D:
raw = float64_to_float32((float64)raw);
break;
case FMT_L:
raw = int64_to_float32((int64_t)raw);
break;
case FMT_W:
raw = int32_to_float32((int32_t)raw);
break;
}
fpu_store(p, tf, fmt, fd, raw);
return 0;
}
int
fpu_cvt_w(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
uint64_t raw;
uint32_t rm;
if (ft != 0)
return SIGILL;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
rm = tf->fsr & FPCSR_RM_MASK;
raw = fpu_load(p, tf, fmt, fs);
if (fmt == FMT_D) {
if (rm == FP_RZ)
raw = float64_to_int32_round_to_zero((float64)raw);
else
raw = float64_to_int32((float64)raw);
} else {
if (rm == FP_RZ)
raw = float32_to_int32_round_to_zero((float32)raw);
else
raw = float32_to_int32((float32)raw);
}
if ((tf->fsr & (FPCSR_C_V | FPCSR_E_V)) != (FPCSR_C_V | FPCSR_E_V))
fpu_store(p, tf, fmt, fd, raw);
return 0;
}
int
fpu_div(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
uint64_t raw1, raw2, rslt;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
raw1 = fpu_load(p, tf, fmt, fs);
raw2 = fpu_load(p, tf, fmt, ft);
if (fmt == FMT_S) {
float32 f32 = float32_div((float32)raw1, (float32)raw2);
rslt = (uint64_t)f32;
} else {
float64 f64 = float64_div((float64)raw1, (float64)raw2);
rslt = (uint64_t)f64;
}
fpu_store(p, tf, fmt, fd, rslt);
return 0;
}
int
fpu_floor_l(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
return fpu_int_l(p, tf, fmt, ft, fs, fd, FP_RM);
}
int
fpu_floor_w(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
return fpu_int_w(p, tf, fmt, ft, fs, fd, FP_RM);
}
int
fpu_madd(struct proc *p, struct trapframe *tf, uint fmt, uint fr, uint ft,
uint fs, uint fd)
{
uint64_t raw1, raw2, raw3, rslt;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
raw1 = fpu_load(p, tf, fmt, fs);
raw2 = fpu_load(p, tf, fmt, ft);
raw3 = fpu_load(p, tf, fmt, fr);
if (fmt == FMT_S) {
float32 f32 = float32_add(
float32_mul((float32)raw1, (float32)raw2),
(float32)raw3);
rslt = (uint64_t)f32;
} else {
float64 f64 = float64_add(
float64_mul((float64)raw1, (float64)raw2),
(float64)raw3);
rslt = (uint64_t)f64;
}
fpu_store(p, tf, fmt, fd, rslt);
return 0;
}
int
fpu_mov(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
uint64_t raw;
if (ft != 0)
return SIGILL;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
raw = fpu_load(p, tf, fmt, fs);
fpu_store(p, tf, fmt, fd, raw);
return 0;
}
int
fpu_movcf(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
uint64_t raw;
uint cc, istf;
int condition;
if ((ft & 0x02) != 0)
return SIGILL;
cc = ft >> 2;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
condition = tf->fsr & FPCSR_CONDVAL(cc);
istf = ft & COPz_BC_TF_MASK;
if ((!condition && !istf) || (condition && istf) ) {
raw = fpu_load(p, tf, fmt, fs);
fpu_store(p, tf, fmt, fd, raw);
}
return 0;
}
int
fpu_movn(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
register_t *regs = (register_t *)tf;
uint64_t raw;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
if (ft != ZERO && regs[ft] != 0) {
raw = fpu_load(p, tf, fmt, fs);
fpu_store(p, tf, fmt, fd, raw);
}
return 0;
}
int
fpu_movz(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
register_t *regs = (register_t *)tf;
uint64_t raw;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
if (ft == ZERO || regs[ft] == 0) {
raw = fpu_load(p, tf, fmt, fs);
fpu_store(p, tf, fmt, fd, raw);
}
return 0;
}
int
fpu_msub(struct proc *p, struct trapframe *tf, uint fmt, uint fr, uint ft,
uint fs, uint fd)
{
uint64_t raw1, raw2, raw3, rslt;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
raw1 = fpu_load(p, tf, fmt, fs);
raw2 = fpu_load(p, tf, fmt, ft);
raw3 = fpu_load(p, tf, fmt, fr);
if (fmt == FMT_S) {
float32 f32 = float32_sub(
float32_mul((float32)raw1, (float32)raw2),
(float32)raw3);
rslt = (uint64_t)f32;
} else {
float64 f64 = float64_sub(
float64_mul((float64)raw1, (float64)raw2),
(float64)raw3);
rslt = (uint64_t)f64;
}
fpu_store(p, tf, fmt, fd, rslt);
return 0;
}
int
fpu_mul(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
uint64_t raw1, raw2, rslt;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
raw1 = fpu_load(p, tf, fmt, fs);
raw2 = fpu_load(p, tf, fmt, ft);
if (fmt == FMT_S) {
float32 f32 = float32_mul((float32)raw1, (float32)raw2);
rslt = (uint64_t)f32;
} else {
float64 f64 = float64_mul((float64)raw1, (float64)raw2);
rslt = (uint64_t)f64;
}
fpu_store(p, tf, fmt, fd, rslt);
return 0;
}
int
fpu_neg(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
uint64_t raw;
if (ft != 0)
return SIGILL;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
raw = fpu_load(p, tf, fmt, fs);
if (fmt == FMT_S) {
float32 f32 = (float32)raw;
if (float32_is_nan(f32)) {
float_set_invalid();
} else {
f32 ^= 1L << 31;
raw = (uint64_t)f32;
}
} else {
float64 f64 = (float64)raw;
if (float64_is_nan(f64)) {
float_set_invalid();
} else {
f64 ^= 1L << 63;
raw = (uint64_t)f64;
}
}
fpu_store(p, tf, fmt, fd, raw);
return 0;
}
int
fpu_nmadd(struct proc *p, struct trapframe *tf, uint fmt, uint fr, uint ft,
uint fs, uint fd)
{
uint64_t raw1, raw2, raw3, rslt;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
raw1 = fpu_load(p, tf, fmt, fs);
raw2 = fpu_load(p, tf, fmt, ft);
raw3 = fpu_load(p, tf, fmt, fr);
if (fmt == FMT_S) {
float32 f32 = float32_add(
float32_mul((float32)raw1, (float32)raw2),
(float32)raw3);
if (float32_is_nan(f32))
float_set_invalid();
else
f32 ^= 1L << 31;
rslt = (uint64_t)f32;
} else {
float64 f64 = float64_add(
float64_mul((float64)raw1, (float64)raw2),
(float64)raw3);
if (float64_is_nan(f64))
float_set_invalid();
else
f64 ^= 1L << 63;
rslt = (uint64_t)f64;
}
fpu_store(p, tf, fmt, fd, rslt);
return 0;
}
int
fpu_nmsub(struct proc *p, struct trapframe *tf, uint fmt, uint fr, uint ft,
uint fs, uint fd)
{
uint64_t raw1, raw2, raw3, rslt;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
raw1 = fpu_load(p, tf, fmt, fs);
raw2 = fpu_load(p, tf, fmt, ft);
raw3 = fpu_load(p, tf, fmt, fr);
if (fmt == FMT_S) {
float32 f32 = float32_sub(
float32_mul((float32)raw1, (float32)raw2),
(float32)raw3);
if (float32_is_nan(f32))
float_set_invalid();
else
f32 ^= 1L << 31;
rslt = (uint64_t)f32;
} else {
float64 f64 = float64_sub(
float64_mul((float64)raw1, (float64)raw2),
(float64)raw3);
if (float64_is_nan(f64))
float_set_invalid();
else
f64 ^= 1L << 63;
rslt = (uint64_t)f64;
}
fpu_store(p, tf, fmt, fd, rslt);
return 0;
}
int
fpu_recip(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
uint64_t raw;
if (ft != 0)
return SIGILL;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
raw = fpu_load(p, tf, fmt, fs);
if (fmt == FMT_S) {
float32 f32 = float32_div(ONE_F32, (float32)raw);
raw = (uint64_t)f32;
} else {
float64 f64 = float64_div(ONE_F64, (float64)raw);
raw = (uint64_t)f64;
}
fpu_store(p, tf, fmt, fd, raw);
return 0;
}
int
fpu_round_l(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
return fpu_int_l(p, tf, fmt, ft, fs, fd, FP_RN);
}
int
fpu_round_w(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
return fpu_int_w(p, tf, fmt, ft, fs, fd, FP_RN);
}
int
fpu_rsqrt(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
uint64_t raw;
if (ft != 0)
return SIGILL;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
raw = fpu_load(p, tf, fmt, fs);
if (fmt == FMT_S) {
float32 f32 = float32_sqrt((float32)raw);
if ((tf->fsr & (FPCSR_C_V | FPCSR_E_V)) !=
(FPCSR_C_V | FPCSR_E_V))
f32 = float32_div(ONE_F32, f32);
raw = (uint64_t)f32;
} else {
float64 f64 = float64_sqrt((float64)raw);
if ((tf->fsr & (FPCSR_C_V | FPCSR_E_V)) !=
(FPCSR_C_V | FPCSR_E_V))
f64 = float64_div(ONE_F64, f64);
raw = (uint64_t)f64;
}
fpu_store(p, tf, fmt, fd, raw);
return 0;
}
int
fpu_sqrt(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
uint64_t raw;
if (ft != 0)
return SIGILL;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
raw = fpu_load(p, tf, fmt, fs);
if (fmt == FMT_S) {
float32 f32 = float32_sqrt((float32)raw);
raw = (uint64_t)f32;
} else {
float64 f64 = float64_sqrt((float64)raw);
raw = (uint64_t)f64;
}
fpu_store(p, tf, fmt, fd, raw);
return 0;
}
int
fpu_sub(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
uint64_t raw1, raw2, rslt;
if (fmt != FMT_S && fmt != FMT_D)
return SIGILL;
raw1 = fpu_load(p, tf, fmt, fs);
raw2 = fpu_load(p, tf, fmt, ft);
if (fmt == FMT_S) {
float32 f32 = float32_sub((float32)raw1, (float32)raw2);
rslt = (uint64_t)f32;
} else {
float64 f64 = float64_sub((float64)raw1, (float64)raw2);
rslt = (uint64_t)f64;
}
fpu_store(p, tf, fmt, fd, rslt);
return 0;
}
int
fpu_trunc_l(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
return fpu_int_l(p, tf, fmt, ft, fs, fd, FP_RZ);
}
int
fpu_trunc_w(struct proc *p, struct trapframe *tf, uint fmt, uint ft, uint fs,
uint fd)
{
return fpu_int_w(p, tf, fmt, ft, fs, fd, FP_RZ);
}
#ifdef FPUEMUL
int
nofpu_emulate_cop1(struct proc *p, struct trapframe *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)
((uint64_t *)p->p_md.md_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(p, 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:
cval = MIPS_SOFT << 8;
break;
case 31:
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;
((uint64_t *)p->p_md.md_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(p, 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:
cval &= ~FPCSR_C_E;
tf->fsr = cval;
break;
case 0:
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) ||
(condition && istf) ) {
dest = tf->pc + 4 + ((short)inst.IType.imm << 2);
if (copyinsn(p, tf->pc + 4, &dinsn) != 0) {
sv->sival_ptr = (void *)(tf->pc + 4);
return SIGSEGV;
}
if (dinsn == 0x00000000 ||
dinsn == 0x00000040 ) {
tf->pc = dest;
} else {
if (fpe_branch_emulate(curproc, tf, dinsn,
dest) != 0)
return SIGILL;
}
} else {
tf->pc += 4;
if (nd)
tf->pc += 4;
}
}
break;
}
return 0;
}
int
nofpu_emulate_cop1x(struct proc *p, struct trapframe *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(p, 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;
}
if (copyin((const void *)va, &wdata, sizeof wdata) != 0) {
sv->sival_ptr = (void *)va;
return SIGSEGV;
}
((uint64_t *)p->p_md.md_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(p, tf, FMT_L, inst.FQType.fs);
else {
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;
}
wdata = ((uint64_t *)p->p_md.md_regs)[FPBASE + inst.FQType.fs];
if (copyout(&wdata, (void *)va, sizeof wdata) != 0) {
sv->sival_ptr = (void *)va;
return SIGSEGV;
}
break;
case OP_PREFX:
break;
}
return 0;
}
int
nofpu_emulate_loadstore(struct proc *p, struct trapframe *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(p, 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;
}
if (copyin((const void *)va, &wdata, sizeof wdata) != 0) {
sv->sival_ptr = (void *)va;
return SIGSEGV;
}
((uint64_t *)p->p_md.md_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(p, tf, FMT_L, inst.IType.rt);
else {
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;
}
wdata = ((uint64_t *)p->p_md.md_regs)[FPBASE + inst.IType.rt];
if (copyout(&wdata, (void *)va, sizeof wdata) != 0) {
sv->sival_ptr = (void *)va;
return SIGSEGV;
}
break;
}
return 0;
}
int
nofpu_emulate_movci(struct trapframe *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) || (condition && istf) ) {
if (inst.RType.rd != ZERO)
regs[inst.RType.rd] = regs[inst.RType.rs];
}
return 0;
}
#endif