@@ -129,8 +129,10 @@ 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 */
};
@@ -2452,6 +2454,7 @@ enum {
PPC_INTERRUPT_HMI, /* Hypervisor Maintenance interrupt */
PPC_INTERRUPT_HDOORBELL, /* Hypervisor Doorbell interrupt */
PPC_INTERRUPT_HVIRT, /* Hypervisor virtualization interrupt */
+ PPC_INTERRUPT_PMC, /* Hypervisor virtualization interrupt */
};
/* Processor Compatibility mask (PCR) */
@@ -799,6 +799,23 @@ 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_FSCR] & (1ull << FSCR_EBB)) &&
+ (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);
@@ -1046,6 +1063,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) {
@@ -323,8 +323,30 @@ static void fire_PMC_interrupt(PowerPCCPU *cpu)
return;
}
- /* PMC interrupt not implemented yet */
- return;
+ if (env->spr[SPR_POWER_MMCR0] & MMCR0_FCECE) {
+ env->spr[SPR_POWER_MMCR0] &= ~MMCR0_FCECE;
+ env->spr[SPR_POWER_MMCR0] |= MMCR0_FC;
+
+ /* Changing MMCR0_FC demands a new hflags compute */
+ hreg_compute_hflags(env);
+
+ /*
+ * Delete all pending timers if we need to freeze
+ * the PMC. We'll restart them when the PMC starts
+ * running again.
+ */
+ pmu_delete_timers(env);
+ }
+
+ pmu_update_cycles(env, env->spr[SPR_POWER_MMCR0]);
+
+ if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMAE) {
+ env->spr[SPR_POWER_MMCR0] &= ~MMCR0_PMAE;
+ env->spr[SPR_POWER_MMCR0] |= MMCR0_PMAO;
+ }
+
+ /* Fire the PMC hardware exception */
+ ppc_set_irq(cpu, PPC_INTERRUPT_PMC, 1);
}
/* This helper assumes that the PMC is running. */