This adds logic to increment PMEVCNTR's based on different event inputs, implements all remaining CP registers, and triggers an interrupt on event overflow.
Written by Aaron Lindsay. Signed-off-by: Christopher Covington <c...@codeaurora.org> --- 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