diff options
Diffstat (limited to 'target-arm/helper.c')
| -rw-r--r-- | target-arm/helper.c | 138 |
1 files changed, 102 insertions, 36 deletions
diff --git a/target-arm/helper.c b/target-arm/helper.c index 2a77c97c7b..2b95f33872 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -548,25 +548,40 @@ static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri) } #ifndef CONFIG_USER_ONLY -static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) + +static inline bool arm_ccnt_enabled(CPUARMState *env) { - /* Don't computer the number of ticks in user mode */ - uint32_t temp_ticks; + /* This does not support checking PMCCFILTR_EL0 register */ - temp_ticks = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) * - get_ticks_per_sec() / 1000000; + if (!(env->cp15.c9_pmcr & PMCRE)) { + return false; + } - if (env->cp15.c9_pmcr & PMCRE) { - /* If the counter is enabled */ - if (env->cp15.c9_pmcr & PMCRD) { - /* Increment once every 64 processor clock cycles */ - env->cp15.c15_ccnt = (temp_ticks/64) - env->cp15.c15_ccnt; - } else { - env->cp15.c15_ccnt = temp_ticks - env->cp15.c15_ccnt; - } + return true; +} + +void pmccntr_sync(CPUARMState *env) +{ + uint64_t temp_ticks; + + 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; } + if (arm_ccnt_enabled(env)) { + env->cp15.c15_ccnt = temp_ticks - env->cp15.c15_ccnt; + } +} + +static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + pmccntr_sync(env); + if (value & PMCRC) { /* The counter has been reset */ env->cp15.c15_ccnt = 0; @@ -576,26 +591,20 @@ static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri, env->cp15.c9_pmcr &= ~0x39; env->cp15.c9_pmcr |= (value & 0x39); - if (env->cp15.c9_pmcr & PMCRE) { - if (env->cp15.c9_pmcr & PMCRD) { - /* Increment once every 64 processor clock cycles */ - temp_ticks /= 64; - } - env->cp15.c15_ccnt = temp_ticks - env->cp15.c15_ccnt; - } + pmccntr_sync(env); } static uint64_t pmccntr_read(CPUARMState *env, const ARMCPRegInfo *ri) { - uint32_t total_ticks; + uint64_t total_ticks; - if (!(env->cp15.c9_pmcr & PMCRE)) { + if (!arm_ccnt_enabled(env)) { /* Counter is disabled, do not change value */ return env->cp15.c15_ccnt; } - total_ticks = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) * - get_ticks_per_sec() / 1000000; + total_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 */ @@ -607,16 +616,16 @@ static uint64_t pmccntr_read(CPUARMState *env, const ARMCPRegInfo *ri) static void pmccntr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - uint32_t total_ticks; + uint64_t total_ticks; - if (!(env->cp15.c9_pmcr & PMCRE)) { + if (!arm_ccnt_enabled(env)) { /* Counter is disabled, set the absolute value */ env->cp15.c15_ccnt = value; return; } - total_ticks = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) * - get_ticks_per_sec() / 1000000; + total_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 */ @@ -624,8 +633,31 @@ static void pmccntr_write(CPUARMState *env, const ARMCPRegInfo *ri, } env->cp15.c15_ccnt = total_ticks - value; } + +static void pmccntr_write32(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint64_t cur_val = pmccntr_read(env, NULL); + + pmccntr_write(env, ri, deposit64(cur_val, 0, 32, value)); +} + +#else /* CONFIG_USER_ONLY */ + +void pmccntr_sync(CPUARMState *env) +{ +} + #endif +static void pmccfiltr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + pmccntr_sync(env); + env->cp15.pmccfiltr_el0 = value & 0x7E000000; + pmccntr_sync(env); +} + static void pmcntenset_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -728,16 +760,28 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { * 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), + .access = PL0_RW, .type = ARM_CP_NO_MIGRATE, + .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten), .writefn = pmcntenset_write, .accessfn = pmreg_access, .raw_writefn = raw_write }, + { .name = "PMCNTENSET_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 1, + .access = PL0_RW, .accessfn = pmreg_access, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), .resetvalue = 0, + .writefn = pmcntenset_write, .raw_writefn = raw_write }, { .name = "PMCNTENCLR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 2, - .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), + .access = PL0_RW, + .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten), .accessfn = pmreg_access, .writefn = pmcntenclr_write, .type = ARM_CP_NO_MIGRATE }, + { .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_NO_MIGRATE, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), + .writefn = pmcntenclr_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, @@ -755,9 +799,21 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { #ifndef CONFIG_USER_ONLY { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0, .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_IO, - .readfn = pmccntr_read, .writefn = pmccntr_write, + .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 + { .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), @@ -2386,13 +2442,23 @@ void register_cp_regs_for_features(ARMCPU *cpu) #ifndef CONFIG_USER_ONLY ARMCPRegInfo pmcr = { .name = "PMCR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 0, - .access = PL0_RW, .resetvalue = cpu->midr & 0xff000000, - .type = ARM_CP_IO, - .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr), + .access = PL0_RW, + .type = ARM_CP_IO | ARM_CP_NO_MIGRATE, + .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcr), .accessfn = pmreg_access, .writefn = pmcr_write, .raw_writefn = raw_write, }; + ARMCPRegInfo pmcr64 = { + .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, + .writefn = pmcr_write, .raw_writefn = raw_write, + }; define_one_arm_cp_reg(cpu, &pmcr); + define_one_arm_cp_reg(cpu, &pmcr64); #endif ARMCPRegInfo clidr = { .name = "CLIDR", .state = ARM_CP_STATE_BOTH, |