[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [RFC 09/14] Implement remaining PMU functionality
From: |
Christopher Covington |
Subject: |
[Qemu-devel] [RFC 09/14] Implement remaining PMU functionality |
Date: |
Wed, 5 Aug 2015 12:51:18 -0400 |
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 <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
- [Qemu-devel] [RFC 04/14] Modify load exclusive/store exclusive to use physical addresses with the monitor, (continued)
- [Qemu-devel] [RFC 04/14] Modify load exclusive/store exclusive to use physical addresses with the monitor, Christopher Covington, 2015/08/05
- [Qemu-devel] [RFC 02/14] Added semihosting support for A64 in full-system mode, Christopher Covington, 2015/08/05
- [Qemu-devel] [RFC 05/14] Fixed TLB invalidate ops., Christopher Covington, 2015/08/05
- [Qemu-devel] [RFC 12/14] bbvec: Detect mode changes after uncached_cpsr update, Christopher Covington, 2015/08/05
- [Qemu-devel] [RFC 10/14] bbvec: Move mode/PID change detection to register writes, Christopher Covington, 2015/08/05
- [Qemu-devel] [RFC 07/14] Add PMU to ARM virt platform, Christopher Covington, 2015/08/05
- [Qemu-devel] [RFC 08/14] Add instruction-counting infrastructure to target-arm, Christopher Covington, 2015/08/05
- [Qemu-devel] [RFC 13/14] Enable negative icount values for QEMU., Christopher Covington, 2015/08/05
- [Qemu-devel] [RFC 11/14] Print bbvec stats on 'magic' exceptions, Christopher Covington, 2015/08/05
- [Qemu-devel] [RFC 09/14] Implement remaining PMU functionality,
Christopher Covington <=
- [Qemu-devel] [RFC 06/14] Added support for block profiling for AArch32 and Aarch64, Christopher Covington, 2015/08/05
- [Qemu-devel] [RFC 14/14] bbvec: Properly detect conditional thumb2 branching instructions, Christopher Covington, 2015/08/05
- Re: [Qemu-devel] RFC: ARM Semihosting, PMU, and BBV Changes, Peter Maydell, 2015/08/11