[RFC PATCH v6 23/35] KVM: arm64: Allow guest SPE physical timestamps only if perfmon_capable()

Alexandru Elisei
2025-11-14T16:08:32Z

The SPE driver allows userspace to use physical timestamps for records only
if the process if perfmon_capable(). Do the same for a virtual machine.

Signed-off-by: Alexandru Elisei <alexandru.elisei@arm.com>
---
arch/arm64/include/asm/kvm_spe.h | 1 +
arch/arm64/kvm/spe.c | 16 ++++++++++++++--
2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_spe.h b/arch/arm64/include/asm/kvm_spe.h
index 077ca1e596b8..a61c1c1de76f 100644
--- a/arch/arm64/include/asm/kvm_spe.h
+++ b/arch/arm64/include/asm/kvm_spe.h
@@ -13,6 +13,7 @@
struct kvm_spe {
struct arm_spe_pmu *arm_spu;
u64 max_buffer_size; /* Maximum per VCPU buffer size */
+ u64 guest_pmscr_el2;
};

struct kvm_vcpu_spe {
diff --git a/arch/arm64/kvm/spe.c b/arch/arm64/kvm/spe.c
index 32156e43f454..85a1ac8bb57f 100644
--- a/arch/arm64/kvm/spe.c
+++ b/arch/arm64/kvm/spe.c
@@ -4,6 +4,7 @@
*/

#include <linux/bitops.h>
+#include <linux/capability.h>
#include <linux/cpumask.h>
#include <linux/kvm_host.h>
#include <linux/perf/arm_spe_pmu.h>
@@ -64,6 +65,8 @@ void kvm_spe_init_vm(struct kvm *kvm)

int kvm_spe_vcpu_first_run_init(struct kvm_vcpu *vcpu)
{
+ struct kvm *kvm = vcpu->kvm;
+ struct kvm_spe *kvm_spe = &kvm->arch.kvm_spe;
struct kvm_vcpu_spe *vcpu_spe = &vcpu->arch.vcpu_spe;

if (!vcpu_has_spe(vcpu))
@@ -72,6 +75,12 @@ int kvm_spe_vcpu_first_run_init(struct kvm_vcpu *vcpu)
if (!vcpu_spe->initialized)
return -EINVAL;

+ if (kvm_vm_has_ran_once(kvm))
+ return 0;
+
+ if (perfmon_capable())
+ kvm_spe->guest_pmscr_el2 = PMSCR_EL2_PCT_PHYS;
+
return 0;
}

@@ -217,8 +226,11 @@ void kvm_vcpu_spe_load(struct kvm_vcpu *vcpu)
local_irq_disable();

host_pmscr_el2 = read_sysreg_el2(SYS_PMSCR);
- write_sysreg_el2(0, SYS_PMSCR);
- /* Host was profiling, synchronize the write to PMSCR_EL2. */
+ write_sysreg_el2(vcpu->kvm->arch.kvm_spe.guest_pmscr_el2, SYS_PMSCR);
+ /*
+ * Host was profiling, synchronize the write to PMSCR_EL2 with the guest
+ * value which disables profiling at EL2.
+ */
if (FIELD_GET(PMSCR_EL2_E2SPE, host_pmscr_el2))
isb();

--
2.51.2