@@ -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)
@@ -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,
@@ -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)
@@ -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) */
@@ -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;
@@ -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);
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(-)