diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2012-06-20 11:57:12 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2012-06-20 12:05:17 +0000 |
commit | 200ac0ef87d3d076ccd5d738e4d1acb7f7b2879f (patch) | |
tree | 840674296bec9958298f309ee857e6a5add5a07b /target-arm | |
parent | 4d31c5967979d11c22c69a52beb1a03cab377e4f (diff) | |
download | qemu-200ac0ef87d3d076ccd5d738e4d1acb7f7b2879f.zip qemu-200ac0ef87d3d076ccd5d738e4d1acb7f7b2879f.tar.gz qemu-200ac0ef87d3d076ccd5d738e4d1acb7f7b2879f.tar.bz2 |
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>
Diffstat (limited to 'target-arm')
-rw-r--r-- | target-arm/cpu.c | 4 | ||||
-rw-r--r-- | target-arm/helper.c | 277 | ||||
-rw-r--r-- | target-arm/translate.c | 26 |
3 files changed, 158 insertions, 149 deletions
diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 1aff266..0098787 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 d4c8a1c..ebe6a55 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -98,6 +98,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 @@ -109,6 +200,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 }; @@ -189,6 +336,16 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, 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(cpu, &pmcr); define_arm_cp_regs(cpu, v7_cp_reginfo); } else { define_arm_cp_regs(cpu, not_v7_cp_reginfo); @@ -1533,81 +1690,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; } @@ -1964,51 +2046,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 e6b0d87..f4e9435 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; } |