/* * MIPS emulation helpers for qemu. * * Copyright (c) 2004-2005 Jocelyn Mayer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * */ #include "qemu/osdep.h" #include "cpu.h" #include "internal.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "exec/memop.h" #include "fpu_helper.h" /* 64 bits arithmetic for 32 bits hosts */ static inline uint64_t get_HILO(CPUMIPSState *env) { return ((uint64_t)(env->active_tc.HI[0]) << 32) | (uint32_t)env->active_tc.LO[0]; } static inline target_ulong set_HIT0_LO(CPUMIPSState *env, uint64_t HILO) { env->active_tc.LO[0] = (int32_t)(HILO & 0xFFFFFFFF); return env->active_tc.HI[0] = (int32_t)(HILO >> 32); } static inline target_ulong set_HI_LOT0(CPUMIPSState *env, uint64_t HILO) { target_ulong tmp = env->active_tc.LO[0] = (int32_t)(HILO & 0xFFFFFFFF); env->active_tc.HI[0] = (int32_t)(HILO >> 32); return tmp; } /* Multiplication variants of the vr54xx. */ target_ulong helper_muls(CPUMIPSState *env, target_ulong arg1, target_ulong arg2) { return set_HI_LOT0(env, 0 - ((int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2)); } target_ulong helper_mulsu(CPUMIPSState *env, target_ulong arg1, target_ulong arg2) { return set_HI_LOT0(env, 0 - (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); } target_ulong helper_macc(CPUMIPSState *env, target_ulong arg1, target_ulong arg2) { return set_HI_LOT0(env, (int64_t)get_HILO(env) + (int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2); } target_ulong helper_macchi(CPUMIPSState *env, target_ulong arg1, target_ulong arg2) { return set_HIT0_LO(env, (int64_t)get_HILO(env) + (int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2); } target_ulong helper_maccu(CPUMIPSState *env, target_ulong arg1, target_ulong arg2) { return set_HI_LOT0(env, (uint64_t)get_HILO(env) + (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); } target_ulong helper_macchiu(CPUMIPSState *env, target_ulong arg1, target_ulong arg2) { return set_HIT0_LO(env, (uint64_t)get_HILO(env) + (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); } target_ulong helper_msac(CPUMIPSState *env, target_ulong arg1, target_ulong arg2) { return set_HI_LOT0(env, (int64_t)get_HILO(env) - (int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2); } target_ulong helper_msachi(CPUMIPSState *env, target_ulong arg1, target_ulong arg2) { return set_HIT0_LO(env, (int64_t)get_HILO(env) - (int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2); } target_ulong helper_msacu(CPUMIPSState *env, target_ulong arg1, target_ulong arg2) { return set_HI_LOT0(env, (uint64_t)get_HILO(env) - (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); } target_ulong helper_msachiu(CPUMIPSState *env, target_ulong arg1, target_ulong arg2) { return set_HIT0_LO(env, (uint64_t)get_HILO(env) - (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); } target_ulong helper_mulhi(CPUMIPSState *env, target_ulong arg1, target_ulong arg2) { return set_HIT0_LO(env, (int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2); } target_ulong helper_mulhiu(CPUMIPSState *env, target_ulong arg1, target_ulong arg2) { return set_HIT0_LO(env, (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); } target_ulong helper_mulshi(CPUMIPSState *env, target_ulong arg1, target_ulong arg2) { return set_HIT0_LO(env, 0 - (int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2); } target_ulong helper_mulshiu(CPUMIPSState *env, target_ulong arg1, target_ulong arg2) { return set_HIT0_LO(env, 0 - (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); } static inline target_ulong bitswap(target_ulong v) { v = ((v >> 1) & (target_ulong)0x5555555555555555ULL) | ((v & (target_ulong)0x5555555555555555ULL) << 1); v = ((v >> 2) & (target_ulong)0x3333333333333333ULL) | ((v & (target_ulong)0x3333333333333333ULL) << 2); v = ((v >> 4) & (target_ulong)0x0F0F0F0F0F0F0F0FULL) | ((v & (target_ulong)0x0F0F0F0F0F0F0F0FULL) << 4); return v; } #ifdef TARGET_MIPS64 target_ulong helper_dbitswap(target_ulong rt) { return bitswap(rt); } #endif target_ulong helper_bitswap(target_ulong rt) { return (int32_t)bitswap(rt); } target_ulong helper_rotx(target_ulong rs, uint32_t shift, uint32_t shiftx, uint32_t stripe) { int i; uint64_t tmp0 = ((uint64_t)rs) << 32 | ((uint64_t)rs & 0xffffffff); uint64_t tmp1 = tmp0; for (i = 0; i <= 46; i++) { int s; if (i & 0x8) { s = shift; } else { s = shiftx; } if (stripe != 0 && !(i & 0x4)) { s = ~s; } if (s & 0x10) { if (tmp0 & (1LL << (i + 16))) { tmp1 |= 1LL << i; } else { tmp1 &= ~(1LL << i); } } } uint64_t tmp2 = tmp1; for (i = 0; i <= 38; i++) { int s; if (i & 0x4) { s = shift; } else { s = shiftx; } if (s & 0x8) { if (tmp1 & (1LL << (i + 8))) { tmp2 |= 1LL << i; } else { tmp2 &= ~(1LL << i); } } } uint64_t tmp3 = tmp2; for (i = 0; i <= 34; i++) { int s; if (i & 0x2) { s = shift; } else { s = shiftx; } if (s & 0x4) { if (tmp2 & (1LL << (i + 4))) { tmp3 |= 1LL << i; } else { tmp3 &= ~(1LL << i); } } } uint64_t tmp4 = tmp3; for (i = 0; i <= 32; i++) { int s; if (i & 0x1) { s = shift; } else { s = shiftx; } if (s & 0x2) { if (tmp3 & (1LL << (i + 2))) { tmp4 |= 1LL << i; } else { tmp4 &= ~(1LL << i); } } } uint64_t tmp5 = tmp4; for (i = 0; i <= 31; i++) { int s; s = shift; if (s & 0x1) { if (tmp4 & (1LL << (i + 1))) { tmp5 |= 1LL << i; } else { tmp5 &= ~(1LL << i); } } } return (int64_t)(int32_t)(uint32_t)tmp5; } void helper_fork(target_ulong arg1, target_ulong arg2) { /* * arg1 = rt, arg2 = rs * TODO: store to TC register */ } target_ulong helper_yield(CPUMIPSState *env, target_ulong arg) { target_long arg1 = arg; if (arg1 < 0) { /* No scheduling policy implemented. */ if (arg1 != -2) { if (env->CP0_VPEControl & (1 << CP0VPECo_YSI) && env->active_tc.CP0_TCStatus & (1 << CP0TCSt_DT)) { env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); env->CP0_VPEControl |= 4 << CP0VPECo_EXCPT; do_raise_exception(env, EXCP_THREAD, GETPC()); } } } else if (arg1 == 0) { if (0) { /* TODO: TC underflow */ env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); do_raise_exception(env, EXCP_THREAD, GETPC()); } else { /* TODO: Deallocate TC */ } } else if (arg1 > 0) { /* Yield qualifier inputs not implemented. */ env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); env->CP0_VPEControl |= 2 << CP0VPECo_EXCPT; do_raise_exception(env, EXCP_THREAD, GETPC()); } return env->CP0_YQMask; } static inline void check_hwrena(CPUMIPSState *env, int reg, uintptr_t pc) { if ((env->hflags & MIPS_HFLAG_CP0) || (env->CP0_HWREna & (1 << reg))) { return; } do_raise_exception(env, EXCP_RI, pc); } target_ulong helper_rdhwr_cpunum(CPUMIPSState *env) { check_hwrena(env, 0, GETPC()); return env->CP0_EBase & 0x3ff; } target_ulong helper_rdhwr_synci_step(CPUMIPSState *env) { check_hwrena(env, 1, GETPC()); return env->SYNCI_Step; } target_ulong helper_rdhwr_cc(CPUMIPSState *env) { check_hwrena(env, 2, GETPC()); #ifdef CONFIG_USER_ONLY return env->CP0_Count; #else return (int32_t)cpu_mips_get_count(env); #endif } target_ulong helper_rdhwr_ccres(CPUMIPSState *env) { check_hwrena(env, 3, GETPC()); return env->CCRes; } target_ulong helper_rdhwr_performance(CPUMIPSState *env) { check_hwrena(env, 4, GETPC()); return env->CP0_Performance0; } target_ulong helper_rdhwr_xnp(CPUMIPSState *env) { check_hwrena(env, 5, GETPC()); return (env->CP0_Config5 >> CP0C5_XNP) & 1; } void helper_pmon(CPUMIPSState *env, int function) { function /= 2; switch (function) { case 2: /* TODO: char inbyte(int waitflag); */ if (env->active_tc.gpr[4] == 0) { env->active_tc.gpr[2] = -1; } /* Fall through */ case 11: /* TODO: char inbyte (void); */ env->active_tc.gpr[2] = -1; break; case 3: case 12: printf("%c", (char)(env->active_tc.gpr[4] & 0xFF)); break; case 17: break; case 158: { unsigned char *fmt = (void *)(uintptr_t)env->active_tc.gpr[4]; printf("%s", fmt); } break; } } #if !defined(CONFIG_USER_ONLY) void mips_cpu_do_unaligned_access(CPUState *cs, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { MIPSCPU *cpu = MIPS_CPU(cs); CPUMIPSState *env = &cpu->env; int error_code = 0; int excp; if (!(env->hflags & MIPS_HFLAG_DM)) { env->CP0_BadVAddr = addr; } if (access_type == MMU_DATA_STORE) { excp = EXCP_AdES; } else { excp = EXCP_AdEL; if (access_type == MMU_INST_FETCH) { error_code |= EXCP_INST_NOTAVAIL; } } do_raise_exception_err(env, excp, error_code, retaddr); } void mips_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, unsigned size, MMUAccessType access_type, int mmu_idx, MemTxAttrs attrs, MemTxResult response, uintptr_t retaddr) { MIPSCPU *cpu = MIPS_CPU(cs); MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(cpu); CPUMIPSState *env = &cpu->env; if (access_type == MMU_INST_FETCH) { do_raise_exception(env, EXCP_IBE, retaddr); } else if (!mcc->no_data_aborts) { do_raise_exception(env, EXCP_DBE, retaddr); } } #endif /* !CONFIG_USER_ONLY */