qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [RFC 09/14] Implement remaining PMU functionality


From: Alistair Francis
Subject: Re: [Qemu-devel] [RFC 09/14] Implement remaining PMU functionality
Date: Tue, 2 Feb 2016 13:22:14 -0800

On Wed, Aug 5, 2015 at 9:51 AM, Christopher Covington
<address@hidden> wrote:
> This adds logic to increment PMEVCNTR's based on different event inputs,
> implements all remaining CP registers, and triggers an interrupt on
> event overflow.

We (Xilinx) need parts of this patch to avoid kernel panics when
booting the 4.4 Linux kernel. Have you done any more work on it? If
you can send out a pach set I'm happy to have a look at it.

I can't find a cover letter though, so I haven't looked at the other
patches in the series.

Thanks,

Alistair

>
> Written by Aaron Lindsay.
>
> Signed-off-by: Christopher Covington <address@hidden>
> ---
>  target-arm/cpu-qom.h |   2 +
>  target-arm/cpu.c     |   2 +
>  target-arm/cpu.h     |  37 ++--
>  target-arm/cpu64.c   |   2 +
>  target-arm/helper.c  | 538 
> ++++++++++++++++++++++++++++++++++++++-------------
>  5 files changed, 425 insertions(+), 156 deletions(-)
>
> diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
> index bb6722f..2a0f3f4 100644
> --- a/target-arm/cpu-qom.h
> +++ b/target-arm/cpu-qom.h
> @@ -136,6 +136,8 @@ typedef struct ARMCPU {
>      uint32_t id_pfr0;
>      uint32_t id_pfr1;
>      uint32_t id_dfr0;
> +    uint32_t pmceid0;
> +    uint32_t pmceid1;
>      uint32_t id_afr0;
>      uint32_t id_mmfr0;
>      uint32_t id_mmfr1;
> diff --git a/target-arm/cpu.c b/target-arm/cpu.c
> index 87d0772..6a728d9 100644
> --- a/target-arm/cpu.c
> +++ b/target-arm/cpu.c
> @@ -938,6 +938,8 @@ static void cortex_a15_initfn(Object *obj)
>      cpu->id_pfr0 = 0x00001131;
>      cpu->id_pfr1 = 0x00011011;
>      cpu->id_dfr0 = 0x02010555;
> +    cpu->pmceid0 = 0x00000481; /* PMUv3 events 0x0, 0x8, and 0x11 */
> +    cpu->pmceid1 = 0x00000000;
>      cpu->id_afr0 = 0x00000000;
>      cpu->id_mmfr0 = 0x10201105;
>      cpu->id_mmfr1 = 0x20000000;
> diff --git a/target-arm/cpu.h b/target-arm/cpu.h
> index 44084a5..f6857fa 100644
> --- a/target-arm/cpu.h
> +++ b/target-arm/cpu.h
> @@ -112,11 +112,22 @@ typedef struct ARMGenericTimer {
>      uint64_t ctl; /* Timer Control register */
>  } ARMGenericTimer;
>
> +/* Indices into ARMCPU.ppi_outputs (and .gt_timer for timers) */
>  #define NUM_GTIMERS 2
>  #define GTIMER_PHYS 0
>  #define GTIMER_VIRT 1
>  #define PMU_IDX     2
>
> +enum pmu_counter_type {
> +    PMU_COUNTER_TYPE_SWINC = 0x000,
> +    PMU_COUNTER_TYPE_INSTRUCTIONS = 0x008,
> +    PMU_COUNTER_TYPE_CYCLES = 0x011
> +};
> +
> +/* Performance monitor event counter state */
> +#define NUM_PMU_COUNTERS 4 /* 0-30, inclusive, doesn't count cycle counter */
> +#define PMU_COUNTER_MASK 0x8000000F /* Mask of bits allowed for 
> PMINTEN{SET|CLR}*/
> +
>  typedef struct {
>      uint64_t raw_tcr;
>      uint32_t mask;
> @@ -287,12 +298,13 @@ typedef struct CPUARMState {
>          };
>          uint32_t c9_insn; /* Cache lockdown registers.  */
>          uint32_t c9_data;
> -        uint64_t c9_pmcr; /* performance monitor control register */
> -        uint64_t c9_pmcnten; /* perf monitor counter enables */
> +        uint32_t c9_pmcr; /* performance monitor control register */
> +        uint64_t c9_pmccntr;
> +        uint32_t c9_pmcnten; /* perf monitor counter enables */
>          uint32_t c9_pmovsr; /* perf monitor overflow status */
> -        uint32_t c9_pmxevtyper; /* perf monitor event type */
>          uint32_t c9_pmuserenr; /* perf monitor user enable */
>          uint32_t c9_pminten; /* perf monitor interrupt enables */
> +        uint32_t c9_pmselr; /* perf monitor event counter selection */
>          union { /* Memory attribute redirection */
>              struct {
>  #ifdef HOST_WORDS_BIGENDIAN
> @@ -354,6 +366,9 @@ typedef struct CPUARMState {
>              uint64_t tpidruro_ns;
>              uint64_t tpidrro_el[1];
>          };
> +        uint32_t c14_pmccfiltr; /* Performance Monitor Filter Register */
> +        uint32_t c14_pmevcntr[NUM_PMU_COUNTERS];
> +        uint32_t c14_pmevtyper[NUM_PMU_COUNTERS];
>          uint64_t c14_cntfrq; /* Counter Frequency register */
>          uint64_t c14_cntkctl; /* Timer Control register */
>          ARMGenericTimer c14_timer[NUM_GTIMERS];
> @@ -371,11 +386,6 @@ typedef struct CPUARMState {
>          uint64_t dbgwvr[16]; /* watchpoint value registers */
>          uint64_t dbgwcr[16]; /* watchpoint control registers */
>          uint64_t mdscr_el1;
> -        /* If the counter is enabled, this stores the last time the counter
> -         * was reset. Otherwise it stores the counter value
> -         */
> -        uint64_t c15_ccnt;
> -        uint64_t pmccfiltr_el0; /* Performance Monitor Filter Register */
>      } cp15;
>
>      struct {
> @@ -508,17 +518,6 @@ int cpu_arm_signal_handler(int host_signum, void *pinfo,
>  int arm_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw,
>                               int mmu_idx);
>
> -/**
> - * pmccntr_sync
> - * @env: CPUARMState
> - *
> - * Synchronises the counter in the PMCCNTR. This must always be called twice,
> - * once before any action that might affect the timer and again afterwards.
> - * The function is used to swap the state of the register if required.
> - * This only happens when not in user mode (!CONFIG_USER_ONLY)
> - */
> -void pmccntr_sync(CPUARMState *env);
> -
>  /* SCTLR bit meanings. Several bits have been reused in newer
>   * versions of the architecture; in that case we define constants
>   * for both old and new bit meanings. Code which tests against those
> diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c
> index 270bc2f..b2a3a74 100644
> --- a/target-arm/cpu64.c
> +++ b/target-arm/cpu64.c
> @@ -132,6 +132,8 @@ static void aarch64_a57_initfn(Object *obj)
>      cpu->id_isar5 = 0x00011121;
>      cpu->id_aa64pfr0 = 0x00002222;
>      cpu->id_aa64dfr0 = 0x10305106;
> +    cpu->pmceid0 = 0x00000481; /* PMUv3 events 0x0, 0x8, and 0x11 */
> +    cpu->pmceid1 = 0x00000000;
>      cpu->id_aa64isar0 = 0x00011120;
>      cpu->id_aa64mmfr0 = 0x00001124;
>      cpu->dbgdidr = 0x3516d000;
> diff --git a/target-arm/helper.c b/target-arm/helper.c
> index be3ad01..a659e67 100644
> --- a/target-arm/helper.c
> +++ b/target-arm/helper.c
> @@ -21,11 +21,6 @@ static inline int get_phys_addr(CPUARMState *env, 
> target_ulong address,
>                                  int access_type, ARMMMUIdx mmu_idx,
>                                  hwaddr *phys_ptr, MemTxAttrs *attrs, int 
> *prot,
>                                  target_ulong *page_size);
> -
> -/* Definitions for the PMCCNTR and PMCR registers */
> -#define PMCRD   0x8
> -#define PMCRC   0x4
> -#define PMCRE   0x1
>  #endif
>
>  #ifdef TARGET_AARCH64
> @@ -660,150 +655,337 @@ static CPAccessResult pmreg_access(CPUARMState *env, 
> const ARMCPRegInfo *ri)
>      return CP_ACCESS_OK;
>  }
>
> -#ifndef CONFIG_USER_ONLY
> +/* Definitions for the PMU CP registers */
> +#define PMCR_LC  0x40
> +#define PMCR_D   0x8
> +#define PMCR_C   0x4
> +#define PMCR_E   0x1
> +
> +#define PMCNTEN_C 0x80000000
> +
> +#define PMCCFILTR_NSH 0x08000000
> +#define PMCCFILTR_P 0x80000000
> +#define PMCCFILTR_U 0x40000000
>
> -static inline bool arm_ccnt_enabled(CPUARMState *env)
> +#define PMEVTYPER_NSH 0x08000000
> +#define PMEVTYPER_P 0x80000000
> +#define PMEVTYPER_U 0x40000000
> +#define PMEVTYPER_EVTCOUNT 0x000003ff
> +
> +#define PMXEVTYPER_P 0x80000000
> +#define PMXEVTYPER_U 0x40000000
> +
> +static void pmccfiltr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> +                            uint64_t value)
>  {
> -    /* This does not support checking PMCCFILTR_EL0 register */
> +    env->cp15.c14_pmccfiltr = value & 0xfc000000;
> +}
>
> -    if (!(env->cp15.c9_pmcr & PMCRE)) {
> -        return false;
> +static uint64_t pmevcntr_read(CPUARMState *env, const ARMCPRegInfo *ri,
> +                              const uint8_t idx)
> +{
> +    if (idx >= NUM_PMU_COUNTERS) {
> +        return arm_cp_read_zero(env, ri);
>      }
> +    return env->cp15.c14_pmevcntr[idx];
> +}
>
> -    return true;
> +static uint64_t pmevcntr_readfn(CPUARMState *env, const ARMCPRegInfo *ri)
> +{
> +    uint8_t idx = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
> +    return pmevcntr_read(env, ri, idx);
>  }
>
> -/* Called by anything that wants to be an input for event counts to the PMU
> - * (except for SWINC, event 0x000, since its events can target specific
> - * counters)
> - */
> -static void pmevcntr_increment(CPUARMState *env, uint8_t event_type,
> -                               uint64_t increment_by)
> +static void pmevcntr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> +                             uint64_t value, const uint8_t idx)
>  {
> +    if (idx >= NUM_PMU_COUNTERS) {
> +        arm_cp_write_ignore(env, ri, value);
> +    } else {
> +        env->cp15.c14_pmevcntr[idx] = value;
> +    }
>  }
>
> -void pmccntr_sync(CPUARMState *env)
> +static void pmevcntr_writefn(CPUARMState *env, const ARMCPRegInfo *ri,
> +                             uint64_t value)
>  {
> -    uint64_t temp_ticks;
> +    uint8_t idx = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
> +    pmevcntr_write(env, ri, value, idx);
>
> -    temp_ticks = muldiv64(qemu_clock_get_us(QEMU_CLOCK_VIRTUAL),
> -                          get_ticks_per_sec(), 1000000);
> +}
>
> -    if (env->cp15.c9_pmcr & PMCRD) {
> -        /* Increment once every 64 processor clock cycles */
> -        temp_ticks /= 64;
> +static uint64_t pmevtyper_read(CPUARMState *env, const ARMCPRegInfo *ri,
> +                               const uint8_t idx)
> +{
> +    if (idx >= NUM_PMU_COUNTERS) {
> +        return arm_cp_read_zero(env, ri);
>      }
> +    return env->cp15.c14_pmevtyper[idx];
> +}
> +
> +static uint64_t pmevtyper_readfn(CPUARMState *env, const ARMCPRegInfo *ri)
> +{
> +    uint8_t idx = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
> +    return pmevtyper_read(env, ri, idx);
> +}
>
> -    if (arm_ccnt_enabled(env)) {
> -        env->cp15.c15_ccnt = temp_ticks - env->cp15.c15_ccnt;
> +static void pmevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri,
> +                           uint64_t value, const uint8_t idx)
> +{
> +    if (idx >= NUM_PMU_COUNTERS) {
> +        arm_cp_write_ignore(env, ri, value);
> +    } else {
> +        env->cp15.c14_pmevtyper[idx] = value & 0xf80003ff;
>      }
>  }
>
> -static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> -                       uint64_t value)
> +static void pmevtyper_writefn(CPUARMState *env, const ARMCPRegInfo *ri,
> +                              uint64_t value)
>  {
> -    pmccntr_sync(env);
> +    uint8_t idx = ((ri->crm & 3) << 3) | (ri->opc2 & 7);
> +    pmevtyper_write(env, ri, value, idx);
> +}
>
> -    if (value & PMCRC) {
> -        /* The counter has been reset */
> -        env->cp15.c15_ccnt = 0;
> +static void pmselr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> +                           uint64_t value)
> +{
> +    env->cp15.c9_pmselr = value & 31;
> +}
> +
> +static uint64_t pmxevcntr_read(CPUARMState *env, const ARMCPRegInfo *ri)
> +{
> +    return pmevcntr_read(env, ri, env->cp15.c9_pmselr & 31);
> +}
> +
> +static void pmxevcntr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> +                            uint64_t value)
> +{
> +    pmevcntr_write(env, ri, value, env->cp15.c9_pmselr & 31);
> +}
> +
> +static uint64_t pmxevtyper_read(CPUARMState *env, const ARMCPRegInfo *ri)
> +{
> +    uint8_t idx = env->cp15.c9_pmselr & 31;
> +    if (idx == 31)
> +        return env->cp15.c14_pmccfiltr;
> +    return pmevtyper_read(env, ri, env->cp15.c9_pmselr & 31);
> +}
> +
> +static void pmxevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri,
> +                             uint64_t value)
> +{
> +    uint8_t idx = env->cp15.c9_pmselr & 31;
> +    if (idx == 31)
> +        pmccfiltr_write(env, ri, value);
> +    else
> +        pmevtyper_write(env, ri, value, env->cp15.c9_pmselr & 31);
> +}
> +
> +static inline bool pmccntr_enabled(CPUARMState *env)
> +{
> +    /* This does not check PMCR.E to see if all events are disabled, it is
> +     * assumed that is being checked externally, and doesn't support checking
> +     * for the secure/non-secure components of the PMCCFILTR_EL0 register.
> +     */
> +
> +    if (!(env->cp15.c9_pmcnten & PMCNTEN_C)) {
> +        return false;
>      }
>
> -    /* only the DP, X, D and E bits are writable */
> -    env->cp15.c9_pmcr &= ~0x39;
> -    env->cp15.c9_pmcr |= (value & 0x39);
> +    switch (arm_current_el(env)) {
> +    case 2:
> +        if (!(env->cp15.c14_pmccfiltr & PMCCFILTR_NSH)) {
> +            return false;
> +        } else {
> +          break;
> +        }
> +    case 1:
> +        if (env->cp15.c14_pmccfiltr & PMCCFILTR_P) {
> +            return false;
> +        } else {
> +            break;
> +        }
> +    case 0:
> +        if (env->cp15.c14_pmccfiltr & PMCCFILTR_U) {
> +            return false;
> +        } else {
> +            break;
> +        }
> +    }
>
> -    pmccntr_sync(env);
> +    return true;
>  }
>
> -static uint64_t pmccntr_read(CPUARMState *env, const ARMCPRegInfo *ri)
> +static inline bool pmevcntr_enabled(CPUARMState *env, uint8_t idx)
>  {
> -    uint64_t total_ticks;
> +    /* This does not check PMCR.E to see if all events are disabled, it is
> +     * assumed that is being checked externally, and doesn't support checking
> +     * for the secure/non-secure components of the PMEVTYPER<n>_EL0 registers
> +     */
> +
> +    if (!(env->cp15.c9_pmcnten & (1U << idx))) {
> +        return false;
> +    }
>
> -    if (!arm_ccnt_enabled(env)) {
> -        /* Counter is disabled, do not change value */
> -        return env->cp15.c15_ccnt;
> +    switch (arm_current_el(env)) {
> +    case 2:
> +        if (!(env->cp15.c14_pmevtyper[idx] & PMEVTYPER_NSH)) {
> +            return false;
> +        } else {
> +          break;
> +        }
> +    case 1:
> +        if (env->cp15.c14_pmevtyper[idx] & PMEVTYPER_P) {
> +            return false;
> +        } else {
> +            break;
> +        }
> +    case 0:
> +        if (env->cp15.c14_pmevtyper[idx] & PMEVTYPER_U) {
> +            return false;
> +        } else {
> +            break;
> +        }
>      }
>
> -    total_ticks = muldiv64(qemu_clock_get_us(QEMU_CLOCK_VIRTUAL),
> -                           get_ticks_per_sec(), 1000000);
> +    return true;
> +}
> +
> +static void pmu_update_irq(CPUARMState *env)
> +{
> +    ARMCPU *cpu = arm_env_get_cpu(env);
> +    qemu_set_irq(cpu->ppi_outputs[PMU_IDX], (env->cp15.c9_pmcr & PMCR_E) &&
> +        (env->cp15.c9_pminten & env->cp15.c9_pmovsr));
> +}
> +
> +static void pmccntr_increment(CPUARMState *env, uint64_t increment_by)
> +{
> +    if (pmccntr_enabled(env)) {
> +        uint64_t new_pmccntr = env->cp15.c9_pmccntr + increment_by;
> +        unsigned int overflow_bit = (env->cp15.c9_pmcr & PMCR_LC) ? 63 : 31;
>
> -    if (env->cp15.c9_pmcr & PMCRD) {
> -        /* Increment once every 64 processor clock cycles */
> -        total_ticks /= 64;
> +        if (!(new_pmccntr & (1 << overflow_bit)) &&
> +              env->cp15.c9_pmccntr & (1 << overflow_bit)) {
> +            env->cp15.c9_pmovsr |= (1 << 31);
> +            pmu_update_irq(env);
> +        }
> +        env->cp15.c9_pmccntr = new_pmccntr;
>      }
> -    return total_ticks - env->cp15.c15_ccnt;
>  }
>
> -static void pmccntr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> -                        uint64_t value)
> +/* Called by anything that wants to be an input for event counts to the PMU
> + * (except for SWINC, event 0x000 since its events can target specific 
> counters
> + */
> +static void pmevcntr_increment(CPUARMState *env, uint8_t event_type,
> +                               uint64_t increment_by)
>  {
> -    uint64_t total_ticks;
> +    unsigned int i;
>
> -    if (!arm_ccnt_enabled(env)) {
> -        /* Counter is disabled, set the absolute value */
> -        env->cp15.c15_ccnt = value;
> +    //early out if no counters are enabled
> +    if (!(env->cp15.c9_pmcr & PMCR_E) || !env->cp15.c9_pmcnten)
>          return;
> -    }
>
> -    total_ticks = muldiv64(qemu_clock_get_us(QEMU_CLOCK_VIRTUAL),
> -                           get_ticks_per_sec(), 1000000);
> +    if (event_type == PMU_COUNTER_TYPE_CYCLES)
> +        pmccntr_increment(env, increment_by);
> +
> +    for (i = 0; i < NUM_PMU_COUNTERS; i++) {
> +        if (pmevcntr_enabled(env, i) &&
> +              (env->cp15.c14_pmevtyper[i] & PMEVTYPER_EVTCOUNT) == 
> event_type) {
> +            uint32_t new_pmevcntr = env->cp15.c14_pmevcntr[i] + increment_by;
>
> -    if (env->cp15.c9_pmcr & PMCRD) {
> -        /* Increment once every 64 processor clock cycles */
> -        total_ticks /= 64;
> +            if (!(new_pmevcntr & (1 << 31)) &&
> +                  (env->cp15.c14_pmevcntr[i] & (1 << 31))) {
> +                env->cp15.c9_pmovsr |= (1 << i);
> +                pmu_update_irq(env);
> +            }
> +            env->cp15.c14_pmevcntr[i] = new_pmevcntr;
> +        }
>      }
> -    env->cp15.c15_ccnt = total_ticks - value;
>  }
>
> -static void pmccntr_write32(CPUARMState *env, const ARMCPRegInfo *ri,
> -                            uint64_t value)
> +static void pmswinc_write(CPUARMState *env, const ARMCPRegInfo *ri,
> +                          uint64_t value)
>  {
> -    uint64_t cur_val = pmccntr_read(env, NULL);
> +    unsigned int i;
>
> -    pmccntr_write(env, ri, deposit64(cur_val, 0, 32, value));
> +    for (i = 0; i < NUM_PMU_COUNTERS; i++) {
> +        if (value & (1 << i) &&
> +              pmevcntr_enabled(env, i) &&
> +              (env->cp15.c14_pmevtyper[i] & PMEVTYPER_EVTCOUNT) == 
> PMU_COUNTER_TYPE_SWINC) {
> +            uint32_t new_pmevcntr = env->cp15.c14_pmevcntr[i] + 1;
> +
> +            if (!(new_pmevcntr & (1 << 31)) &&
> +                  (env->cp15.c14_pmevcntr[i] & (1 << 31))) {
> +                env->cp15.c9_pmovsr |= (1 << i);
> +                pmu_update_irq(env);
> +            }
> +            env->cp15.c14_pmevcntr[i] = new_pmevcntr;
> +        }
> +    }
>  }
>
> -#else /* CONFIG_USER_ONLY */
> +static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> +                       uint64_t value)
> +{
> +    if (value & PMCR_C) {
> +        /* The counter has been reset */
> +        env->cp15.c9_pmccntr = 0;
> +    }
> +
> +    /* only the DP, X, D and E bits are writable */
> +    env->cp15.c9_pmcr &= ~0x39;
> +    env->cp15.c9_pmcr |= (value & 0x39);
> +}
>
> -void pmccntr_sync(CPUARMState *env)
> +static uint64_t pmccntr_read(CPUARMState *env, const ARMCPRegInfo *ri)
>  {
> +    return env->cp15.c9_pmccntr;
>  }
>
> -#endif
> +static void pmccntr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> +                          uint64_t value)
> +{
> +    env->cp15.c9_pmccntr = value;
> +}
>
> -static void pmccfiltr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> +static void pmccntr_write32(CPUARMState *env, const ARMCPRegInfo *ri,
>                              uint64_t value)
>  {
> -    pmccntr_sync(env);
> -    env->cp15.pmccfiltr_el0 = value & 0x7E000000;
> -    pmccntr_sync(env);
> +    uint64_t cur_val = pmccntr_read(env, NULL);
> +
> +    pmccntr_write(env, ri, deposit64(cur_val, 0, 32, value));
>  }
>
>  static void pmcntenset_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                              uint64_t value)
>  {
> -    value &= (1 << 31);
> +    value &= PMU_COUNTER_MASK;
>      env->cp15.c9_pmcnten |= value;
> +    pmu_update_irq(env);
>  }
>
>  static void pmcntenclr_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                               uint64_t value)
>  {
> -    value &= (1 << 31);
> +    value &= PMU_COUNTER_MASK;
>      env->cp15.c9_pmcnten &= ~value;
> +    pmu_update_irq(env);
>  }
>
> -static void pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> +static void pmovsset_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                           uint64_t value)
>  {
> -    env->cp15.c9_pmovsr &= ~value;
> +    value &= PMU_COUNTER_MASK;
> +    env->cp15.c9_pmovsr |= value;
> +    pmu_update_irq(env);
>  }
>
> -static void pmxevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri,
> -                             uint64_t value)
> +static void pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> +                         uint64_t value)
>  {
> -    env->cp15.c9_pmxevtyper = value & 0xff;
> +    value &= PMU_COUNTER_MASK;
> +    env->cp15.c9_pmovsr &= ~value;
> +    pmu_update_irq(env);
>  }
>
>  static void pmuserenr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> @@ -815,16 +997,17 @@ static void pmuserenr_write(CPUARMState *env, const 
> ARMCPRegInfo *ri,
>  static void pmintenset_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                               uint64_t value)
>  {
> -    /* We have no event counters so only the C bit can be changed */
> -    value &= (1 << 31);
> +    value &= PMU_COUNTER_MASK;
>      env->cp15.c9_pminten |= value;
> +    pmu_update_irq(env);
>  }
>
>  static void pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                               uint64_t value)
>  {
> -    value &= (1 << 31);
> +    value &= PMU_COUNTER_MASK;
>      env->cp15.c9_pminten &= ~value;
> +    pmu_update_irq(env);
>  }
>
>  static void vbar_write(CPUARMState *env, const ARMCPRegInfo *ri,
> @@ -905,11 +1088,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
>      /* the old v6 WFI, UNPREDICTABLE in v7 but we choose to NOP */
>      { .name = "NOP", .cp = 15, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 4,
>        .access = PL1_W, .type = ARM_CP_NOP },
> -    /* Performance monitors are implementation defined in v7,
> -     * but with an ARM recommended set of registers, which we
> -     * follow (although we don't actually implement any counters)
> -     *
> -     * Performance registers fall into three categories:
> +    /* Performance monitor registers fall into three categories:
>       *  (a) always UNDEF in PL0, RW in PL1 (PMINTENSET, PMINTENCLR)
>       *  (b) RO in PL0 (ie UNDEF on write), RW in PL1 (PMUSERENR)
>       *  (c) UNDEF in PL0 if PMUSERENR.EN==0, otherwise accessible (all 
> others)
> @@ -918,7 +1097,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
>       */
>      { .name = "PMCNTENSET", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 
> = 1,
>        .access = PL0_RW, .type = ARM_CP_ALIAS,
> -      .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten),
> +      .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
>        .writefn = pmcntenset_write,
>        .accessfn = pmreg_access,
>        .raw_writefn = raw_write },
> @@ -929,71 +1108,117 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
>        .writefn = pmcntenset_write, .raw_writefn = raw_write },
>      { .name = "PMCNTENCLR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 
> = 2,
>        .access = PL0_RW,
> -      .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten),
> +      .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
>        .accessfn = pmreg_access,
> -      .writefn = pmcntenclr_write,
> +      .writefn = pmcntenclr_write, .raw_writefn = raw_write,
>        .type = ARM_CP_ALIAS },
>      { .name = "PMCNTENCLR_EL0", .state = ARM_CP_STATE_AA64,
>        .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 2,
>        .access = PL0_RW, .accessfn = pmreg_access,
>        .type = ARM_CP_ALIAS,
>        .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
> -      .writefn = pmcntenclr_write },
> +      .writefn = pmcntenclr_write, .raw_writefn = raw_write },
> +    { .name = "PMOVSSET_EL0", .state = ARM_CP_STATE_AA64,
> +      .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 3,
> +      .access = PL0_RW, .accessfn = pmreg_access,
> +      .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), .resetvalue = 0,
> +      .writefn = pmovsset_write, .raw_writefn = raw_write },
>      { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3,
>        .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr),
> -      .accessfn = pmreg_access,
> +      .accessfn = pmreg_access, .type = ARM_CP_ALIAS,
> +      .writefn = pmovsr_write,
> +      .raw_writefn = raw_write },
> +    { .name = "PMOVSCLR_EL0", .state = ARM_CP_STATE_AA64,
> +      .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 3,
> +      .access = PL0_RW, .accessfn = pmreg_access,
> +      .type = ARM_CP_ALIAS,
> +      .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr),
>        .writefn = pmovsr_write,
>        .raw_writefn = raw_write },
>      /* Unimplemented so WI. */
>      { .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4,
> -      .access = PL0_W, .accessfn = pmreg_access, .type = ARM_CP_NOP },
> -    /* Since we don't implement any events, writing to PMSELR is 
> UNPREDICTABLE.
> -     * We choose to RAZ/WI.
> -     */
> +      .access = PL0_W, .accessfn = pmreg_access, .type = ARM_CP_NO_RAW,
> +      .writefn = pmswinc_write },
> +    { .name = "PMSWINC_EL0", .state = ARM_CP_STATE_AA64,
> +      .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 4,
> +      .access = PL0_W, .accessfn = pmreg_access, .type = ARM_CP_NO_RAW,
> +      .writefn = pmswinc_write },
>      { .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5,
> -      .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0,
> -      .accessfn = pmreg_access },
> -#ifndef CONFIG_USER_ONLY
> +      .access = PL0_RW, .type = ARM_CP_ALIAS,
> +      .accessfn = pmreg_access, .writefn = pmselr_write,
> +      .fieldoffset = offsetof(CPUARMState, cp15.c9_pmselr) },
> +    { .name = "PMSELR_EL0", .state = ARM_CP_STATE_AA64,
> +      .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 5,
> +      .access = PL0_RW, .accessfn = pmreg_access,
> +      .fieldoffset = offsetof(CPUARMState, cp15.c9_pmselr),
> +      .writefn = pmselr_write, .resetvalue = 0 },
> +    { .name = "PMXEVCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 
> 2,
> +      .access = PL0_RW, .type = ARM_CP_ALIAS,
> +      .accessfn = pmreg_access, .writefn = pmxevcntr_write,
> +      .readfn = pmxevcntr_read },
> +    { .name = "PMXEVCNTR_EL0", .state = ARM_CP_STATE_AA64,
> +      .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 2,
> +      .access = PL0_RW, .type = ARM_CP_ALIAS,
> +      .accessfn = pmreg_access, .writefn = pmxevcntr_write,
> +      .readfn = pmxevcntr_read },
> +    { .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 
> = 1,
> +      .access = PL0_RW, .type = ARM_CP_ALIAS,
> +      .accessfn = pmreg_access, .writefn = pmxevtyper_write,
> +      .readfn = pmxevtyper_read },
> +    { .name = "PMXEVTYPER_EL0", .state = ARM_CP_STATE_AA64,
> +      .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 1,
> +      .access = PL0_RW, .type = ARM_CP_ALIAS,
> +      .accessfn = pmreg_access, .writefn = pmxevtyper_write,
> +      .readfn = pmxevtyper_read },
>      { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0,
> -      .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_IO,
> +      .access = PL0_RW, .resetvalue = 0,
>        .readfn = pmccntr_read, .writefn = pmccntr_write32,
>        .accessfn = pmreg_access },
>      { .name = "PMCCNTR_EL0", .state = ARM_CP_STATE_AA64,
>        .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 0,
>        .access = PL0_RW, .accessfn = pmreg_access,
> -      .type = ARM_CP_IO,
> -      .readfn = pmccntr_read, .writefn = pmccntr_write, },
> -#endif
> +      .type = ARM_CP_ALIAS,
> +      .readfn = pmccntr_read, .writefn = pmccntr_write },
> +    { .name = "PMCCFILTR", .cp = 15, .crn = 14, .crm = 15, .opc1 = 0, .opc2 
> = 7,
> +      .access = PL0_RW, .accessfn = pmreg_access,
> +      .writefn = pmccfiltr_write,
> +      .fieldoffset = offsetof(CPUARMState, cp15.c14_pmccfiltr),
> +      .resetvalue = 0 },
>      { .name = "PMCCFILTR_EL0", .state = ARM_CP_STATE_AA64,
>        .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 15, .opc2 = 7,
>        .writefn = pmccfiltr_write,
>        .access = PL0_RW, .accessfn = pmreg_access,
> -      .type = ARM_CP_IO,
> -      .fieldoffset = offsetof(CPUARMState, cp15.pmccfiltr_el0),
> -      .resetvalue = 0, },
> -    { .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 
> = 1,
> -      .access = PL0_RW,
> -      .fieldoffset = offsetof(CPUARMState, cp15.c9_pmxevtyper),
> -      .accessfn = pmreg_access, .writefn = pmxevtyper_write,
> -      .raw_writefn = raw_write },
> -    /* Unimplemented, RAZ/WI. */
> -    { .name = "PMXEVCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 
> 2,
> -      .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0,
> -      .accessfn = pmreg_access },
> +      .type = ARM_CP_ALIAS,
> +      .fieldoffset = offsetof(CPUARMState, cp15.c14_pmccfiltr) },
>      { .name = "PMUSERENR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 
> 0,
> +      .access = PL0_R | PL1_RW, .type = ARM_CP_ALIAS,
> +      .fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr),
> +      .writefn = pmuserenr_write, .raw_writefn = raw_write },
> +    { .name = "PMUSERENR_EL0", .state = ARM_CP_STATE_AA64,
> +      .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 0,
>        .access = PL0_R | PL1_RW,
>        .fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr),
>        .resetvalue = 0,
>        .writefn = pmuserenr_write, .raw_writefn = raw_write },
>      { .name = "PMINTENSET", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 
> = 1,
> -      .access = PL1_RW,
> +      .access = PL1_RW, .type = ARM_CP_ALIAS,
>        .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
>        .resetvalue = 0,
>        .writefn = pmintenset_write, .raw_writefn = raw_write },
> +    { .name = "PMINTENSET_EL1", .state = ARM_CP_STATE_AA64,
> +      .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 1,
> +      .access = PL1_RW,
> +      .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), .resetvalue = 0,
> +      .writefn = pmintenset_write, .raw_writefn = raw_write },
>      { .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 
> = 2,
>        .access = PL1_RW, .type = ARM_CP_ALIAS,
>        .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
>        .resetvalue = 0, .writefn = pmintenclr_write, },
> +    { .name = "PMINTENCLR_EL1", .state = ARM_CP_STATE_AA64,
> +      .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 2,
> +      .access = PL1_RW, .type = ARM_CP_ALIAS,
> +      .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
> +      .writefn = pmintenclr_write },
>      { .name = "VBAR", .state = ARM_CP_STATE_BOTH,
>        .opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0,
>        .access = PL1_RW, .writefn = vbar_write,
> @@ -3024,6 +3249,7 @@ static void define_debug_regs(ARMCPU *cpu)
>
>  void register_cp_regs_for_features(ARMCPU *cpu)
>  {
> +    unsigned int i;
>      /* Register all the coprocessor registers based on feature bits */
>      CPUARMState *env = &cpu->env;
>      if (arm_feature(env, ARM_FEATURE_M)) {
> @@ -3120,15 +3346,14 @@ void register_cp_regs_for_features(ARMCPU *cpu)
>      }
>      if (arm_feature(env, ARM_FEATURE_V7)) {
>          /* v7 performance monitor control register: same implementor
> -         * field as main ID register, and we implement only the cycle
> -         * count register.
> +         * field as main ID register.
>           */
> -#ifndef CONFIG_USER_ONLY
>          ARMCPRegInfo pmcr = {
>              .name = "PMCR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 
> = 0,
>              .access = PL0_RW,
> -            .type = ARM_CP_IO | ARM_CP_ALIAS,
> -            .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcr),
> +            .type = ARM_CP_ALIAS,
> +            .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr),
> +            .resetvalue = (cpu->midr & 0xff000000) | (NUM_PMU_COUNTERS << 
> 11),
>              .accessfn = pmreg_access, .writefn = pmcr_write,
>              .raw_writefn = raw_write,
>          };
> @@ -3136,14 +3361,47 @@ void register_cp_regs_for_features(ARMCPU *cpu)
>              .name = "PMCR_EL0", .state = ARM_CP_STATE_AA64,
>              .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 0,
>              .access = PL0_RW, .accessfn = pmreg_access,
> -            .type = ARM_CP_IO,
>              .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr),
> -            .resetvalue = cpu->midr & 0xff000000,
> +            .resetvalue = (cpu->midr & 0xff000000) | (NUM_PMU_COUNTERS << 
> 11),
>              .writefn = pmcr_write, .raw_writefn = raw_write,
>          };
>          define_one_arm_cp_reg(cpu, &pmcr);
>          define_one_arm_cp_reg(cpu, &pmcr64);
> -#endif
> +
> +        for (i = 0; i < 31; i++) {
> +            char *pmevcntr_name = g_strdup_printf("PMEVCNTR%d", i);
> +            char *pmevcntr_el0_name = g_strdup_printf("PMEVCNTR%d_EL0", i);
> +            char *pmevtyper_name = g_strdup_printf("PMEVTYPER%d", i);
> +            char *pmevtyper_el0_name = g_strdup_printf("PMEVTYPER%d_EL0", i);
> +            ARMCPRegInfo pmcntr_regs[] = {
> +                { .name = pmevcntr_name, .cp = 15, .crn = 15,
> +                  .crm = 8 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7,
> +                  .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_ALIAS,
> +                  .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn,
> +                  .accessfn = pmreg_access },
> +                { .name = pmevcntr_el0_name, .state = ARM_CP_STATE_AA64,
> +                  .opc0 = 3, .opc1 = 3, .crn = 15, .crm = 8 | (3 & (i >> 3)),
> +                  .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access,
> +                  .resetvalue = 0,
> +                  .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn },
> +                { .name = pmevtyper_name, .cp = 15, .crn = 15,
> +                  .crm = 12 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7,
> +                  .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_ALIAS,
> +                  .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn,
> +                  .accessfn = pmreg_access },
> +                { .name = pmevtyper_el0_name, .state = ARM_CP_STATE_AA64,
> +                  .opc0 = 3, .opc1 = 3, .crn = 15, .crm = 12 | (3 & (i >> 
> 3)),
> +                  .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access,
> +                  .resetvalue = 0,
> +                  .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn },
> +                REGINFO_SENTINEL
> +            };
> +            define_arm_cp_regs(cpu, pmcntr_regs);
> +            g_free(pmevcntr_name);
> +            g_free(pmevcntr_el0_name);
> +            g_free(pmevtyper_name);
> +            g_free(pmevtyper_el0_name);
> +        }
>          ARMCPRegInfo clidr = {
>              .name = "CLIDR", .state = ARM_CP_STATE_BOTH,
>              .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 1,
> @@ -3169,16 +3427,19 @@ void register_cp_regs_for_features(ARMCPU *cpu)
>              { .name = "ID_AA64DFR0_EL1", .state = ARM_CP_STATE_AA64,
>                .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 0,
>                .access = PL1_R, .type = ARM_CP_CONST,
> -              /* We mask out the PMUVer field, because we don't currently
> -               * implement the PMU. Not advertising it prevents the guest
> -               * from trying to use it and getting UNDEFs on registers we
> -               * don't implement.
> -               */
> -              .resetvalue = cpu->id_aa64dfr0 & ~0xf00 },
> +              .resetvalue = cpu->id_aa64dfr0 },
>              { .name = "ID_AA64DFR1_EL1", .state = ARM_CP_STATE_AA64,
>                .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 1,
>                .access = PL1_R, .type = ARM_CP_CONST,
>                .resetvalue = cpu->id_aa64dfr1 },
> +            { .name = "PMCEID0_EL0", .state = ARM_CP_STATE_AA64,
> +              .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 6,
> +              .access = PL1_R, .type = ARM_CP_CONST,
> +              .resetvalue = cpu->pmceid0},
> +            { .name = "PMCEID1_EL0", .state = ARM_CP_STATE_AA64,
> +              .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 7,
> +              .access = PL1_R, .type = ARM_CP_CONST,
> +              .resetvalue = cpu->pmceid1},
>              { .name = "ID_AA64AFR0_EL1", .state = ARM_CP_STATE_AA64,
>                .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 4,
>                .access = PL1_R, .type = ARM_CP_CONST,
> @@ -4073,14 +4334,17 @@ void HELPER(context_check_pid)(CPUARMState *env)
>  void HELPER(update_instruction_count)(CPUARMState *env)
>  {
>      if (bbtrace_initialized()) {
> -      /*
> -       * If the bbv plugin is compiled in and enabled, we must account for 
> the
> -       * fact that bbv_profile needs to see prof_ic before we clear it.
> -       * However, it doesn't always clear the counter every time this gets
> -       * called, so we must keep track of the last value seen to ensure we
> -       * update the instruction counter correctly in that case.
> -       */
> -        increment_instruction_counters(env->prof_ic - env->prof_ic_last);
> +        /*
> +         * If the bbv plugin is compiled in and enabled, we must account for 
> the
> +         * fact that bbv_profile needs to see prof_ic before we clear it.
> +         * However, it doesn't always clear the counter every time this gets
> +         * called, so we must keep track of the last value seen to ensure we
> +         * update the instruction counter correctly in that case.
> +         */
> +        pmevcntr_increment(env, PMU_COUNTER_TYPE_INSTRUCTIONS,
> +                           env->prof_ic - env->prof_ic_last);
> +        pmevcntr_increment(env, PMU_COUNTER_TYPE_CYCLES,
> +                           env->prof_ic - env->prof_ic_last);
>          if (env->prof_pc && env->prof_is_jmp) {
>              // If this is the end of a basic block, zero out last_seen 
> counter too
>              env->prof_ic_last = 0;
> --
> Qualcomm Innovation Center, Inc.
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
>
>



reply via email to

[Prev in Thread] Current Thread [Next in Thread]