Commit 033614c4 authored by Aaron Lindsay's avatar Aaron Lindsay Committed by Peter Maydell
Browse files

target/arm: Filter cycle counter based on PMCCFILTR_EL0



Rename arm_ccnt_enabled to pmu_counter_enabled, and add logic to only
return 'true' if the specified counter is enabled and neither prohibited
or filtered.

Signed-off-by: default avatarAaron Lindsay <alindsay@codeaurora.org>
Signed-off-by: default avatarAaron Lindsay <aclindsa@gmail.com>
Reviewed-by: default avatarPeter Maydell <peter.maydell@linaro.org>
Reviewed-by: default avatarRichard Henderson <richard.henderson@linaro.org>
Message-id: 20181211151945.29137-5-aaron@os.amperecomputing.com
Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parent 980ebe87
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -1038,6 +1038,9 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
    if (!cpu->has_pmu) {
        unset_feature(env, ARM_FEATURE_PMU);
        cpu->id_aa64dfr0 &= ~0xf00;
    } else if (!kvm_enabled()) {
        arm_register_pre_el_change_hook(cpu, &pmu_pre_el_change, 0);
        arm_register_el_change_hook(cpu, &pmu_post_el_change, 0);
    }

    if (!arm_feature(env, ARM_FEATURE_EL2)) {
+9 −1
Original line number Diff line number Diff line
@@ -1002,6 +1002,12 @@ void pmccntr_op_finish(CPUARMState *env);
void pmu_op_start(CPUARMState *env);
void pmu_op_finish(CPUARMState *env);

/**
 * Functions to register as EL change hooks for PMU mode filtering
 */
void pmu_pre_el_change(ARMCPU *cpu, void *ignored);
void pmu_post_el_change(ARMCPU *cpu, void *ignored);

/* 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
@@ -1084,7 +1090,8 @@ void pmu_op_finish(CPUARMState *env);

#define MDCR_EPMAD    (1U << 21)
#define MDCR_EDAD     (1U << 20)
#define MDCR_SPME     (1U << 17)
#define MDCR_SPME     (1U << 17)  /* MDCR_EL3 */
#define MDCR_HPMD     (1U << 17)  /* MDCR_EL2 */
#define MDCR_SDD      (1U << 16)
#define MDCR_SPD      (3U << 14)
#define MDCR_TDRA     (1U << 11)
@@ -1094,6 +1101,7 @@ void pmu_op_finish(CPUARMState *env);
#define MDCR_HPME     (1U << 7)
#define MDCR_TPM      (1U << 6)
#define MDCR_TPMCR    (1U << 5)
#define MDCR_HPMN     (0x1fU)

/* Not all of the MDCR_EL3 bits are present in the 32-bit SDCR */
#define SDCR_VALID_MASK (MDCR_EPMAD | MDCR_EDAD | MDCR_SPME | MDCR_SPD)
+89 −7
Original line number Diff line number Diff line
@@ -976,10 +976,24 @@ static const ARMCPRegInfo v6_cp_reginfo[] = {
/* Definitions for the PMU registers */
#define PMCRN_MASK  0xf800
#define PMCRN_SHIFT 11
#define PMCRDP  0x10
#define PMCRD   0x8
#define PMCRC   0x4
#define PMCRE   0x1

#define PMXEVTYPER_P          0x80000000
#define PMXEVTYPER_U          0x40000000
#define PMXEVTYPER_NSK        0x20000000
#define PMXEVTYPER_NSU        0x10000000
#define PMXEVTYPER_NSH        0x08000000
#define PMXEVTYPER_M          0x04000000
#define PMXEVTYPER_MT         0x02000000
#define PMXEVTYPER_EVTCOUNT   0x0000ffff
#define PMXEVTYPER_MASK       (PMXEVTYPER_P | PMXEVTYPER_U | PMXEVTYPER_NSK | \
                               PMXEVTYPER_NSU | PMXEVTYPER_NSH | \
                               PMXEVTYPER_M | PMXEVTYPER_MT | \
                               PMXEVTYPER_EVTCOUNT)

static inline uint32_t pmu_num_counters(CPUARMState *env)
{
  return (env->cp15.c9_pmcr & PMCRN_MASK) >> PMCRN_SHIFT;
@@ -1075,16 +1089,66 @@ static CPAccessResult pmreg_access_ccntr(CPUARMState *env,
    return pmreg_access(env, ri, isread);
}

static inline bool arm_ccnt_enabled(CPUARMState *env)
/* Returns true if the counter (pass 31 for PMCCNTR) should count events using
 * the current EL, security state, and register configuration.
 */
static bool pmu_counter_enabled(CPUARMState *env, uint8_t counter)
{
    /* This does not support checking PMCCFILTR_EL0 register */
    uint64_t filter;
    bool e, p, u, nsk, nsu, nsh, m;
    bool enabled, prohibited, filtered;
    bool secure = arm_is_secure(env);
    int el = arm_current_el(env);
    uint8_t hpmn = env->cp15.mdcr_el2 & MDCR_HPMN;

    if (!(env->cp15.c9_pmcr & PMCRE) || !(env->cp15.c9_pmcnten & (1 << 31))) {
        return false;
    if (!arm_feature(env, ARM_FEATURE_EL2) ||
            (counter < hpmn || counter == 31)) {
        e = env->cp15.c9_pmcr & PMCRE;
    } else {
        e = env->cp15.mdcr_el2 & MDCR_HPME;
    }
    enabled = e && (env->cp15.c9_pmcnten & (1 << counter));

    return true;
    if (!secure) {
        if (el == 2 && (counter < hpmn || counter == 31)) {
            prohibited = env->cp15.mdcr_el2 & MDCR_HPMD;
        } else {
            prohibited = false;
        }
    } else {
        prohibited = arm_feature(env, ARM_FEATURE_EL3) &&
           (env->cp15.mdcr_el3 & MDCR_SPME);
    }

    if (prohibited && counter == 31) {
        prohibited = env->cp15.c9_pmcr & PMCRDP;
    }

    /* TODO Remove assert, set filter to correct PMEVTYPER */
    assert(counter == 31);
    filter = env->cp15.pmccfiltr_el0;

    p   = filter & PMXEVTYPER_P;
    u   = filter & PMXEVTYPER_U;
    nsk = arm_feature(env, ARM_FEATURE_EL3) && (filter & PMXEVTYPER_NSK);
    nsu = arm_feature(env, ARM_FEATURE_EL3) && (filter & PMXEVTYPER_NSU);
    nsh = arm_feature(env, ARM_FEATURE_EL2) && (filter & PMXEVTYPER_NSH);
    m   = arm_el_is_aa64(env, 1) &&
              arm_feature(env, ARM_FEATURE_EL3) && (filter & PMXEVTYPER_M);

    if (el == 0) {
        filtered = secure ? u : u != nsu;
    } else if (el == 1) {
        filtered = secure ? p : p != nsk;
    } else if (el == 2) {
        filtered = !nsh;
    } else { /* EL3 */
        filtered = m != p;
    }

    return enabled && !prohibited && !filtered;
}

/*
 * Ensure c15_ccnt is the guest-visible count so that operations such as
 * enabling/disabling the counter or filtering, modifying the count itself,
@@ -1097,7 +1161,7 @@ void pmccntr_op_start(CPUARMState *env)
    cycles = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
                          ARM_CPU_FREQ, NANOSECONDS_PER_SECOND);

    if (arm_ccnt_enabled(env)) {
    if (pmu_counter_enabled(env, 31)) {
        uint64_t eff_cycles = cycles;
        if (env->cp15.c9_pmcr & PMCRD) {
            /* Increment once every 64 processor clock cycles */
@@ -1116,7 +1180,7 @@ void pmccntr_op_start(CPUARMState *env)
 */
void pmccntr_op_finish(CPUARMState *env)
{
    if (arm_ccnt_enabled(env)) {
    if (pmu_counter_enabled(env, 31)) {
        uint64_t prev_cycles = env->cp15.c15_ccnt_delta;

        if (env->cp15.c9_pmcr & PMCRD) {
@@ -1138,6 +1202,16 @@ void pmu_op_finish(CPUARMState *env)
    pmccntr_op_finish(env);
}

void pmu_pre_el_change(ARMCPU *cpu, void *ignored)
{
    pmu_op_start(&cpu->env);
}

void pmu_post_el_change(ARMCPU *cpu, void *ignored)
{
    pmu_op_finish(&cpu->env);
}

static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
                       uint64_t value)
{
@@ -1209,6 +1283,14 @@ void pmu_op_finish(CPUARMState *env)
{
}

void pmu_pre_el_change(ARMCPU *cpu, void *ignored)
{
}

void pmu_post_el_change(ARMCPU *cpu, void *ignored)
{
}

#endif

static void pmccfiltr_write(CPUARMState *env, const ARMCPRegInfo *ri,