Privilege filtering records cycle/instruction increments per mode. Reset
overwrites privilege/V without adding the final interval to the old
mode's total, so a counter filtering for that mode loses those counts.

Account for the old mode before entering M-mode with V=0 and rebuild the
overflow timer after reset. On initial reset, only initialize baselines:
no guest code has run, so pre-execution QEMU time must not count.
Preserve architectural counter and selector state, as before.

Test that a VS-only cycle counter retains its counts across reset.

Signed-off-by: TANG Tiancheng <[email protected]>
---
 target/riscv/cpu.c                   |  16 ++++++
 target/riscv/tcg/pmu.c               |  11 ++++
 target/riscv/tcg/pmu.h               |   1 +
 tests/tcg/riscv64/pmu-reset-vs.S     | 105 +++++++++++++++++++++++++++++++++++
 tests/tcg/riscv64/system/meson.build |   7 +++
 5 files changed, 140 insertions(+)

diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 
f60d7cca1e2f0008b09b1055c5cdeed3ec4ad1d3..e27312ac505dbee072c5a3c52073b00d703fd81d
 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -42,6 +42,9 @@
 #include "disas/capstone.h"
 #if !defined(CONFIG_USER_ONLY)
 #include "target/riscv/tcg/debug.h"
+#ifdef CONFIG_TCG
+#include "target/riscv/tcg/pmu.h"
+#endif
 #endif
 
 /* RISC-V CPU definitions */
@@ -979,6 +982,16 @@ static void riscv_cpu_reset_hold(Object *obj, ResetType 
type)
     }
 #ifndef CONFIG_USER_ONLY
     env->misa_mxl = mcc->def->misa_mxl_max;
+#ifdef CONFIG_TCG
+    /* The initial reset has no guest execution to count. */
+    if (tcg_enabled()) {
+        if (qdev_is_realized(DEVICE(cpu))) {
+            riscv_pmu_update_fixed_ctrs(env, PRV_M, false);
+        } else {
+            riscv_pmu_init_fixed_counter_baselines(env);
+        }
+    }
+#endif
     env->priv = PRV_M;
     env->virt_enabled = false;
     env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV);
@@ -1092,6 +1105,9 @@ static void riscv_cpu_reset_hold(Object *obj, ResetType 
type)
 
 #ifndef CONFIG_USER_ONLY
 #ifdef CONFIG_TCG
+    if (tcg_enabled()) {
+        riscv_pmu_rebuild_timer(env);
+    }
     if (cpu->cfg.debug || cpu->cfg.ext_sdtrig) {
         riscv_trigger_reset_hold(env);
     }
diff --git a/target/riscv/tcg/pmu.c b/target/riscv/tcg/pmu.c
index 
df99b572a4c16cb1ac65c2f7cde35c6f8349e681..1d692a0a8e3f7759e033214d579103627d070b5b
 100644
--- a/target/riscv/tcg/pmu.c
+++ b/target/riscv/tcg/pmu.c
@@ -171,6 +171,17 @@ void riscv_pmu_update_fixed_ctrs(CPURISCVState *env,
     riscv_pmu_update_fixed_ctrs_snapshot(env, newpriv, new_virt, &snapshot);
 }
 
+void riscv_pmu_init_fixed_counter_baselines(CPURISCVState *env)
+{
+    RISCVPMUFixedSnapshot snapshot;
+
+    riscv_pmu_take_fixed_snapshot(env, &snapshot);
+    env->pmu_fixed_ctrs[RISCV_PMU_FIXED_DOMAIN_CYCLE]
+        .counter_prev[PRV_M] = snapshot.cycle;
+    env->pmu_fixed_ctrs[RISCV_PMU_FIXED_DOMAIN_INSTRET]
+        .counter_prev[PRV_M] = snapshot.instret;
+}
+
 uint64_t
 riscv_pmu_ctr_get_fixed_value(CPURISCVState *env, uint32_t ctr_idx,
                               const RISCVPMUFixedSnapshot *snapshot)
diff --git a/target/riscv/tcg/pmu.h b/target/riscv/tcg/pmu.h
index 
1cfe6acf55b5468f5c00c4a136ece88981384341..fac84dbb1a8a6c637f7241ca27910e5e212223e9
 100644
--- a/target/riscv/tcg/pmu.h
+++ b/target/riscv/tcg/pmu.h
@@ -53,6 +53,7 @@ int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum 
riscv_pmu_event_idx event_idx);
 void riscv_pmu_generate_fdt_node(void *fdt, uint32_t cmask, char *pmu_name);
 void riscv_pmu_update_fixed_ctrs(CPURISCVState *env, privilege_mode_t newpriv,
                                  bool new_virt);
+void riscv_pmu_init_fixed_counter_baselines(CPURISCVState *env);
 void riscv_pmu_decr_instret(CPURISCVState *env);
 RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val,
                                   bool upper_half, uint32_t ctr_idx,
diff --git a/tests/tcg/riscv64/pmu-reset-vs.S b/tests/tcg/riscv64/pmu-reset-vs.S
new file mode 100644
index 
0000000000000000000000000000000000000000..d216faef17a2a395120cb3a71166a821a43dee88
--- /dev/null
+++ b/tests/tcg/riscv64/pmu-reset-vs.S
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/* CSR number for older assemblers. */
+#define CSR_MCYCLECFG        0x321
+
+       .option norvc
+       .option norelax
+
+       .equ    MSTATUS_MPP_S,  (1 << 11)
+       .equ    MSTATUS_MPP,    (3 << 11)
+       .equ    MSTATUS_SPP,    (1 << 8)
+       .equ    HSTATUS_SPV,    (1 << 7)
+       .equ    SIFIVE_TEST,    0x100000
+       .equ    MTIMECMP,       0x2004000
+       .equ    FINISHER_RESET, 0x7777
+
+       .text
+       .global _start
+_start:
+       /* Preserve the reset marker and saved count in MTIMECMP. */
+       li      t0, MTIMECMP
+       ld      t1, 0(t0)
+       srli    t2, t1, 48
+       li      t3, 0xa5a5
+       beq     t2, t3, after_reset
+
+       /* Count VS only, then remember the value before entering VS-mode. */
+       li      t0, 0x1d                /* MINH | SINH | UINH | VUINH */
+       slli    t0, t0, 58
+       csrw    CSR_MCYCLECFG, t0
+       csrr    t1, mcycle
+       slli    t1, t1, 16
+       srli    t1, t1, 16
+       li      t2, 0xa5a5
+       slli    t2, t2, 48
+       or      t1, t1, t2
+       li      t0, MTIMECMP
+       sd      t1, 0(t0)
+       li      t0, -1
+       csrw    pmpaddr0, t0
+       li      t0, 0x1f                /* RWX, NAPOT */
+       csrw    pmpcfg0, t0
+
+       /* Enter HS-mode first. */
+       csrr    t0, mstatus
+       li      t1, MSTATUS_MPP
+       not     t1, t1
+       and     t0, t0, t1
+       li      t1, MSTATUS_MPP_S
+       or      t0, t0, t1
+       csrw    mstatus, t0
+       lla     t0, hs_enter
+       csrw    mepc, t0
+       mret
+
+hs_enter:
+       li      t0, HSTATUS_SPV
+       csrs    hstatus, t0
+       li      t0, MSTATUS_SPP
+       csrs    sstatus, t0
+       lla     t0, vs_reset
+       csrw    sepc, t0
+       sret
+
+vs_reset:
+       /* These VS-mode cycles must remain in mcycle after reset. */
+       li      t0, 128
+1:
+       addi    t0, t0, -1
+       bnez    t0, 1b
+
+       li      t0, SIFIVE_TEST
+       li      t1, FINISHER_RESET
+       sw      t1, 0(t0)
+       j       .
+
+after_reset:
+       csrr    t0, mcycle
+       slli    t1, t1, 16              /* Clear the MTIMECMP marker. */
+       srli    t1, t1, 16
+       addi    t1, t1, 128
+       bgeu    t1, t0, fail
+       li      t3, 0
+       j       exit
+
+fail:
+       li      t3, 1
+
+exit:
+       lla     a1, semiargs
+       li      t0, 0x20026             /* ADP_Stopped_ApplicationExit */
+       sd      t0, 0(a1)
+       sd      t3, 8(a1)
+       li      a0, 0x20                /* TARGET_SYS_EXIT_EXTENDED */
+
+       .balign 16
+       slli    zero, zero, 0x1f
+       ebreak
+       srai    zero, zero, 0x7
+       j       .
+
+       .data
+       .balign 16
+semiargs:
+       .space  16
diff --git a/tests/tcg/riscv64/system/meson.build 
b/tests/tcg/riscv64/system/meson.build
index 
d2355090121ba7a7d0046c1fc1824d6a89c1267f..3f2043d100ff88cc39a21002a1e90ccab319001b
 100644
--- a/tests/tcg/riscv64/system/meson.build
+++ b/tests/tcg/riscv64/system/meson.build
@@ -142,6 +142,13 @@ tests += {
   },
 }
 
+tests += {
+  'pmu-reset-vs.S': {
+    'cflags': cflags,
+    'qemu_args': ['-cpu', 'max,smcntrpmf=true', '-icount', 'shift=0', 
qemu_args],
+  },
+}
+
 if 'qemu-system-riscv64' in emulators
   tcg_tests += {
     'riscv64-softmmu': {

-- 
2.43.0


Reply via email to