summaryrefslogtreecommitdiffstats
path: root/target/mips/tcg/op_helper.c
diff options
context:
space:
mode:
authorPeter Maydell2021-05-03 13:05:11 +0200
committerPeter Maydell2021-05-03 13:05:12 +0200
commite93d8bcf9dbd5b8dd3b9ddbb1ece6a37e608f300 (patch)
tree2884b98954f0eb8572a467550fe4d9b43c1212d1 /target/mips/tcg/op_helper.c
parentMerge remote-tracking branch 'remotes/rth-gitlab/tags/pull-hex-20210502' into... (diff)
parentgitlab-ci: Add KVM mips64el cross-build jobs (diff)
downloadqemu-e93d8bcf9dbd5b8dd3b9ddbb1ece6a37e608f300.tar.gz
qemu-e93d8bcf9dbd5b8dd3b9ddbb1ece6a37e608f300.tar.xz
qemu-e93d8bcf9dbd5b8dd3b9ddbb1ece6a37e608f300.zip
Merge remote-tracking branch 'remotes/philmd/tags/mips-20210502' into staging
MIPS patches queue - Fix CACHEE opcode - Add missing CP0 checks to nanoMIPS RDPGPR / WRPGPR opcodes - Remove isa_get_irq() call in PIIX4 south bridge - Add various missing fields to the MIPS CPU migration vmstate - Lot of code moved around to allow TCG or KVM only builds - Restrict non-virtualized machines to TCG - Add KVM mips64el cross-build jobs to gitlab-ci # gpg: Signature made Sun 02 May 2021 15:56:51 BST # gpg: using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE # gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full] # Primary key fingerprint: FAAB E75E 1291 7221 DCFD 6BB2 E3E3 2C2C DEAD C0DE * remotes/philmd/tags/mips-20210502: (36 commits) gitlab-ci: Add KVM mips64el cross-build jobs hw/mips: Restrict non-virtualized machines to TCG target/mips: Move TCG source files under tcg/ sub directory target/mips: Move CP0 helpers to sysemu/cp0.c target/mips: Move exception management code to exception.c target/mips: Move TLB management helpers to tcg/sysemu/tlb_helper.c target/mips: Move helper_cache() to tcg/sysemu/special_helper.c target/mips: Move Special opcodes to tcg/sysemu/special_helper.c target/mips: Restrict CPUMIPSTLBContext::map_address() handlers scope target/mips: Move tlb_helper.c to tcg/sysemu/ target/mips: Restrict mmu_init() to TCG target/mips: Move sysemu TCG-specific code to tcg/sysemu/ subfolder target/mips: Restrict cpu_mips_get_random() / update_pagemask() to TCG target/mips: Move physical addressing code to sysemu/physaddr.c target/mips: Move sysemu specific files under sysemu/ subfolder target/mips: Move cpu_signal_handler definition around target/mips: Add simple user-mode mips_cpu_tlb_fill() target/mips: Add simple user-mode mips_cpu_do_interrupt() target/mips: Introduce tcg-internal.h for TCG specific declarations meson: Introduce meson_user_arch source set for arch-specific user-mode ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'target/mips/tcg/op_helper.c')
-rw-r--r--target/mips/tcg/op_helper.c420
1 files changed, 420 insertions, 0 deletions
diff --git a/target/mips/tcg/op_helper.c b/target/mips/tcg/op_helper.c
new file mode 100644
index 0000000000..ce1549c985
--- /dev/null
+++ b/target/mips/tcg/op_helper.c
@@ -0,0 +1,420 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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);
+ CPUMIPSState *env = &cpu->env;
+
+ if (access_type == MMU_INST_FETCH) {
+ do_raise_exception(env, EXCP_IBE, retaddr);
+ } else {
+ do_raise_exception(env, EXCP_DBE, retaddr);
+ }
+}
+#endif /* !CONFIG_USER_ONLY */