Octeon firmware and kernels use implementation specific CP0 registers
for the cycle counter and local memory control.
Add CvmCount, CvmCtl, and CvmMemCtl accessors for Octeon CPUs; migrate
the new CP0 state, and translate the enabled CVMSEG virtual window to
per vCPU backing memory.
---
target/mips/cpu.h | 5 +++
target/mips/system/machine.c | 13 +++++++
target/mips/system/physaddr.c | 22 +++++++++++
target/mips/tcg/op_helper.c | 2 +-
target/mips/tcg/system/cp0_helper.c | 10 +++++
target/mips/tcg/system_helper.h.inc | 1 +
target/mips/tcg/translate.c | 60 +++++++++++++++++++++++++++++
7 files changed, 112 insertions(+), 1 deletion(-)
diff --git a/target/mips/cpu.h b/target/mips/cpu.h
index 319147a948..c6da52e17f 100644
--- a/target/mips/cpu.h
+++ b/target/mips/cpu.h
@@ -341,6 +341,8 @@ typedef struct mips_def_t mips_def_t;
#define CP0_REG09__COUNT 0
#define CP0_REG09__SAARI 6
#define CP0_REG09__SAAR 7
+#define CP0_REG09__CVMCOUNT 6
+#define CP0_REG09__CVMCTL 7
/* CP0 Register 10 */
#define CP0_REG10__ENTRYHI 0
#define CP0_REG10__GUESTCTL1 4
@@ -349,6 +351,7 @@ typedef struct mips_def_t mips_def_t;
/* CP0 Register 11 */
#define CP0_REG11__COMPARE 0
#define CP0_REG11__GUESTCTL0EXT 4
+#define CP0_REG11__CVMMEMCTL 7
/* CP0 Register 12 */
#define CP0_REG12__STATUS 0
#define CP0_REG12__INTCTL 1
@@ -787,6 +790,7 @@ typedef struct CPUArchState {
* CP0 Register 9
*/
int32_t CP0_Count;
+ target_ulong CP0_CvmCtl;
#define CP0SAARI_TARGET 0 /* 5..0 */
#define CP0SAAR_BASE 12 /* 43..12 */
#define CP0SAAR_SIZE 1 /* 5..1 */
@@ -801,6 +805,7 @@ typedef struct CPUArchState {
* CP0 Register 11
*/
int32_t CP0_Compare;
+ target_ulong CP0_CvmMemCtl;
/*
* CP0 Register 12
*/
diff --git a/target/mips/system/machine.c b/target/mips/system/machine.c
index bd1e4002cf..3661da0566 100644
--- a/target/mips/system/machine.c
+++ b/target/mips/system/machine.c
@@ -372,6 +372,18 @@ static const VMStateDescription mips_vmstate_octeon_crypto
= {
}
};
+static const VMStateDescription mips_vmstate_octeon_cp0 = {
+ .name = "cpu/octeon_cp0",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = mips_octeon_needed,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINTTL(env.CP0_CvmCtl, MIPSCPU),
+ VMSTATE_UINTTL(env.CP0_CvmMemCtl, MIPSCPU),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
const VMStateDescription vmstate_mips_cpu = {
.name = "cpu",
.version_id = 21,
@@ -490,6 +502,7 @@ const VMStateDescription vmstate_mips_cpu = {
&mips_vmstate_timer,
&mips_vmstate_octeon_multiplier,
&mips_vmstate_octeon_crypto,
+ &mips_vmstate_octeon_cp0,
NULL
}
};
diff --git a/target/mips/system/physaddr.c b/target/mips/system/physaddr.c
index 626d7ccc4c..a0538d8b6b 100644
--- a/target/mips/system/physaddr.c
+++ b/target/mips/system/physaddr.c
@@ -21,6 +21,14 @@
#include "exec/page-protection.h"
#include "../internal.h"
+/*
+ * QEMU-only backing for per-core CVMSEG. Keep it outside guest DRAM;
+ * the guest sees the virtual CVMSEG window, not this physical address.
+ */
+#define OCTEON_CVMSEG_BASE 0x10000000000ULL
+#define OCTEON_CVMSEG_SIZE 0x4000ULL
+#define OCTEON_CVMSEG_MASK 0x3fffULL
+
static int is_seg_am_mapped(unsigned int am, bool eu, int mmu_idx)
{
/*
@@ -128,6 +136,20 @@ int get_physical_address(CPUMIPSState *env, hwaddr
*physical,
int ret = TLBRET_MATCH;
target_ulong address = real_address;
+#if defined(TARGET_MIPS64)
+ if ((env->insn_flags & INSN_OCTEON) && (env->CP0_CvmMemCtl & 0x100) &&
+ address >= 0xffffffffffff8000ULL &&
+ address <= 0xffffffffffffbfffULL) {
+ MIPSCPU *cpu = env_archcpu(env);
+
+ *physical = OCTEON_CVMSEG_BASE +
+ CPU(cpu)->cpu_index * OCTEON_CVMSEG_SIZE +
+ (address & OCTEON_CVMSEG_MASK);
+ *prot = PAGE_READ | PAGE_WRITE;
+ return TLBRET_MATCH;
+ }
+#endif
+
if (address <= USEG_LIMIT) {
/* useg */
uint16_t segctl;
diff --git a/target/mips/tcg/op_helper.c b/target/mips/tcg/op_helper.c
index df1b5c3734..2bfe661ef2 100644
--- a/target/mips/tcg/op_helper.c
+++ b/target/mips/tcg/op_helper.c
@@ -268,7 +268,7 @@ target_ulong helper_rdhwr_cvmcount(CPUMIPSState *env)
#ifdef CONFIG_USER_ONLY
return cpu_get_host_ticks();
#else
- return (uint32_t)cpu_mips_get_count(env);
+ return helper_mfc0_cvmcount(env);
#endif
}
diff --git a/target/mips/tcg/system/cp0_helper.c
b/target/mips/tcg/system/cp0_helper.c
index ba6b487b75..1c23d2a525 100644
--- a/target/mips/tcg/system/cp0_helper.c
+++ b/target/mips/tcg/system/cp0_helper.c
@@ -23,6 +23,8 @@
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qemu/main-loop.h"
+#include "qemu/timer.h"
+#include "hw/core/clock.h"
#include "cpu.h"
#include "internal.h"
#include "qemu/host-utils.h"
@@ -374,6 +376,14 @@ target_ulong helper_mfc0_count(CPUMIPSState *env)
return (int32_t)cpu_mips_get_count(env);
}
+target_ulong helper_mfc0_cvmcount(CPUMIPSState *env)
+{
+ MIPSCPU *cpu = env_archcpu(env);
+
+ return clock_ns_to_ticks(cpu->clock,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
+}
+
target_ulong helper_mftc0_entryhi(CPUMIPSState *env)
{
int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
diff --git a/target/mips/tcg/system_helper.h.inc
b/target/mips/tcg/system_helper.h.inc
index eaac5e2c6f..c8b6d8b92c 100644
--- a/target/mips/tcg/system_helper.h.inc
+++ b/target/mips/tcg/system_helper.h.inc
@@ -31,6 +31,7 @@ DEF_HELPER_1(mftc0_tcschedule, tl, env)
DEF_HELPER_1(mfc0_tcschefback, tl, env)
DEF_HELPER_1(mftc0_tcschefback, tl, env)
DEF_HELPER_1(mfc0_count, tl, env)
+DEF_HELPER_1(mfc0_cvmcount, tl, env)
DEF_HELPER_1(mftc0_entryhi, tl, env)
DEF_HELPER_1(mftc0_status, tl, env)
DEF_HELPER_1(mftc0_cause, tl, env)
diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c
index e3467d1525..123ba6aecd 100644
--- a/target/mips/tcg/translate.c
+++ b/target/mips/tcg/translate.c
@@ -5460,6 +5460,19 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int
reg, int sel)
ctx->base.is_jmp = DISAS_EXIT;
register_name = "Count";
break;
+ case CP0_REG09__CVMCOUNT:
+ CP0_CHECK(ctx->insn_flags & INSN_OCTEON);
+ translator_io_start(&ctx->base);
+ gen_helper_mfc0_cvmcount(arg, tcg_env);
+ tcg_gen_ext32s_tl(arg, arg);
+ register_name = "CvmCount";
+ break;
+ case CP0_REG09__CVMCTL:
+ CP0_CHECK(ctx->insn_flags & INSN_OCTEON);
+ tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_CvmCtl));
+ tcg_gen_ext32s_tl(arg, arg);
+ register_name = "CvmCtl";
+ break;
default:
goto cp0_unimplemented;
}
@@ -5481,6 +5494,13 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int
reg, int sel)
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Compare));
register_name = "Compare";
break;
+ case CP0_REG11__CVMMEMCTL:
+ CP0_CHECK(ctx->insn_flags & INSN_OCTEON);
+ tcg_gen_ld_tl(arg, tcg_env,
+ offsetof(CPUMIPSState, CP0_CvmMemCtl));
+ tcg_gen_ext32s_tl(arg, arg);
+ register_name = "CvmMemCtl";
+ break;
/* 6,7 are implementation dependent */
default:
goto cp0_unimplemented;
@@ -6176,6 +6196,11 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int
reg, int sel)
gen_helper_mtc0_count(tcg_env, arg);
register_name = "Count";
break;
+ case CP0_REG09__CVMCTL:
+ CP0_CHECK(ctx->insn_flags & INSN_OCTEON);
+ tcg_gen_st_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_CvmCtl));
+ register_name = "CvmCtl";
+ break;
default:
goto cp0_unimplemented;
}
@@ -6196,6 +6221,13 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int
reg, int sel)
gen_helper_mtc0_compare(tcg_env, arg);
register_name = "Compare";
break;
+ case CP0_REG11__CVMMEMCTL:
+ CP0_CHECK(ctx->insn_flags & INSN_OCTEON);
+ tcg_gen_st_tl(arg, tcg_env,
+ offsetof(CPUMIPSState, CP0_CvmMemCtl));
+ ctx->base.is_jmp = DISAS_STOP;
+ register_name = "CvmMemCtl";
+ break;
/* 6,7 are implementation dependent */
default:
goto cp0_unimplemented;
@@ -6940,6 +6972,17 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int
reg, int sel)
ctx->base.is_jmp = DISAS_EXIT;
register_name = "Count";
break;
+ case CP0_REG09__CVMCOUNT:
+ CP0_CHECK(ctx->insn_flags & INSN_OCTEON);
+ translator_io_start(&ctx->base);
+ gen_helper_mfc0_cvmcount(arg, tcg_env);
+ register_name = "CvmCount";
+ break;
+ case CP0_REG09__CVMCTL:
+ CP0_CHECK(ctx->insn_flags & INSN_OCTEON);
+ tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_CvmCtl));
+ register_name = "CvmCtl";
+ break;
default:
goto cp0_unimplemented;
}
@@ -6960,6 +7003,12 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int
reg, int sel)
gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Compare));
register_name = "Compare";
break;
+ case CP0_REG11__CVMMEMCTL:
+ CP0_CHECK(ctx->insn_flags & INSN_OCTEON);
+ tcg_gen_ld_tl(arg, tcg_env,
+ offsetof(CPUMIPSState, CP0_CvmMemCtl));
+ register_name = "CvmMemCtl";
+ break;
/* 6,7 are implementation dependent */
default:
goto cp0_unimplemented;
@@ -7642,6 +7691,11 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int
reg, int sel)
gen_helper_mtc0_count(tcg_env, arg);
register_name = "Count";
break;
+ case CP0_REG09__CVMCTL:
+ CP0_CHECK(ctx->insn_flags & INSN_OCTEON);
+ tcg_gen_st_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_CvmCtl));
+ register_name = "CvmCtl";
+ break;
default:
goto cp0_unimplemented;
}
@@ -7664,6 +7718,12 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int
reg, int sel)
gen_helper_mtc0_compare(tcg_env, arg);
register_name = "Compare";
break;
+ case CP0_REG11__CVMMEMCTL:
+ CP0_CHECK(ctx->insn_flags & INSN_OCTEON);
+ tcg_gen_st_tl(arg, tcg_env,
+ offsetof(CPUMIPSState, CP0_CvmMemCtl));
+ register_name = "CvmMemCtl";
+ break;
/* 6,7 are implementation dependent */
default:
goto cp0_unimplemented;
--
2.54.0