aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2012-04-14 16:37:40 +0000
committerPeter Maydell <peter.maydell@linaro.org>2012-04-14 16:37:40 +0000
commit254a0e0c8e9e525a8d0553506f1f85ae8ea616a5 (patch)
tree21350b8701559d86c4c803b3c55ee2429931ea85
parentf1732a0cf1ff0b3162b9ed4d391b607cd2f42e20 (diff)
target-arm: Convert performance monitor registers
Convert the v7 performance monitor cp15 registers to the new scheme. Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--target-arm/cpu.c4
-rw-r--r--target-arm/helper.c277
-rw-r--r--target-arm/translate.c26
3 files changed, 158 insertions, 149 deletions
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index eca686cbf1..43f252f0e4 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -132,10 +132,6 @@ static void arm_cpu_reset(CPUState *s)
}
env->vfp.xregs[ARM_VFP_FPEXC] = 0;
env->cp15.c2_base_mask = 0xffffc000u;
- /* v7 performance monitor control register: same implementor
- * field as main ID register, and we implement no event counters.
- */
- env->cp15.c9_pmcr = (cpu->midr & 0xff000000);
#endif
set_flush_to_zero(1, &env->vfp.standard_fp_status);
set_flush_inputs_to_zero(1, &env->vfp.standard_fp_status);
diff --git a/target-arm/helper.c b/target-arm/helper.c
index eec111c27b..c61e0de761 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -103,6 +103,97 @@ static const ARMCPRegInfo v6_cp_reginfo[] = {
REGINFO_SENTINEL
};
+static int pmreg_read(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t *value)
+{
+ /* Generic performance monitor register read function for where
+ * user access may be allowed by PMUSERENR.
+ */
+ if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
+ return EXCP_UDEF;
+ }
+ *value = CPREG_FIELD32(env, ri);
+ return 0;
+}
+
+static int pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
+ return EXCP_UDEF;
+ }
+ /* only the DP, X, D and E bits are writable */
+ env->cp15.c9_pmcr &= ~0x39;
+ env->cp15.c9_pmcr |= (value & 0x39);
+ return 0;
+}
+
+static int pmcntenset_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
+ return EXCP_UDEF;
+ }
+ value &= (1 << 31);
+ env->cp15.c9_pmcnten |= value;
+ return 0;
+}
+
+static int pmcntenclr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
+ return EXCP_UDEF;
+ }
+ value &= (1 << 31);
+ env->cp15.c9_pmcnten &= ~value;
+ return 0;
+}
+
+static int pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
+ return EXCP_UDEF;
+ }
+ env->cp15.c9_pmovsr &= ~value;
+ return 0;
+}
+
+static int pmxevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ if (arm_current_pl(env) == 0 && !env->cp15.c9_pmuserenr) {
+ return EXCP_UDEF;
+ }
+ env->cp15.c9_pmxevtyper = value & 0xff;
+ return 0;
+}
+
+static int pmuserenr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ env->cp15.c9_pmuserenr = value & 1;
+ return 0;
+}
+
+static int 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);
+ env->cp15.c9_pminten |= value;
+ return 0;
+}
+
+static int pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ value &= (1 << 31);
+ env->cp15.c9_pminten &= ~value;
+ return 0;
+}
+
static const ARMCPRegInfo v7_cp_reginfo[] = {
/* DBGDRAR, DBGDSAR: always RAZ since we don't implement memory mapped
* debug components
@@ -114,6 +205,62 @@ 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:
+ * (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)
+ * For the cases controlled by PMUSERENR we must set .access to PL0_RW
+ * or PL0_RO as appropriate and then check PMUSERENR in the helper fn.
+ */
+ { .name = "PMCNTENSET", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 1,
+ .access = PL0_RW, .resetvalue = 0,
+ .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
+ .readfn = pmreg_read, .writefn = pmcntenset_write },
+ { .name = "PMCNTENCLR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 2,
+ .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten),
+ .readfn = pmreg_read, .writefn = pmcntenclr_write },
+ { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3,
+ .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr),
+ .readfn = pmreg_read, .writefn = pmovsr_write },
+ /* Unimplemented so WI. Strictly speaking write accesses in PL0 should
+ * respect PMUSERENR.
+ */
+ { .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4,
+ .access = PL0_W, .type = ARM_CP_NOP },
+ /* Since we don't implement any events, writing to PMSELR is UNPREDICTABLE.
+ * We choose to RAZ/WI. XXX should respect PMUSERENR.
+ */
+ { .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5,
+ .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+ /* Unimplemented, RAZ/WI. XXX PMUSERENR */
+ { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0,
+ .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+ { .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 1,
+ .access = PL0_RW,
+ .fieldoffset = offsetof(CPUARMState, cp15.c9_pmxevtyper),
+ .readfn = pmreg_read, .writefn = pmxevtyper_write },
+ /* Unimplemented, RAZ/WI. XXX PMUSERENR */
+ { .name = "PMXEVCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 2,
+ .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+ { .name = "PMUSERENR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 0,
+ .access = PL0_R | PL1_RW,
+ .fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr),
+ .resetvalue = 0,
+ .writefn = pmuserenr_write },
+ { .name = "PMINTENSET", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 1,
+ .access = PL1_RW,
+ .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
+ .resetvalue = 0,
+ .writefn = pmintenset_write },
+ { .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2,
+ .access = PL1_RW,
+ .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
+ .resetvalue = 0,
+ .writefn = pmintenclr_write },
REGINFO_SENTINEL
};
@@ -195,6 +342,16 @@ void register_cp_regs_for_features(ARMCPU *cpu)
define_arm_cp_regs(env, v6k_cp_reginfo);
}
if (arm_feature(env, ARM_FEATURE_V7)) {
+ /* v7 performance monitor control register: same implementor
+ * field as main ID register, and we implement no event counters.
+ */
+ ARMCPRegInfo pmcr = {
+ .name = "PMCR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 0,
+ .access = PL0_RW, .resetvalue = cpu->midr & 0xff000000,
+ .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr),
+ .readfn = pmreg_read, .writefn = pmcr_write
+ };
+ define_one_arm_cp_reg(env, &pmcr);
define_arm_cp_regs(env, v7_cp_reginfo);
} else {
define_arm_cp_regs(env, not_v7_cp_reginfo);
@@ -1521,81 +1678,6 @@ void HELPER(set_cp15)(CPUARMState *env, uint32_t insn, uint32_t val)
case 1: /* TCM memory region registers. */
/* Not implemented. */
goto bad_reg;
- case 12: /* Performance monitor control */
- /* 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)
- */
- if (!arm_feature(env, ARM_FEATURE_V7)) {
- goto bad_reg;
- }
- switch (op2) {
- case 0: /* performance monitor control register */
- /* only the DP, X, D and E bits are writable */
- env->cp15.c9_pmcr &= ~0x39;
- env->cp15.c9_pmcr |= (val & 0x39);
- break;
- case 1: /* Count enable set register */
- val &= (1 << 31);
- env->cp15.c9_pmcnten |= val;
- break;
- case 2: /* Count enable clear */
- val &= (1 << 31);
- env->cp15.c9_pmcnten &= ~val;
- break;
- case 3: /* Overflow flag status */
- env->cp15.c9_pmovsr &= ~val;
- break;
- case 4: /* Software increment */
- /* RAZ/WI since we don't implement the software-count event */
- break;
- case 5: /* Event counter selection register */
- /* Since we don't implement any events, writing to this register
- * is actually UNPREDICTABLE. So we choose to RAZ/WI.
- */
- break;
- default:
- goto bad_reg;
- }
- break;
- case 13: /* Performance counters */
- if (!arm_feature(env, ARM_FEATURE_V7)) {
- goto bad_reg;
- }
- switch (op2) {
- case 0: /* Cycle count register: not implemented, so RAZ/WI */
- break;
- case 1: /* Event type select */
- env->cp15.c9_pmxevtyper = val & 0xff;
- break;
- case 2: /* Event count register */
- /* Unimplemented (we have no events), RAZ/WI */
- break;
- default:
- goto bad_reg;
- }
- break;
- case 14: /* Performance monitor control */
- if (!arm_feature(env, ARM_FEATURE_V7)) {
- goto bad_reg;
- }
- switch (op2) {
- case 0: /* user enable */
- env->cp15.c9_pmuserenr = val & 1;
- /* changes access rights for cp registers, so flush tbs */
- tb_flush(env);
- break;
- case 1: /* interrupt enable set */
- /* We have no event counters so only the C bit can be changed */
- val &= (1 << 31);
- env->cp15.c9_pminten |= val;
- break;
- case 2: /* interrupt enable clear */
- val &= (1 << 31);
- env->cp15.c9_pminten &= ~val;
- break;
- }
- break;
default:
goto bad_reg;
}
@@ -1952,51 +2034,6 @@ uint32_t HELPER(get_cp15)(CPUARMState *env, uint32_t insn)
goto bad_reg;
}
break;
- case 12: /* Performance monitor control */
- if (!arm_feature(env, ARM_FEATURE_V7)) {
- goto bad_reg;
- }
- switch (op2) {
- case 0: /* performance monitor control register */
- return env->cp15.c9_pmcr;
- case 1: /* count enable set */
- case 2: /* count enable clear */
- return env->cp15.c9_pmcnten;
- case 3: /* overflow flag status */
- return env->cp15.c9_pmovsr;
- case 4: /* software increment */
- case 5: /* event counter selection register */
- return 0; /* Unimplemented, RAZ/WI */
- default:
- goto bad_reg;
- }
- case 13: /* Performance counters */
- if (!arm_feature(env, ARM_FEATURE_V7)) {
- goto bad_reg;
- }
- switch (op2) {
- case 1: /* Event type select */
- return env->cp15.c9_pmxevtyper;
- case 0: /* Cycle count register */
- case 2: /* Event count register */
- /* Unimplemented, so RAZ/WI */
- return 0;
- default:
- goto bad_reg;
- }
- case 14: /* Performance monitor control */
- if (!arm_feature(env, ARM_FEATURE_V7)) {
- goto bad_reg;
- }
- switch (op2) {
- case 0: /* user enable */
- return env->cp15.c9_pmuserenr;
- case 1: /* interrupt enable set */
- case 2: /* interrupt enable clear */
- return env->cp15.c9_pminten;
- default:
- goto bad_reg;
- }
default:
goto bad_reg;
}
diff --git a/target-arm/translate.c b/target-arm/translate.c
index fc8dbf8539..6f7932f3bb 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -2439,30 +2439,6 @@ static int disas_dsp_insn(CPUARMState *env, DisasContext *s, uint32_t insn)
return 1;
}
-static int cp15_user_ok(CPUARMState *env, uint32_t insn)
-{
- int cpn = (insn >> 16) & 0xf;
- int cpm = insn & 0xf;
- int op = ((insn >> 5) & 7) | ((insn >> 18) & 0x38);
-
- if (arm_feature(env, ARM_FEATURE_V7) && cpn == 9) {
- /* Performance monitor registers fall into three categories:
- * (a) always UNDEF in usermode
- * (b) UNDEF only if PMUSERENR.EN is 0
- * (c) always read OK and UNDEF on write (PMUSERENR only)
- */
- if ((cpm == 12 && (op < 6)) ||
- (cpm == 13 && (op < 3))) {
- return env->cp15.c9_pmuserenr;
- } else if (cpm == 14 && op == 0 && (insn & ARM_CP_RW_BIT)) {
- /* PMUSERENR, read only */
- return 1;
- }
- return 0;
- }
- return 0;
-}
-
/* Disassemble system coprocessor (cp15) instruction. Return nonzero if
instruction is not defined. */
static int disas_cp15_insn(CPUARMState *env, DisasContext *s, uint32_t insn)
@@ -2487,7 +2463,7 @@ static int disas_cp15_insn(CPUARMState *env, DisasContext *s, uint32_t insn)
return 1;
}
- if (IS_USER(s) && !cp15_user_ok(env, insn)) {
+ if (IS_USER(s)) {
return 1;
}