aboutsummaryrefslogtreecommitdiff
path: root/target/ppc/power8-pmu.c
diff options
context:
space:
mode:
authorDaniel Henrique Barboza <danielhb413@gmail.com>2021-12-17 17:57:18 +0100
committerCédric Le Goater <clg@kaod.org>2021-12-17 17:57:18 +0100
commit46d396bde988020528445691089711eb27b348b5 (patch)
treed3bedbaffa6b7506d737e3b3b203bd2a7460ea0d /target/ppc/power8-pmu.c
parent1474ba6d100179c248fed6c67756814a6fa89432 (diff)
target/ppc: enable PMU instruction count
The PMU is already counting cycles by calculating time elapsed in nanoseconds. Counting instructions is a different matter and requires another approach. This patch adds the capability of counting completed instructions (Perf event PM_INST_CMPL) by counting the amount of instructions translated in each translation block right before exiting it. A new pmu_count_insns() helper in translation.c was added to do that. After verifying that the PMU is counting instructions, call helper_insns_inc(). This new helper from power8-pmu.c will add the instructions to the relevant counters. It'll also be responsible for triggering counter negative overflows as it is already being done with cycles. To verify whether the PMU is counting instructions or now, a new hflags named 'HFLAGS_INSN_CNT' is introduced. This flag will match the internal state of the PMU. We're be using this flag to avoid calling helper_insn_inc() when we do not have a valid instruction event being sampled. Reviewed-by: David Gibson <david@gibson.dropbear.id.au> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> Message-Id: <20211201151734.654994-7-danielhb413@gmail.com> Signed-off-by: Cédric Le Goater <clg@kaod.org>
Diffstat (limited to 'target/ppc/power8-pmu.c')
-rw-r--r--target/ppc/power8-pmu.c67
1 files changed, 66 insertions, 1 deletions
diff --git a/target/ppc/power8-pmu.c b/target/ppc/power8-pmu.c
index 399234a2fc..e163ba5640 100644
--- a/target/ppc/power8-pmu.c
+++ b/target/ppc/power8-pmu.c
@@ -112,6 +112,54 @@ static PMUEventType pmc_get_event(CPUPPCState *env, int sprn)
return evt_type;
}
+bool pmu_insn_cnt_enabled(CPUPPCState *env)
+{
+ int sprn;
+
+ for (sprn = SPR_POWER_PMC1; sprn <= SPR_POWER_PMC5; sprn++) {
+ if (pmc_get_event(env, sprn) == PMU_EVENT_INSTRUCTIONS) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool pmu_increment_insns(CPUPPCState *env, uint32_t num_insns)
+{
+ bool overflow_triggered = false;
+ int sprn;
+
+ /* PMC6 never counts instructions */
+ for (sprn = SPR_POWER_PMC1; sprn <= SPR_POWER_PMC5; sprn++) {
+ if (pmc_get_event(env, sprn) != PMU_EVENT_INSTRUCTIONS) {
+ continue;
+ }
+
+ env->spr[sprn] += num_insns;
+
+ if (env->spr[sprn] >= PMC_COUNTER_NEGATIVE_VAL &&
+ pmc_has_overflow_enabled(env, sprn)) {
+
+ overflow_triggered = true;
+
+ /*
+ * The real PMU will always trigger a counter overflow with
+ * PMC_COUNTER_NEGATIVE_VAL. We don't have an easy way to
+ * do that since we're counting block of instructions at
+ * the end of each translation block, and we're probably
+ * passing this value at this point.
+ *
+ * Let's write PMC_COUNTER_NEGATIVE_VAL to the overflowed
+ * counter to simulate what the real hardware would do.
+ */
+ env->spr[sprn] = PMC_COUNTER_NEGATIVE_VAL;
+ }
+ }
+
+ return overflow_triggered;
+}
+
static void pmu_update_cycles(CPUPPCState *env)
{
uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
@@ -195,7 +243,7 @@ void helper_store_mmcr0(CPUPPCState *env, target_ulong value)
env->spr[SPR_POWER_MMCR0] = value;
- /* MMCR0 writes can change HFLAGS_PMCCCLEAR */
+ /* MMCR0 writes can change HFLAGS_PMCCCLEAR and HFLAGS_INSN_CNT */
hreg_compute_hflags(env);
/* Update cycle overflow timers with the current MMCR0 state */
@@ -207,6 +255,9 @@ void helper_store_mmcr1(CPUPPCState *env, uint64_t value)
pmu_update_cycles(env);
env->spr[SPR_POWER_MMCR1] = value;
+
+ /* MMCR1 writes can change HFLAGS_INSN_CNT */
+ hreg_compute_hflags(env);
}
target_ulong helper_read_pmc(CPUPPCState *env, uint32_t sprn)
@@ -237,6 +288,20 @@ static void fire_PMC_interrupt(PowerPCCPU *cpu)
return;
}
+/* This helper assumes that the PMC is running. */
+void helper_insns_inc(CPUPPCState *env, uint32_t num_insns)
+{
+ bool overflow_triggered;
+ PowerPCCPU *cpu;
+
+ overflow_triggered = pmu_increment_insns(env, num_insns);
+
+ if (overflow_triggered) {
+ cpu = env_archcpu(env);
+ fire_PMC_interrupt(cpu);
+ }
+}
+
static void cpu_ppc_pmu_timer_cb(void *opaque)
{
PowerPCCPU *cpu = opaque;