CC: Gustavo Romero <gustavo.romero@linaro.org>
Signed-off-by: Gustavo Romero <gromero@linux.ibm.com>
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
---
hw/ppc/spapr_cpu_core.c | 6 ++++++
target/ppc/cpu.h | 12 +++++++++++-
target/ppc/excp_helper.c | 28 +++++++++++++++++++++++++++
target/ppc/power8_pmu.c | 41 ++++++++++++++++++++++++++++++++++++++++
target/ppc/power8_pmu.h | 25 ++++++++++++++++++++++++
5 files changed, 111 insertions(+), 1 deletion(-)
create mode 100644 target/ppc/power8_pmu.h
diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c
index 4f316a6f9d..c7a342c4aa 100644
--- a/hw/ppc/spapr_cpu_core.c
+++ b/hw/ppc/spapr_cpu_core.c
@@ -20,6 +20,7 @@
#include "target/ppc/kvm_ppc.h"
#include "hw/ppc/ppc.h"
#include "target/ppc/mmu-hash64.h"
+#include "target/ppc/power8_pmu.h"
#include "sysemu/numa.h"
#include "sysemu/reset.h"
#include "sysemu/hw_accel.h"
@@ -266,6 +267,11 @@ static bool spapr_realize_vcpu(PowerPCCPU *cpu,
SpaprMachineState *spapr,
return false;
}
+ /* Init PMU interrupt timer (TCG only) */
+ if (!kvm_enabled()) {
+ cpu_ppc_pmu_timer_init(env);
+ }
+
if (!sc->pre_3_0_migration) {
vmstate_register(NULL, cs->cpu_index, &vmstate_spapr_cpu_state,
cpu->machine_data);
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index 9d5eb9ead4..535754ddff 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -129,8 +129,9 @@ enum {
/* ISA 3.00 additions */
POWERPC_EXCP_HVIRT = 101,
POWERPC_EXCP_SYSCALL_VECTORED = 102, /* scv exception
*/
+ POWERPC_EXCP_EBB = 103, /* Event-based branch exception */
/* EOL
*/
- POWERPC_EXCP_NB = 103,
+ POWERPC_EXCP_NB = 104,
/* QEMU exceptions: special cases we want to stop translation
*/
POWERPC_EXCP_SYSCALL_USER = 0x203, /* System call in user mode only
*/
};
@@ -1047,6 +1048,8 @@ struct ppc_radix_page_info {
#define PPC_CPU_OPCODES_LEN 0x40
#define PPC_CPU_INDIRECT_OPCODES_LEN 0x20
+#define PMU_TIMERS_LEN 5
+
struct CPUPPCState {
/* Most commonly used resources during translated code execution first */
target_ulong gpr[32]; /* general purpose registers */
@@ -1208,6 +1211,12 @@ struct CPUPPCState {
* running cycles.
*/
uint64_t pmu_base_time;
+
+ /*
+ * Timers used to fire performance monitor alerts and
+ * interrupts. All PMCs but PMC5 has a timer.
+ */
+ QEMUTimer *pmu_intr_timers[PMU_TIMERS_LEN];
};
#define SET_FIT_PERIOD(a_, b_, c_, d_) \
@@ -2424,6 +2433,7 @@ enum {
PPC_INTERRUPT_HMI, /* Hypervisor Maintenance interrupt */
PPC_INTERRUPT_HDOORBELL, /* Hypervisor Doorbell interrupt */
PPC_INTERRUPT_HVIRT, /* Hypervisor virtualization interrupt */
+ PPC_INTERRUPT_PMC, /* Performance Monitor Counter interrupt */
};
/* Processor Compatibility mask (PCR) */
diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
index 058b063d8a..e97898c5f4 100644
--- a/target/ppc/excp_helper.c
+++ b/target/ppc/excp_helper.c
@@ -821,6 +821,22 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int
excp_model, int excp)
cpu_abort(cs, "Non maskable external exception "
"is not implemented yet !\n");
break;
+ case POWERPC_EXCP_EBB: /* Event-based branch exception */
+ if ((env->spr[SPR_BESCR] & BESCR_GE) &&
+ (env->spr[SPR_BESCR] & BESCR_PME)) {
+ target_ulong nip;
+
+ env->spr[SPR_BESCR] &= ~BESCR_GE; /* Clear GE */
+ env->spr[SPR_BESCR] |= BESCR_PMEO; /* Set PMEO */
+ env->spr[SPR_EBBRR] = env->nip; /* Save NIP for rfebb insn */
+ nip = env->spr[SPR_EBBHR]; /* EBB handler */
+ powerpc_set_excp_state(cpu, nip, env->msr);
+ }
+ /*
+ * This interrupt is handled by userspace. No need
+ * to proceed.
+ */
+ return;
default:
excp_invalid:
cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp);
@@ -1068,6 +1084,18 @@ static void ppc_hw_interrupt(CPUPPCState *env)
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_THERM);
return;
}
+ /* PMC -> Event-based branch exception */
+ if (env->pending_interrupts & (1 << PPC_INTERRUPT_PMC)) {
+ /*
+ * Performance Monitor event-based exception can only
+ * occur in problem state.
+ */
+ if (msr_pr == 1) {
+ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PMC);
+ powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_EBB);
+ return;
+ }
+ }
}
if (env->resume_as_sreset) {
diff --git a/target/ppc/power8_pmu.c b/target/ppc/power8_pmu.c
index 4545fe7810..a57b602125 100644
--- a/target/ppc/power8_pmu.c
+++ b/target/ppc/power8_pmu.c
@@ -12,12 +12,14 @@
#include "qemu/osdep.h"
+#include "power8_pmu.h"
#include "cpu.h"
#include "helper_regs.h"
#include "exec/exec-all.h"
#include "exec/helper-proto.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
+#include "hw/ppc/ppc.h"
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
@@ -114,6 +116,45 @@ static void update_cycles_PMCs(CPUPPCState *env)
}
}
+static void cpu_ppc_pmu_timer_cb(void *opaque)
+{
+ PowerPCCPU *cpu = opaque;
+ CPUPPCState *env = &cpu->env;
+ uint64_t mmcr0;
+
+ mmcr0 = env->spr[SPR_POWER_MMCR0];
+ if (env->spr[SPR_POWER_MMCR0] & MMCR0_EBE) {
+ /* freeeze counters if needed */
+ if (mmcr0 & MMCR0_FCECE) {
+ mmcr0 &= ~MMCR0_FCECE;
+ mmcr0 |= MMCR0_FC;
+ }
+
+ /* Clear PMAE and set PMAO */
+ if (mmcr0 & MMCR0_PMAE) {
+ mmcr0 &= ~MMCR0_PMAE;
+ mmcr0 |= MMCR0_PMAO;
+ }
+ env->spr[SPR_POWER_MMCR0] = mmcr0;
+
+ /* Fire the PMC hardware exception */
+ ppc_set_irq(cpu, PPC_INTERRUPT_PMC, 1);
+ }
+}
+
+void cpu_ppc_pmu_timer_init(CPUPPCState *env)
+{
+ PowerPCCPU *cpu = env_archcpu(env);
+ QEMUTimer *timer;
+ int i;
+
+ for (i = 0; i < PMU_TIMERS_LEN; i++) {
+ timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_pmu_timer_cb,
+ cpu);
+ env->pmu_intr_timers[i] = timer;
+ }
+}
+
void helper_store_mmcr0(CPUPPCState *env, target_ulong value)
{
target_ulong curr_value = env->spr[SPR_POWER_MMCR0];
diff --git a/target/ppc/power8_pmu.h b/target/ppc/power8_pmu.h
new file mode 100644
index 0000000000..34a9d0e8a2
--- /dev/null
+++ b/target/ppc/power8_pmu.h
@@ -0,0 +1,25 @@
+/*
+ * PMU emulation helpers for TCG IBM POWER chips
+ *
+ * Copyright IBM Corp. 2021
+ *
+ * Authors:
+ * Daniel Henrique Barboza <danielhb413@gmail.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef PMU_BOOK3S_HELPER
+#define PMU_BOOK3S_HELPER
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "exec/helper-proto.h"
+#include "qemu/error-report.h"
+#include "qemu/main-loop.h"
+
+void cpu_ppc_pmu_timer_init(CPUPPCState *env);
+
+#endif