/* * 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" 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; } } #ifdef TARGET_MIPS64 target_ulong helper_lcsr_cpucfg(CPUMIPSState *env, target_ulong rs) { switch (rs) { case 0: return env->CP0_PRid; case 1: return env->lcsr_cpucfg1; case 2: return env->lcsr_cpucfg2; default: return 0; } } #endif #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 */