KVM: arm64: Guard PMSCR_EL1 initialization with SPE presence check

Commit efad60e460 ("KVM: arm64: Initialize PMSCR_EL1 when in VHE")
does not perform sufficient check before initializing PMSCR_EL1 to 0
when running in VHE mode. On some platforms, this causes the system to
hang during boot, as EL3 has not delegated access to the Profiling
Buffer to the Non-secure world, nor does it reinject an UNDEF on sysreg
trap.

To avoid this issue, restrict the PMSCR_EL1 initialization to CPUs that
support Statistical Profiling Extension (FEAT_SPE) and have the
Profiling Buffer accessible in Non-secure EL1. This is determined via a
new helper `cpu_has_spe()` which checks both PMSVer and PMBIDR_EL1.P.

This ensures the initialization only affects CPUs where SPE is
implemented and usable, preventing boot failures on platforms where SPE
is not properly configured.

Fixes: efad60e460 ("KVM: arm64: Initialize PMSCR_EL1 when in VHE")
Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
Mukesh Ojha 2025-10-10 23:17:07 +05:30 committed by Marc Zyngier
parent 9a7f87eb58
commit c35dd83866
1 changed files with 10 additions and 5 deletions

View File

@ -15,6 +15,12 @@
#include <asm/kvm_arm.h> #include <asm/kvm_arm.h>
#include <asm/kvm_emulate.h> #include <asm/kvm_emulate.h>
static int cpu_has_spe(u64 dfr0)
{
return cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_PMSVer_SHIFT) &&
!(read_sysreg_s(SYS_PMBIDR_EL1) & PMBIDR_EL1_P);
}
/** /**
* kvm_arm_setup_mdcr_el2 - configure vcpu mdcr_el2 value * kvm_arm_setup_mdcr_el2 - configure vcpu mdcr_el2 value
* *
@ -77,13 +83,12 @@ void kvm_init_host_debug_data(void)
*host_data_ptr(debug_brps) = SYS_FIELD_GET(ID_AA64DFR0_EL1, BRPs, dfr0); *host_data_ptr(debug_brps) = SYS_FIELD_GET(ID_AA64DFR0_EL1, BRPs, dfr0);
*host_data_ptr(debug_wrps) = SYS_FIELD_GET(ID_AA64DFR0_EL1, WRPs, dfr0); *host_data_ptr(debug_wrps) = SYS_FIELD_GET(ID_AA64DFR0_EL1, WRPs, dfr0);
if (cpu_has_spe(dfr0))
host_data_set_flag(HAS_SPE);
if (has_vhe()) if (has_vhe())
return; return;
if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_PMSVer_SHIFT) &&
!(read_sysreg_s(SYS_PMBIDR_EL1) & PMBIDR_EL1_P))
host_data_set_flag(HAS_SPE);
/* Check if we have BRBE implemented and available at the host */ /* Check if we have BRBE implemented and available at the host */
if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_BRBE_SHIFT)) if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_BRBE_SHIFT))
host_data_set_flag(HAS_BRBE); host_data_set_flag(HAS_BRBE);
@ -102,7 +107,7 @@ void kvm_init_host_debug_data(void)
void kvm_debug_init_vhe(void) void kvm_debug_init_vhe(void)
{ {
/* Clear PMSCR_EL1.E{0,1}SPE which reset to UNKNOWN values. */ /* Clear PMSCR_EL1.E{0,1}SPE which reset to UNKNOWN values. */
if (SYS_FIELD_GET(ID_AA64DFR0_EL1, PMSVer, read_sysreg(id_aa64dfr0_el1))) if (host_data_test_flag(HAS_SPE))
write_sysreg_el1(0, SYS_PMSCR); write_sysreg_el1(0, SYS_PMSCR);
} }