blob: 6c89015f8ae284a00dd95fbb7fe30fb3f148a52a [file] [log] [blame]
#include <arch/softfloat.h>
#include <smp.h>
#include <trap.h>
#include <umem.h>
static uint32_t ls(uint64_t *addr)
{
uint32_t r;
asm("fld f0, %1; mftx.s %0, f0" : "=r"(r) : "m"(*addr));
return r;
}
static void ss(uint64_t *addr, uint32_t val)
{
asm("mxtf.s f0, %0; fsd f0, %1" : : "r"(val), "m"(*addr));
}
static int emulate_fpu_silly(struct hw_trapframe *state,
ancillary_state_t *silly)
{
int insn;
if (memcpy_from_user(current, &insn, (void *)state->epc, 4)) {
state->cause = CAUSE_FAULT_FETCH;
handle_trap(state);
}
#define DECLARE_INSN(name, match, mask) bool is_##name = (insn & mask) == match;
#include <arch/opcodes.h>
#undef DECLARE_INSN
int rd = (insn >> 27) & 0x1f;
int rs1 = (insn >> 22) & 0x1f;
int rs2 = (insn >> 17) & 0x1f;
int rs3 = (insn >> 12) & 0x1f;
int imm = (insn << 10) >> 20;
int bimm = ((insn >> 10) & 0x7f) | ((insn & 0xf8000000) >> 20);
void *load_address = (void *)(state->gpr[rs1] + imm);
void *store_address = (void *)(state->gpr[rs1] + bimm);
softfloat_t sf;
sf.float_rounding_mode = silly->fsr >> 5;
sf.float_exception_flags = silly->fsr & 0x1f;
if (is_fsqrt_s)
ss(&silly->fpr[rd], float32_sqrt(&sf, ls(&silly->fpr[rs1])));
else if (is_fsqrt_d)
silly->fpr[rd] = float64_sqrt(&sf, silly->fpr[rs1]);
else if (is_fdiv_s)
ss(&silly->fpr[rd], float32_div(&sf, ls(&silly->fpr[rs1]),
ls(&silly->fpr[rs2])));
else if (is_fdiv_d)
silly->fpr[rd] =
float64_div(&sf, silly->fpr[rs1], silly->fpr[rs2]);
/* Eventually, we will emulate the full FPU, including the below insns
else if (is_mffsr)
{
// use sf instead of silly->fsr
state->gpr[rd] = silly->fsr;
}
else if (is_mtfsr)
{
// use sf instead of silly->fsr
int temp = silly->fsr;
silly->fsr = state->gpr[rs1] & 0xFF;
state->gpr[rd] = silly->fsr;
}
else if (is_fld)
{
uint64_t dest;
if (!memcpy_from_user(current, &dest, load_address,
sizeof(dest)))
{
state->cause = CAUSE_FAULT_LOAD;
state->badvaddr = (long)load_address;
handle_trap(state);
}
silly->fpr[rd] = dest;
}
else if (is_flw)
{
uint32_t dest;
if (!memcpy_from_user(current, &dest, load_address,
sizeof(dest)))
{
state->cause = CAUSE_FAULT_LOAD;
state->badvaddr = (long)load_address;
handle_trap(state);
}
silly->fpr[rd] = dest;
}
else if (is_fsd)
{
if (!memcpy_to_user(current, store_address, &silly->fpr[rs2],
sizeof(uint64_t)))
{
state->cause = CAUSE_FAULT_STORE;
state->badvaddr = (long)store_address;
handle_trap(state);
}
}
else if (is_flw)
{
if (!memcpy_to_user(current, store_address, &silly->fpr[rs2],
sizeof(uint32_t)))
{
state->cause = CAUSE_FAULT_STORE;
state->badvaddr = (long)store_address;
handle_trap(state);
}
}
*/
else
return 1;
silly->fsr = sf.float_rounding_mode << 5 | sf.float_exception_flags;
return 0;
}
/* For now we can only emulate missing compute insns, not the whole FPU */
int emulate_fpu(struct hw_trapframe *state)
{
if (!(state->sr & SR_EF)) {
state->cause = CAUSE_FP_DISABLED;
handle_trap(state);
}
ancillary_state_t fp_state;
save_fp_state(&fp_state);
int code = emulate_fpu_silly(state, &fp_state);
restore_fp_state(&fp_state);
return code;
}