Capture shpmspc and shpmsdata just before updating mip.LCOFIP.
As required by the spec, record the current PC, counter ID and effective
privilege mode. When supervisor counter delegation is enabled,
only overflows of delegated counters update the sample CSRs.

Preserve shpmspc and shpmsdata while LCOFIP remains pending so later
overflows do not replace the pending sample. Currently, cntrid is not
updated if two counters overflow simultaneously.

The captured PC is QEMU's current env->pc, so it provides best-effort
attribution rather than guaranteed precise attribution.

Signed-off-by: Mayuresh Chitale <[email protected]>
---
 target/riscv/cpu_bits.h |  3 +--
 target/riscv/tcg/pmu.c  | 39 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 61258ce6e1..e99da434d5 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -642,9 +642,8 @@
 #define SHPMSDATA_CNTRID    0x1f
 #define SHPMSDATA_MODE      0x60
 #define SHPMSDATA_V         0x80
-#define SHPMSDATA_MODE_U    0x00
-#define SHPMSDATA_MODE_S    0x20
 #define SHPMSDATA_VALID_MASK (SHPMSDATA_CNTRID | SHPMSDATA_MODE | SHPMSDATA_V)
+#define SHPMSDATA_MODE_SHIFT 5
 
 /* Crypto Extension */
 #define CSR_SEED            0x015
diff --git a/target/riscv/tcg/pmu.c b/target/riscv/tcg/pmu.c
index 1a4658319b..31edd31117 100644
--- a/target/riscv/tcg/pmu.c
+++ b/target/riscv/tcg/pmu.c
@@ -178,6 +178,43 @@ void riscv_pmu_decr_instret(CPURISCVState *env)
     env->pmu_ctrs[2].mhpmcounter_prev++;
 }
 
+static void riscv_pmu_sspesa_capture(CPURISCVState *env, uint32_t ctr_idx)
+{
+    RISCVCPU *cpu = env_archcpu(env);
+    target_ulong sdata;
+    bool cde, ctr_enabled;
+
+    if (!cpu->cfg.ext_sspesa) {
+        return;
+    }
+
+    /*
+     * When supervisor counter delegation is enabled only overflows of
+     * delegated counters update the sample CSRs.
+     */
+    cde = get_field(env->menvcfg, MENVCFG_CDE);
+    ctr_enabled = get_field(env->mcounteren, BIT(ctr_idx));
+    if (cde && !ctr_enabled) {
+        return;
+    }
+
+    sdata = ctr_idx;
+    sdata |= env->priv << SHPMSDATA_MODE_SHIFT;
+    if (env->virt_enabled) {
+        sdata |= SHPMSDATA_V;
+    }
+
+    /*
+     * Only the first overflow that raises LCOFIP updates the sample PC and
+     * metadata.  Currently updating CNTRID on simultaneous overflows is
+     * not supported.
+     */
+    if (!(env->mip & MIP_LCOFIP)) {
+        env->shpmspc = env->pc;
+        env->shpmsdata = sdata;
+    }
+}
+
 int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx)
 {
     uint32_t ctr_idx;
@@ -211,6 +248,7 @@ int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum 
riscv_pmu_event_idx event_idx)
         /* Generate interrupt only if OF bit is clear */
         if (!(env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_OF)) {
             env->mhpmevent_val[ctr_idx] |= MHPMEVENT_BIT_OF;
+            riscv_pmu_sspesa_capture(env, ctr_idx);
             riscv_cpu_update_mip(env, MIP_LCOFIP, BOOL_TO_MASK(1));
         }
     } else {
@@ -403,6 +441,7 @@ static void pmu_timer_trigger_irq(RISCVCPU *cpu,
 
     if (cpu->pmu_avail_ctrs & BIT(ctr_idx)) {
         if (pmu_hpmevent_set_of_if_clear(env, ctr_idx)) {
+            riscv_pmu_sspesa_capture(env, ctr_idx);
             riscv_cpu_update_mip(env, MIP_LCOFIP, BOOL_TO_MASK(1));
         }
     }
-- 
2.43.0


Reply via email to