Set up MDCR_EL2 to handle a Partitioned PMU. If partitioned, set the HPME, HPMD, and HCCD bits. If we have the ability to use Fine Grain Traps (FEAT_FGT) also, unset the TPM and TPMCR bits that trap all PMU accesses and set HPMN to the correct number of guest counters so hardware enforces the right values.
Signed-off-by: Colton Lewis <[email protected]> --- arch/arm64/kvm/debug.c | 27 ++++++++++++++++++++++++--- arch/arm64/kvm/pmu.c | 7 +++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c index f4d7b12045e8f..c84321277d893 100644 --- a/arch/arm64/kvm/debug.c +++ b/arch/arm64/kvm/debug.c @@ -43,14 +43,35 @@ static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu) * This also clears MDCR_EL2_E2PB_MASK and MDCR_EL2_E2TB_MASK * to disable guest access to the profiling and trace buffers */ - vcpu->arch.mdcr_el2 = FIELD_PREP(MDCR_EL2_HPMN, - *host_data_ptr(nr_event_counters)); + + vcpu->arch.mdcr_el2 = FIELD_PREP(MDCR_EL2_HPMN, *host_data_ptr(nr_event_counters)); vcpu->arch.mdcr_el2 |= (MDCR_EL2_TPM | MDCR_EL2_TPMS | MDCR_EL2_TTRF | MDCR_EL2_TPMCR | MDCR_EL2_TDRA | - MDCR_EL2_TDOSA); + MDCR_EL2_TDOSA | + MDCR_EL2_HPME); + + if (kvm_pmu_is_partitioned(vcpu->kvm)) { + u8 nr_guest_cntr = vcpu->kvm->arch.nr_pmu_counters; + + vcpu->arch.mdcr_el2 |= (MDCR_EL2_HPMD | MDCR_EL2_HCCD); + + /* + * Take out the coarse grain traps if we are using + * fine grain traps and enforce counter access with + * HPMN. + */ + if (!vcpu_on_unsupported_cpu(vcpu) && + cpus_have_final_cap(ARM64_HAS_FGT) && + (cpus_have_final_cap(ARM64_HAS_HPMN0) || nr_guest_cntr > 0)) { + vcpu->arch.mdcr_el2 &= ~(MDCR_EL2_TPM | MDCR_EL2_TPMCR | MDCR_EL2_HPMN); + vcpu->arch.mdcr_el2 |= FIELD_PREP(MDCR_EL2_HPMN, nr_guest_cntr); + } + + + } /* Is the VM being debugged by userspace? */ if (vcpu->guest_debug) diff --git a/arch/arm64/kvm/pmu.c b/arch/arm64/kvm/pmu.c index 9ad3520417413..55cda8021400a 100644 --- a/arch/arm64/kvm/pmu.c +++ b/arch/arm64/kvm/pmu.c @@ -552,6 +552,13 @@ u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm) if (cpus_have_final_cap(ARM64_WORKAROUND_PMUV3_IMPDEF_TRAPS)) return 1; + /* + * If partitioned then we are limited by the max counters in + * the guest partition. + */ + if (pmu_is_partitioned(arm_pmu)) + return arm_pmu->max_guest_counters; + /* * The arm_pmu->cntr_mask considers the fixed counter(s) as well. * Ignore those and return only the general-purpose counters. -- 2.54.0.1136.gdb2ca164c4-goog

