|  | #include <trap.h> | 
|  | #include <smp.h> | 
|  | #include <umem.h> | 
|  | #include <arch/softfloat.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; | 
|  | } |