Skip to content

Commit 1b1d1b1

Browse files
committed
Merge branch 'kvm-arm64/pmuv3-asahi' into kvmarm/next
* kvm-arm64/pmuv3-asahi: : Support PMUv3 for KVM guests on Apple silicon : : Take advantage of some IMPLEMENTATION DEFINED traps available on Apple : parts to trap-and-emulate the PMUv3 registers on behalf of a KVM guest. : Constrain the vPMU to a cycle counter and single event counter, as the : Apple PMU has events that cannot be counted on every counter. : : There is a small new interface between the ARM PMU driver and KVM, where : the PMU driver owns the PMUv3 -> hardware event mappings. arm64: Enable IMP DEF PMUv3 traps on Apple M* KVM: arm64: Provide 1 event counter on IMPDEF hardware drivers/perf: apple_m1: Provide helper for mapping PMUv3 events KVM: arm64: Remap PMUv3 events onto hardware KVM: arm64: Advertise PMUv3 if IMPDEF traps are present KVM: arm64: Compute synthetic sysreg ESR for Apple PMUv3 traps KVM: arm64: Move PMUVer filtering into KVM code KVM: arm64: Use guard() to cleanup usage of arm_pmus_lock KVM: arm64: Drop kvm_arm_pmu_available static key KVM: arm64: Use a cpucap to determine if system supports FEAT_PMUv3 KVM: arm64: Always support SW_INCR PMU event KVM: arm64: Compute PMCEID from arm_pmu's event bitmaps drivers/perf: apple_m1: Support host/guest event filtering drivers/perf: apple_m1: Refactor event select/filter configuration Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
2 parents d300b01 + e1231aa commit 1b1d1b1

File tree

15 files changed

+303
-104
lines changed

15 files changed

+303
-104
lines changed

arch/arm64/include/asm/apple_m1_pmu.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#define PMCR0_PMI_ENABLE_8_9 GENMASK(45, 44)
3838

3939
#define SYS_IMP_APL_PMCR1_EL1 sys_reg(3, 1, 15, 1, 0)
40+
#define SYS_IMP_APL_PMCR1_EL12 sys_reg(3, 1, 15, 7, 2)
4041
#define PMCR1_COUNT_A64_EL0_0_7 GENMASK(15, 8)
4142
#define PMCR1_COUNT_A64_EL1_0_7 GENMASK(23, 16)
4243
#define PMCR1_COUNT_A64_EL0_8_9 GENMASK(41, 40)

arch/arm64/include/asm/cpucaps.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ cpucap_is_possible(const unsigned int cap)
7171
* KVM MPAM support doesn't rely on the host kernel supporting MPAM.
7272
*/
7373
return true;
74+
case ARM64_HAS_PMUV3:
75+
return IS_ENABLED(CONFIG_HW_PERF_EVENTS);
7476
}
7577

7678
return true;

arch/arm64/include/asm/cpufeature.h

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -525,29 +525,6 @@ cpuid_feature_extract_unsigned_field(u64 features, int field)
525525
return cpuid_feature_extract_unsigned_field_width(features, field, 4);
526526
}
527527

528-
/*
529-
* Fields that identify the version of the Performance Monitors Extension do
530-
* not follow the standard ID scheme. See ARM DDI 0487E.a page D13-2825,
531-
* "Alternative ID scheme used for the Performance Monitors Extension version".
532-
*/
533-
static inline u64 __attribute_const__
534-
cpuid_feature_cap_perfmon_field(u64 features, int field, u64 cap)
535-
{
536-
u64 val = cpuid_feature_extract_unsigned_field(features, field);
537-
u64 mask = GENMASK_ULL(field + 3, field);
538-
539-
/* Treat IMPLEMENTATION DEFINED functionality as unimplemented */
540-
if (val == ID_AA64DFR0_EL1_PMUVer_IMP_DEF)
541-
val = 0;
542-
543-
if (val > cap) {
544-
features &= ~mask;
545-
features |= (cap << field) & mask;
546-
}
547-
548-
return features;
549-
}
550-
551528
static inline u64 arm64_ftr_mask(const struct arm64_ftr_bits *ftrp)
552529
{
553530
return (u64)GENMASK(ftrp->shift + ftrp->width - 1, ftrp->shift);
@@ -866,6 +843,11 @@ static __always_inline bool system_supports_mpam_hcr(void)
866843
return alternative_has_cap_unlikely(ARM64_MPAM_HCR);
867844
}
868845

846+
static inline bool system_supports_pmuv3(void)
847+
{
848+
return cpus_have_final_cap(ARM64_HAS_PMUV3);
849+
}
850+
869851
int do_emulate_mrs(struct pt_regs *regs, u32 sys_reg, u32 rt);
870852
bool try_emulate_mrs(struct pt_regs *regs, u32 isn);
871853

arch/arm64/kernel/cpu_errata.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,43 @@ has_neoverse_n1_erratum_1542419(const struct arm64_cpu_capabilities *entry,
247247
return is_midr_in_range(&range) && has_dic;
248248
}
249249

250+
static const struct midr_range impdef_pmuv3_cpus[] = {
251+
MIDR_ALL_VERSIONS(MIDR_APPLE_M1_ICESTORM),
252+
MIDR_ALL_VERSIONS(MIDR_APPLE_M1_FIRESTORM),
253+
MIDR_ALL_VERSIONS(MIDR_APPLE_M1_ICESTORM_PRO),
254+
MIDR_ALL_VERSIONS(MIDR_APPLE_M1_FIRESTORM_PRO),
255+
MIDR_ALL_VERSIONS(MIDR_APPLE_M1_ICESTORM_MAX),
256+
MIDR_ALL_VERSIONS(MIDR_APPLE_M1_FIRESTORM_MAX),
257+
MIDR_ALL_VERSIONS(MIDR_APPLE_M2_BLIZZARD),
258+
MIDR_ALL_VERSIONS(MIDR_APPLE_M2_AVALANCHE),
259+
MIDR_ALL_VERSIONS(MIDR_APPLE_M2_BLIZZARD_PRO),
260+
MIDR_ALL_VERSIONS(MIDR_APPLE_M2_AVALANCHE_PRO),
261+
MIDR_ALL_VERSIONS(MIDR_APPLE_M2_BLIZZARD_MAX),
262+
MIDR_ALL_VERSIONS(MIDR_APPLE_M2_AVALANCHE_MAX),
263+
{},
264+
};
265+
266+
static bool has_impdef_pmuv3(const struct arm64_cpu_capabilities *entry, int scope)
267+
{
268+
u64 dfr0 = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
269+
unsigned int pmuver;
270+
271+
if (!is_kernel_in_hyp_mode())
272+
return false;
273+
274+
pmuver = cpuid_feature_extract_unsigned_field(dfr0,
275+
ID_AA64DFR0_EL1_PMUVer_SHIFT);
276+
if (pmuver != ID_AA64DFR0_EL1_PMUVer_IMP_DEF)
277+
return false;
278+
279+
return is_midr_in_range_list(impdef_pmuv3_cpus);
280+
}
281+
282+
static void cpu_enable_impdef_pmuv3_traps(const struct arm64_cpu_capabilities *__unused)
283+
{
284+
sysreg_clear_set_s(SYS_HACR_EL2, 0, BIT(56));
285+
}
286+
250287
#ifdef CONFIG_ARM64_WORKAROUND_REPEAT_TLBI
251288
static const struct arm64_cpu_capabilities arm64_repeat_tlbi_list[] = {
252289
#ifdef CONFIG_QCOM_FALKOR_ERRATUM_1009
@@ -847,6 +884,13 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
847884
{}
848885
})),
849886
},
887+
{
888+
.desc = "Apple IMPDEF PMUv3 Traps",
889+
.capability = ARM64_WORKAROUND_PMUV3_IMPDEF_TRAPS,
890+
.type = ARM64_CPUCAP_LOCAL_CPU_ERRATUM,
891+
.matches = has_impdef_pmuv3,
892+
.cpu_enable = cpu_enable_impdef_pmuv3_traps,
893+
},
850894
{
851895
}
852896
};

arch/arm64/kernel/cpufeature.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1900,6 +1900,28 @@ static bool has_lpa2(const struct arm64_cpu_capabilities *entry, int scope)
19001900
}
19011901
#endif
19021902

1903+
#ifdef CONFIG_HW_PERF_EVENTS
1904+
static bool has_pmuv3(const struct arm64_cpu_capabilities *entry, int scope)
1905+
{
1906+
u64 dfr0 = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);
1907+
unsigned int pmuver;
1908+
1909+
/*
1910+
* PMUVer follows the standard ID scheme for an unsigned field with the
1911+
* exception of 0xF (IMP_DEF) which is treated specially and implies
1912+
* FEAT_PMUv3 is not implemented.
1913+
*
1914+
* See DDI0487L.a D24.1.3.2 for more details.
1915+
*/
1916+
pmuver = cpuid_feature_extract_unsigned_field(dfr0,
1917+
ID_AA64DFR0_EL1_PMUVer_SHIFT);
1918+
if (pmuver == ID_AA64DFR0_EL1_PMUVer_IMP_DEF)
1919+
return false;
1920+
1921+
return pmuver >= ID_AA64DFR0_EL1_PMUVer_IMP;
1922+
}
1923+
#endif
1924+
19031925
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
19041926
#define KPTI_NG_TEMP_VA (-(1UL << PMD_SHIFT))
19051927

@@ -3010,6 +3032,14 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
30103032
.matches = has_cpuid_feature,
30113033
ARM64_CPUID_FIELDS(ID_AA64PFR1_EL1, GCS, IMP)
30123034
},
3035+
#endif
3036+
#ifdef CONFIG_HW_PERF_EVENTS
3037+
{
3038+
.desc = "PMUv3",
3039+
.capability = ARM64_HAS_PMUV3,
3040+
.type = ARM64_CPUCAP_SYSTEM_FEATURE,
3041+
.matches = has_pmuv3,
3042+
},
30133043
#endif
30143044
{},
30153045
};

arch/arm64/kernel/image-vars.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,6 @@ KVM_NVHE_ALIAS(broken_cntvoff_key);
113113
KVM_NVHE_ALIAS(__start___kvm_ex_table);
114114
KVM_NVHE_ALIAS(__stop___kvm_ex_table);
115115

116-
/* PMU available static key */
117-
#ifdef CONFIG_HW_PERF_EVENTS
118-
KVM_NVHE_ALIAS(kvm_arm_pmu_available);
119-
#endif
120-
121116
/* Position-independent library routines */
122117
KVM_NVHE_ALIAS_HYP(clear_page, __pi_clear_page);
123118
KVM_NVHE_ALIAS_HYP(copy_page, __pi_copy_page);

arch/arm64/kvm/arm.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
366366
r = get_num_wrps();
367367
break;
368368
case KVM_CAP_ARM_PMU_V3:
369-
r = kvm_arm_support_pmu_v3();
369+
r = kvm_supports_guest_pmuv3();
370370
break;
371371
case KVM_CAP_ARM_INJECT_SERROR_ESR:
372372
r = cpus_have_final_cap(ARM64_HAS_RAS_EXTN);
@@ -1400,7 +1400,7 @@ static unsigned long system_supported_vcpu_features(void)
14001400
if (!cpus_have_final_cap(ARM64_HAS_32BIT_EL1))
14011401
clear_bit(KVM_ARM_VCPU_EL1_32BIT, &features);
14021402

1403-
if (!kvm_arm_support_pmu_v3())
1403+
if (!kvm_supports_guest_pmuv3())
14041404
clear_bit(KVM_ARM_VCPU_PMU_V3, &features);
14051405

14061406
if (!system_supports_sve())

arch/arm64/kvm/hyp/include/hyp/switch.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ static inline void __activate_traps_common(struct kvm_vcpu *vcpu)
244244
* counter, which could make a PMXEVCNTR_EL0 access UNDEF at
245245
* EL1 instead of being trapped to EL2.
246246
*/
247-
if (kvm_arm_support_pmu_v3()) {
247+
if (system_supports_pmuv3()) {
248248
struct kvm_cpu_context *hctxt;
249249

250250
write_sysreg(0, pmselr_el0);
@@ -281,7 +281,7 @@ static inline void __deactivate_traps_common(struct kvm_vcpu *vcpu)
281281
write_sysreg(*host_data_ptr(host_debug_state.mdcr_el2), mdcr_el2);
282282

283283
write_sysreg(0, hstr_el2);
284-
if (kvm_arm_support_pmu_v3()) {
284+
if (system_supports_pmuv3()) {
285285
struct kvm_cpu_context *hctxt;
286286

287287
hctxt = host_data_ptr(host_ctxt);

arch/arm64/kvm/hyp/vhe/switch.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,25 @@ static bool kvm_hyp_handle_sysreg_vhe(struct kvm_vcpu *vcpu, u64 *exit_code)
527527
return kvm_hyp_handle_sysreg(vcpu, exit_code);
528528
}
529529

530+
static bool kvm_hyp_handle_impdef(struct kvm_vcpu *vcpu, u64 *exit_code)
531+
{
532+
u64 iss;
533+
534+
if (!cpus_have_final_cap(ARM64_WORKAROUND_PMUV3_IMPDEF_TRAPS))
535+
return false;
536+
537+
/*
538+
* Compute a synthetic ESR for a sysreg trap. Conveniently, AFSR1_EL2
539+
* is populated with a correct ISS for a sysreg trap. These fruity
540+
* parts are 64bit only, so unconditionally set IL.
541+
*/
542+
iss = ESR_ELx_ISS(read_sysreg_s(SYS_AFSR1_EL2));
543+
vcpu->arch.fault.esr_el2 = FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_SYS64) |
544+
FIELD_PREP(ESR_ELx_ISS_MASK, iss) |
545+
ESR_ELx_IL;
546+
return false;
547+
}
548+
530549
static const exit_handler_fn hyp_exit_handlers[] = {
531550
[0 ... ESR_ELx_EC_MAX] = NULL,
532551
[ESR_ELx_EC_CP15_32] = kvm_hyp_handle_cp15_32,
@@ -538,6 +557,9 @@ static const exit_handler_fn hyp_exit_handlers[] = {
538557
[ESR_ELx_EC_WATCHPT_LOW] = kvm_hyp_handle_watchpt_low,
539558
[ESR_ELx_EC_ERET] = kvm_hyp_handle_eret,
540559
[ESR_ELx_EC_MOPS] = kvm_hyp_handle_mops,
560+
561+
/* Apple shenanigans */
562+
[0x3F] = kvm_hyp_handle_impdef,
541563
};
542564

543565
static inline bool fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)

0 commit comments

Comments
 (0)