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


Reply via email to