diff mbox series

[v2,10/16] target/ppc: PMU Event-Based exception support

Message ID 20210824163032.394099-11-danielhb413@gmail.com
State New
Headers show
Series PMU-EBB support for PPC64 TCG | expand

Commit Message

Daniel Henrique Barboza Aug. 24, 2021, 4:30 p.m. UTC
From: Gustavo Romero <gromero@linux.ibm.com>

Following up the rfebb implementation, this patch adds the EBB exception
support that are triggered by Performance Monitor alerts. This exception
occurs when an enabled PMU condition or event happens and both MMCR0_EBE
and BESCR_PME are set.

The supported PM alerts will consist of counter negative conditions of
the PMU counters. This will be achieved by a timer mechanism that will
predict when a counter becomes negative. The PMU timer callback will set
the appropriate bits in MMCR0 and fire a PMC interrupt. The EBB
exception code will then set the appropriate BESCR bits, set the next
instruction pointer to the address pointed by the return register
(SPR_EBBRR), and redirect execution to the handler (pointed by
SPR_EBBHR).

This patch sets the basic structure of interrupts and timers. The
following patches will add the counter negative logic for the registers.

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

Comments

David Gibson Aug. 25, 2021, 5:37 a.m. UTC | #1
On Tue, Aug 24, 2021 at 01:30:26PM -0300, Daniel Henrique Barboza wrote:
> From: Gustavo Romero <gromero@linux.ibm.com>
> 
> Following up the rfebb implementation, this patch adds the EBB exception
> support that are triggered by Performance Monitor alerts. This exception
> occurs when an enabled PMU condition or event happens and both MMCR0_EBE
> and BESCR_PME are set.
> 
> The supported PM alerts will consist of counter negative conditions of
> the PMU counters. This will be achieved by a timer mechanism that will
> predict when a counter becomes negative. The PMU timer callback will set
> the appropriate bits in MMCR0 and fire a PMC interrupt. The EBB
> exception code will then set the appropriate BESCR bits, set the next
> instruction pointer to the address pointed by the return register
> (SPR_EBBRR), and redirect execution to the handler (pointed by
> SPR_EBBHR).
> 
> This patch sets the basic structure of interrupts and timers. The
> following patches will add the counter negative logic for the
> registers.

A timer makes sense for tripping cycles based EBB events.  But for
instructions based EBB events, shouldn't you instead have a test in
the update instructions path which trips the event if you've passed
the magic number?

> 
> 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
Daniel Henrique Barboza Aug. 30, 2021, 7:09 p.m. UTC | #2
On 8/25/21 2:37 AM, David Gibson wrote:
> On Tue, Aug 24, 2021 at 01:30:26PM -0300, Daniel Henrique Barboza wrote:
>> From: Gustavo Romero <gromero@linux.ibm.com>
>>
>> Following up the rfebb implementation, this patch adds the EBB exception
>> support that are triggered by Performance Monitor alerts. This exception
>> occurs when an enabled PMU condition or event happens and both MMCR0_EBE
>> and BESCR_PME are set.
>>
>> The supported PM alerts will consist of counter negative conditions of
>> the PMU counters. This will be achieved by a timer mechanism that will
>> predict when a counter becomes negative. The PMU timer callback will set
>> the appropriate bits in MMCR0 and fire a PMC interrupt. The EBB
>> exception code will then set the appropriate BESCR bits, set the next
>> instruction pointer to the address pointed by the return register
>> (SPR_EBBRR), and redirect execution to the handler (pointed by
>> SPR_EBBHR).
>>
>> This patch sets the basic structure of interrupts and timers. The
>> following patches will add the counter negative logic for the
>> registers.
> 
> A timer makes sense for tripping cycles based EBB events.  But for
> instructions based EBB events, shouldn't you instead have a test in
> the update instructions path which trips the event if you've passed
> the magic number?

IIUC this is done in patch 14.


Thanks,


Daniel

> 
>>
>> 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
>
diff mbox series

Patch

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