target/riscv/tcg/csr.c | 73 ++++++++++++++++++++++---------
tests/tcg/riscv64/Makefile.softmmu-target | 4 ++
tests/tcg/riscv64/sscofpmf-overflow.S | 60 +++++++++++++++++++++++++
3 files changed, 116 insertions(+), 21 deletions(-)
diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c
index
65985efb220c80023cfd9d08e1878a19342aa879..52664a26f5a97a5dc8ff37abf99b4d10927fb120
100644
--- a/target/riscv/tcg/csr.c
+++ b/target/riscv/tcg/csr.c
@@ -1209,23 +1209,58 @@ static RISCVException write_minstretcfgh(CPURISCVState
*env, int csrno,
static RISCVException read_mhpmevent(CPURISCVState *env, int csrno,
target_ulong *val)
{
- int evt_index = csrno - CSR_MCOUNTINHIBIT;
+ int ctr_idx = csrno - CSR_MCOUNTINHIBIT;
bool rv32 = riscv_cpu_mxl(env) == MXL_RV32;
- *val = extract64(env->mhpmevent_val[evt_index], 0, rv32 ? 32 : 64);
+ *val = extract64(env->mhpmevent_val[ctr_idx], 0, rv32 ? 32 : 64);
return RISCV_EXCP_NONE;
}
+static uint64_t riscv_pmu_ctr_get_fixed_counters_val(CPURISCVState *env,
+ int counter_idx);
+
+static void riscv_pmu_write_mhpmevent(CPURISCVState *env,
+ uint32_t ctr_idx, uint64_t value)
+{
+ PMUCTRState *counter = &env->pmu_ctrs[ctr_idx];
+ bool enabled = !get_field(env->mcountinhibit, BIT(ctr_idx));
+
+ /*
+ * A programmable counter backed by a fixed source uses mhpmcounter_val
+ * as its base and mhpmcounter_prev as the source snapshot. Preserve the
+ * visible value before changing the source or its privilege filters.
+ */
+ if (enabled &&
+ (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) ||
+ riscv_pmu_ctr_monitor_instructions(env, ctr_idx))) {
+ uint64_t source = riscv_pmu_ctr_get_fixed_counters_val(env,
+ ctr_idx);
+
+ counter->mhpmcounter_val += source - counter->mhpmcounter_prev;
+ }
+
+ env->mhpmevent_val[ctr_idx] = value;
+ riscv_pmu_update_event_map(env, value, ctr_idx);
+
+ if (enabled &&
+ (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) ||
+ riscv_pmu_ctr_monitor_instructions(env, ctr_idx))) {
+ counter->mhpmcounter_prev =
+ riscv_pmu_ctr_get_fixed_counters_val(env, ctr_idx);
+ riscv_pmu_setup_timer(env, counter->mhpmcounter_val, ctr_idx);
+ }
+}
+
static RISCVException write_mhpmevent(CPURISCVState *env, int csrno,
target_ulong val, uintptr_t ra)
{
- int evt_index = csrno - CSR_MCOUNTINHIBIT;
+ int ctr_idx = csrno - CSR_MCOUNTINHIBIT;
uint64_t mhpmevt_val;
uint64_t inh_avail_mask;
if (riscv_cpu_mxl(env) == MXL_RV32) {
- mhpmevt_val = deposit64(env->mhpmevent_val[evt_index], 0, 32, val);
+ mhpmevt_val = deposit64(env->mhpmevent_val[ctr_idx], 0, 32, val);
} else {
inh_avail_mask = ~MHPMEVENT_FILTER_MASK | MHPMEVENT_BIT_MINH;
inh_avail_mask |= riscv_has_ext(env, RVU) ? MHPMEVENT_BIT_UINH : 0;
@@ -1237,8 +1272,7 @@ static RISCVException write_mhpmevent(CPURISCVState *env,
int csrno,
mhpmevt_val = val & inh_avail_mask;
}
- env->mhpmevent_val[evt_index] = mhpmevt_val;
- riscv_pmu_update_event_map(env, mhpmevt_val, evt_index);
+ riscv_pmu_write_mhpmevent(env, ctr_idx, mhpmevt_val);
return RISCV_EXCP_NONE;
}
@@ -1246,9 +1280,9 @@ static RISCVException write_mhpmevent(CPURISCVState *env,
int csrno,
static RISCVException read_mhpmeventh(CPURISCVState *env, int csrno,
target_ulong *val)
{
- int evt_index = csrno - CSR_MHPMEVENT3H + 3;
+ int ctr_idx = csrno - CSR_MHPMEVENT3H + 3;
- *val = extract64(env->mhpmevent_val[evt_index], 32, 32);
+ *val = extract64(env->mhpmevent_val[ctr_idx], 32, 32);
return RISCV_EXCP_NONE;
}
@@ -1256,7 +1290,7 @@ static RISCVException read_mhpmeventh(CPURISCVState *env,
int csrno,
static RISCVException write_mhpmeventh(CPURISCVState *env, int csrno,
target_ulong val, uintptr_t ra)
{
- int evt_index = csrno - CSR_MHPMEVENT3H + 3;
+ int ctr_idx = csrno - CSR_MHPMEVENT3H + 3;
target_ulong inh_avail_mask = (target_ulong)(~MHPMEVENTH_FILTER_MASK |
MHPMEVENTH_BIT_MINH);
@@ -1267,10 +1301,9 @@ static RISCVException write_mhpmeventh(CPURISCVState *env, int csrno,
inh_avail_mask |= (riscv_has_ext(env, RVH) &&
riscv_has_ext(env, RVS)) ? MHPMEVENTH_BIT_VSINH : 0;
- env->mhpmevent_val[evt_index] = deposit64(env->mhpmevent_val[evt_index],
- 32, 32, val & inh_avail_mask);
-
- riscv_pmu_update_event_map(env, env->mhpmevent_val[evt_index], evt_index);
+ riscv_pmu_write_mhpmevent(env, ctr_idx,
+ deposit64(env->mhpmevent_val[ctr_idx], 32, 32,
+ val & inh_avail_mask));
return RISCV_EXCP_NONE;
}
@@ -1512,11 +1545,11 @@ static int rmw_cd_mhpmcounterh(CPURISCVState *env, int
ctr_idx,
return 0;
}
-static int rmw_cd_mhpmevent(CPURISCVState *env, int evt_index,
+static int rmw_cd_mhpmevent(CPURISCVState *env, int ctr_idx,
target_ulong *val, target_ulong new_val,
uint64_t wr_mask)
{
- uint64_t mhpmevt_val = env->mhpmevent_val[evt_index];
+ uint64_t mhpmevt_val = env->mhpmevent_val[ctr_idx];
if (wr_mask != 0 && wr_mask != -1) {
return -EINVAL;
@@ -1531,8 +1564,7 @@ static int rmw_cd_mhpmevent(CPURISCVState *env, int
evt_index,
wr_mask &= ~MHPMEVENT_BIT_MINH;
/* wr_mask is 64-bit so upper 32 bits of mhpmevt_val are retained */
mhpmevt_val = (new_val & wr_mask) | (mhpmevt_val & ~wr_mask);
- env->mhpmevent_val[evt_index] = mhpmevt_val;
- riscv_pmu_update_event_map(env, mhpmevt_val, evt_index);
+ riscv_pmu_write_mhpmevent(env, ctr_idx, mhpmevt_val);
} else {
return -EINVAL;
}
@@ -1540,11 +1572,11 @@ static int rmw_cd_mhpmevent(CPURISCVState *env, int
evt_index,
return 0;
}
-static int rmw_cd_mhpmeventh(CPURISCVState *env, int evt_index,
+static int rmw_cd_mhpmeventh(CPURISCVState *env, int ctr_idx,
target_ulong *val, target_ulong new_val,
target_ulong wr_mask)
{
- uint64_t mhpmevt_val = env->mhpmevent_val[evt_index];
+ uint64_t mhpmevt_val = env->mhpmevent_val[ctr_idx];
uint32_t mhpmevth_val = extract64(mhpmevt_val, 32, 32);
if (wr_mask != 0 && wr_mask != -1) {
@@ -1560,8 +1592,7 @@ static int rmw_cd_mhpmeventh(CPURISCVState *env, int
evt_index,
wr_mask &= ~MHPMEVENTH_BIT_MINH;
mhpmevth_val = (new_val & wr_mask) | (mhpmevth_val & ~wr_mask);
mhpmevt_val = deposit64(mhpmevt_val, 32, 32, mhpmevth_val);
- env->mhpmevent_val[evt_index] = mhpmevt_val;
- riscv_pmu_update_event_map(env, mhpmevt_val, evt_index);
+ riscv_pmu_write_mhpmevent(env, ctr_idx, mhpmevt_val);
} else {
return -EINVAL;
}
diff --git a/tests/tcg/riscv64/Makefile.softmmu-target
b/tests/tcg/riscv64/Makefile.softmmu-target
index
cd1ec0b8219bdb63dfe6a45bb9674fa22e62ee4e..97978c6707247bb786f41549ada69e12aab619ce
100644
--- a/tests/tcg/riscv64/Makefile.softmmu-target
+++ b/tests/tcg/riscv64/Makefile.softmmu-target
@@ -40,6 +40,10 @@ run-test-mcycle-rv32: test-mcycle-rv32
$(call run-test, $<, \
$(QEMU) -cpu rv32 -icount shift=1 $(QEMU_OPTS)$<)
+TESTS += sscofpmf-overflow
+run-sscofpmf-overflow: sscofpmf-overflow
+ $(call run-test, $<, $(QEMU) -cpu max -icount shift=0 $(QEMU_OPTS)$<)
+
EXTRA_RUNS += run-plugin-doubletrap
run-plugin-doubletrap: doubletrap
$(call run-test, $<, \
diff --git a/tests/tcg/riscv64/sscofpmf-overflow.S
b/tests/tcg/riscv64/sscofpmf-overflow.S
new file mode 100644
index
0000000000000000000000000000000000000000..587c7d3ec430aac984621dfb818f141088be7b23
--- /dev/null
+++ b/tests/tcg/riscv64/sscofpmf-overflow.S
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+ .option norvc
+ .option norelax
+
+ .text
+ .global _start
+_start:
+ /* Program hpmcounter3 while no event is selected. */
+ csrw 0x323, zero /* mhpmevent3 */
+ li t0, -256
+ csrw 0xb03, t0 /* mhpmcounter3 */
+
+ /* Start counting retired instructions with overflow enabled. */
+ li t0, 2
+ csrw 0x323, t0
+
+ /* Cross the 64-bit unsigned overflow boundary. */
+ .rept 1024
+ nop
+ .endr
+
+ /* OF must be sticky and LCOFIP must pend even with LCOFIE clear. */
+ li t4, 0
+ csrr t0, 0x323
+ srli t1, t0, 63
+ xori t1, t1, 1
+ or t4, t4, t1
+
+ csrr t0, mip
+ li t1, 1 << 13
+ and t0, t0, t1
+ sltu t0, zero, t0
+ xori t0, t0, 1
+ or t4, t4, t0
+
+ /* The counter wraps and continues counting after overflow. */
+ csrr t0, 0xb03
+ li t1, -256
+ sltu t0, t0, t1
+ xori t0, t0, 1
+ or t4, t4, t0
+
+ lla a1, semiargs
+ li t0, 0x20026 /* ADP_Stopped_ApplicationExit */
+ sd t0, 0(a1)
+ sd t4, 8(a1)
+ li a0, 0x20 /* TARGET_SYS_EXIT_EXTENDED */
+
+ /* Semihosting call sequence. */
+ .balign 16
+ slli zero, zero, 0x1f
+ ebreak
+ srai zero, zero, 0x7
+ j .
+
+ .data
+ .balign 16
+semiargs:
+ .space 16