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. Signed-off-by: Kirill A. Korinsky <[email protected]> --- 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
