diff mbox series

[v4,06/15] target/ppc: initialize PMUEvents on MMCR1 write

Message ID 20211018010133.315842-7-danielhb413@gmail.com (mailing list archive)
State New, archived
Headers show
Series PPC64/TCG: Implement 'rfebb' instruction | expand

Commit Message

Daniel Henrique Barboza Oct. 18, 2021, 1:01 a.m. UTC
The value of MMCR1 determines the events that are going to be sampled by
the programmable counters (PMCs 1-4). PMCs 5 and 6 are always counting
instructions and cycles respectively and aren't affected by MMCR1.

This patch adds a helper to initialize PMCs 1-4 PMUEvents when writing
the MMCR1 register. The following events are considered valid at this
moment:

- For PMCs 1-4, event 0x2 is the implementation dependent value of
PMU_EVENT_INSTRUCTIONS and event 0x1E is the implementation dependent
value of PMU_EVENT_CYCLES. These events are supported by IBM Power chips
since Power8, at least, and the Linux Perf driver makes use of these
events until kernel v5.15;

- for PMC1, event 0xF0 is the architected PowerISA event for cycles.
Event 0xFE is the architected PowerISA event for instructions.

Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
---
 target/ppc/cpu.h                 | 11 ++++++
 target/ppc/cpu_init.c            |  2 +-
 target/ppc/helper.h              |  1 +
 target/ppc/power8-pmu-regs.c.inc | 16 +++++++++
 target/ppc/power8-pmu.c          | 59 ++++++++++++++++++++++++++++++++
 target/ppc/spr_tcg.h             |  1 +
 6 files changed, 89 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index 21591ec725..aaf503c8ff 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -382,6 +382,17 @@  typedef struct PMUEvent {
 #define MMCR2_UREG_MASK (MMCR2_FC1P0 | MMCR2_FC2P0 | MMCR2_FC3P0 | \
                          MMCR2_FC4P0 | MMCR2_FC5P0 | MMCR2_FC6P0)
 
+#define MMCR1_EVT_SIZE 8
+/* extract64() does a right shift before extracting */
+#define MMCR1_PMC1SEL_START 32
+#define MMCR1_PMC1EVT_EXTR (64 - MMCR1_PMC1SEL_START - MMCR1_EVT_SIZE)
+#define MMCR1_PMC2SEL_START 40
+#define MMCR1_PMC2EVT_EXTR (64 - MMCR1_PMC2SEL_START - MMCR1_EVT_SIZE)
+#define MMCR1_PMC3SEL_START 48
+#define MMCR1_PMC3EVT_EXTR (64 - MMCR1_PMC3SEL_START - MMCR1_EVT_SIZE)
+#define MMCR1_PMC4SEL_START 56
+#define MMCR1_PMC4EVT_EXTR (64 - MMCR1_PMC4SEL_START - MMCR1_EVT_SIZE)
+
 /* LPCR bits */
 #define LPCR_VPM0         PPC_BIT(0)
 #define LPCR_VPM1         PPC_BIT(1)
diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c
index 65545ba9ca..7c9ed5dfe1 100644
--- a/target/ppc/cpu_init.c
+++ b/target/ppc/cpu_init.c
@@ -6824,7 +6824,7 @@  static void register_book3s_pmu_sup_sprs(CPUPPCState *env)
                      KVM_REG_PPC_MMCR0, 0x00000000);
     spr_register_kvm(env, SPR_POWER_MMCR1, "MMCR1",
                      SPR_NOACCESS, SPR_NOACCESS,
-                     &spr_read_generic, &spr_write_generic,
+                     &spr_read_generic, &spr_write_MMCR1,
                      KVM_REG_PPC_MMCR1, 0x00000000);
     spr_register_kvm(env, SPR_POWER_MMCRA, "MMCRA",
                      SPR_NOACCESS, SPR_NOACCESS,
diff --git a/target/ppc/helper.h b/target/ppc/helper.h
index 4076aa281e..fc5bae8330 100644
--- a/target/ppc/helper.h
+++ b/target/ppc/helper.h
@@ -20,6 +20,7 @@  DEF_HELPER_1(rfscv, void, env)
 DEF_HELPER_1(hrfid, void, env)
 DEF_HELPER_2(store_lpcr, void, env, tl)
 DEF_HELPER_2(store_pcr, void, env, tl)
+DEF_HELPER_2(store_mmcr1, void, env, tl)
 #endif
 DEF_HELPER_1(check_tlb_flush_local, void, env)
 DEF_HELPER_1(check_tlb_flush_global, void, env)
diff --git a/target/ppc/power8-pmu-regs.c.inc b/target/ppc/power8-pmu-regs.c.inc
index 7391851238..a0a64e76ad 100644
--- a/target/ppc/power8-pmu-regs.c.inc
+++ b/target/ppc/power8-pmu-regs.c.inc
@@ -219,6 +219,17 @@  void spr_write_PMC56_ureg(DisasContext *ctx, int sprn, int gprn)
     /* The remaining steps are similar to PMCs 1-4 userspace write */
     spr_write_PMC14_ureg(ctx, sprn, gprn);
 }
+
+void spr_write_MMCR1(DisasContext *ctx, int sprn, int gprn)
+{
+    gen_helper_store_mmcr1(cpu_env, cpu_gpr[gprn]);
+
+    /*
+     * Writes in MMCR1 must force a new translation block, allowing
+     * the PMU to calculate events with more accuracy.
+     */
+    ctx->base.is_jmp = DISAS_EXIT_UPDATE;
+}
 #else
 void spr_read_MMCR0_ureg(DisasContext *ctx, int gprn, int sprn)
 {
@@ -259,4 +270,9 @@  void spr_write_PMC56_ureg(DisasContext *ctx, int sprn, int gprn)
 {
     spr_noaccess(ctx, gprn, sprn);
 }
+
+void spr_write_MMCR1(DisasContext *ctx, int sprn, int gprn)
+{
+    spr_write_generic(ctx, sprn, gprn);
+}
 #endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */
diff --git a/target/ppc/power8-pmu.c b/target/ppc/power8-pmu.c
index 42452b5870..9a0b544b04 100644
--- a/target/ppc/power8-pmu.c
+++ b/target/ppc/power8-pmu.c
@@ -23,6 +23,65 @@ 
 
 #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
 
+/*
+ * For PMCs 1-4, IBM POWER chips has support for an implementation
+ * dependent event, 0x1E, that enables cycle counting. The Linux kernel
+ * makes extensive use of 0x1E, so let's also support it.
+ *
+ * Likewise, event 0x2 is an implementation-dependent event that IBM
+ * POWER chips implement (at least since POWER8) that is equivalent to
+ * PM_INST_CMPL. Let's support this event on PMCs 1-4 as well.
+ */
+static void define_enabled_events(CPUPPCState *env)
+{
+    uint8_t mmcr1_evt_extr[] = { MMCR1_PMC1EVT_EXTR, MMCR1_PMC2EVT_EXTR,
+                                 MMCR1_PMC3EVT_EXTR, MMCR1_PMC4EVT_EXTR };
+    int i;
+
+    for (i = 0; i < 4; i++) {
+        uint8_t pmcsel = extract64(env->spr[SPR_POWER_MMCR1],
+                                   mmcr1_evt_extr[i],
+                                   MMCR1_EVT_SIZE);
+        PMUEvent *event = &env->pmu_events[i];
+
+        switch (pmcsel) {
+        case 0x2:
+            event->type = PMU_EVENT_INSTRUCTIONS;
+            break;
+        case 0x1E:
+            event->type = PMU_EVENT_CYCLES;
+            break;
+        case 0xF0:
+            /*
+             * PMC1SEL = 0xF0 is the architected PowerISA v3.1
+             * event that counts cycles using PMC1.
+             */
+            if (event->sprn == SPR_POWER_PMC1) {
+                event->type = PMU_EVENT_CYCLES;
+            }
+            break;
+        case 0xFE:
+            /*
+             * PMC1SEL = 0xFE is the architected PowerISA v3.1
+             * event to sample instructions using PMC1.
+             */
+            if (event->sprn == SPR_POWER_PMC1) {
+                event->type = PMU_EVENT_INSTRUCTIONS;
+            }
+            break;
+        default:
+            event->type = PMU_EVENT_INVALID;
+        }
+    }
+}
+
+void helper_store_mmcr1(CPUPPCState *env, uint64_t value)
+{
+    env->spr[SPR_POWER_MMCR1] = value;
+
+    define_enabled_events(env);
+}
+
 static void fire_PMC_interrupt(PowerPCCPU *cpu)
 {
     CPUPPCState *env = &cpu->env;
diff --git a/target/ppc/spr_tcg.h b/target/ppc/spr_tcg.h
index 520f1ef233..85852f236e 100644
--- a/target/ppc/spr_tcg.h
+++ b/target/ppc/spr_tcg.h
@@ -25,6 +25,7 @@ 
 void spr_noaccess(DisasContext *ctx, int gprn, int sprn);
 void spr_read_generic(DisasContext *ctx, int gprn, int sprn);
 void spr_write_generic(DisasContext *ctx, int sprn, int gprn);
+void spr_write_MMCR1(DisasContext *ctx, int sprn, int gprn);
 void spr_read_xer(DisasContext *ctx, int gprn, int sprn);
 void spr_write_xer(DisasContext *ctx, int sprn, int gprn);
 void spr_read_lr(DisasContext *ctx, int gprn, int sprn);