Introduce a new vCPU feature KVM_ARM_VCPU_PMU_V3_STRICT. When set, KVM
does not create a default PMU when initializing the vCPU, and userspace
must select one explicitly via KVM_ARM_VCPU_PMU_V3_SET_PMU before the
first KVM_RUN.

The flag forces the VMM to be aware of the PMU implementation of the
guest to be created, so that certain information about the PMU becomes
deterministic (if on a heterogeneous system) and becomes safe to be
exposed to the guest. It can be used as an umbrella flag to gate future
PMUv3 UAPI changes.

When no default PMU is created, kvm->arch.arm_pmu stays NULL until SET_PMU
runs, so kvm_arm_pmu_v3_enable() now refuses to run if kvm->arch.arm_pmu
is NULL.

Signed-off-by: Congkai Tan <[email protected]>
Reviewed-by: Geoff Blake <[email protected]>
Reviewed-by: Haris Okanovic <[email protected]>
Reviewed-by: Stanislav Spassov <[email protected]>
---
 Documentation/virt/kvm/api.rst          |  5 +++++
 arch/arm64/include/asm/kvm_host.h       |  2 +-
 arch/arm64/include/uapi/asm/kvm.h       |  1 +
 arch/arm64/kvm/arm.c                    | 18 ++++++++++++++----
 arch/arm64/kvm/pmu-emul.c               | 14 +++++++++++++-
 include/kvm/arm_pmu.h                   |  4 ++++
 tools/arch/arm64/include/uapi/asm/kvm.h |  1 +
 7 files changed, 39 insertions(+), 6 deletions(-)

diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index 52bbbb553ce1..79b024a7ba16 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -3515,6 +3515,11 @@ Possible features:
          Depends on KVM_CAP_ARM_PSCI_0_2.
        - KVM_ARM_VCPU_PMU_V3: Emulate PMUv3 for the CPU.
          Depends on KVM_CAP_ARM_PMU_V3.
+       - KVM_ARM_VCPU_PMU_V3_STRICT: Enable strict PMUv3 UAPI.
+         Requires KVM_ARM_VCPU_PMU_V3. If set, KVM does not create a default
+         PMU; userspace must select a PMU implementation with
+         KVM_ARM_VCPU_PMU_V3_SET_PMU before the first KVM_RUN. The selected
+         PMU exposes the SLOTS field of its PMMIR_EL1 register to the guest.
 
        - KVM_ARM_VCPU_PTRAUTH_ADDRESS: Enables Address Pointer authentication
          for arm64 only.
diff --git a/arch/arm64/include/asm/kvm_host.h 
b/arch/arm64/include/asm/kvm_host.h
index 65eead8362e0..a6e33aaf400d 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -39,7 +39,7 @@
 
 #define KVM_MAX_VCPUS VGIC_V3_MAX_CPUS
 
-#define KVM_VCPU_MAX_FEATURES 9
+#define KVM_VCPU_MAX_FEATURES 10
 #define KVM_VCPU_VALID_FEATURES        (BIT(KVM_VCPU_MAX_FEATURES) - 1)
 
 #define KVM_REQ_SLEEP \
diff --git a/arch/arm64/include/uapi/asm/kvm.h 
b/arch/arm64/include/uapi/asm/kvm.h
index 1c13bfa2d38a..019e5e3d892e 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -106,6 +106,7 @@ struct kvm_regs {
 #define KVM_ARM_VCPU_PTRAUTH_GENERIC   6 /* VCPU uses generic authentication */
 #define KVM_ARM_VCPU_HAS_EL2           7 /* Support nested virtualization */
 #define KVM_ARM_VCPU_HAS_EL2_E2H0      8 /* Limit NV support to E2H RES0 */
+#define KVM_ARM_VCPU_PMU_V3_STRICT     9 /* No default PMU creation */
 
 struct kvm_vcpu_init {
        __u32 target;
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 9453321ef8c6..d1914bee1e76 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -1548,8 +1548,10 @@ static unsigned long system_supported_vcpu_features(void)
        if (!cpus_have_final_cap(ARM64_HAS_32BIT_EL1))
                clear_bit(KVM_ARM_VCPU_EL1_32BIT, &features);
 
-       if (!kvm_supports_guest_pmuv3())
+       if (!kvm_supports_guest_pmuv3()) {
                clear_bit(KVM_ARM_VCPU_PMU_V3, &features);
+               clear_bit(KVM_ARM_VCPU_PMU_V3_STRICT, &features);
+       }
 
        if (!system_supports_sve())
                clear_bit(KVM_ARM_VCPU_SVE, &features);
@@ -1590,6 +1592,11 @@ static int kvm_vcpu_init_check_features(struct kvm_vcpu 
*vcpu,
            test_bit(KVM_ARM_VCPU_PTRAUTH_GENERIC, &features))
                return -EINVAL;
 
+       /* Strict PMUv3 UAPI requires PMUv3. */
+       if (test_bit(KVM_ARM_VCPU_PMU_V3_STRICT, &features) &&
+           !test_bit(KVM_ARM_VCPU_PMU_V3, &features))
+               return -EINVAL;
+
        if (!test_bit(KVM_ARM_VCPU_EL1_32BIT, &features))
                return 0;
 
@@ -1619,10 +1626,13 @@ static int kvm_setup_vcpu(struct kvm_vcpu *vcpu)
        int ret = 0;
 
        /*
-        * When the vCPU has a PMU, but no PMU is set for the guest
-        * yet, set the default one.
+        * When the vCPU has a PMU, but no PMU is set for the guest yet, set
+        * the default one. If KVM_ARM_VCPU_PMU_V3_STRICT is set, no default
+        * PMU is created, and userspace must select a PMU via
+        * KVM_ARM_VCPU_PMU_V3_SET_PMU.
         */
-       if (kvm_vcpu_has_pmu(vcpu) && !kvm->arch.arm_pmu)
+       if (kvm_vcpu_has_pmu(vcpu) && !kvm->arch.arm_pmu &&
+           !kvm_vcpu_has_pmuv3_strict(vcpu))
                ret = kvm_arm_set_default_pmu(kvm);
 
        /* Prepare for nested if required */
diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c
index e1860acae641..1f24169505a9 100644
--- a/arch/arm64/kvm/pmu-emul.c
+++ b/arch/arm64/kvm/pmu-emul.c
@@ -923,6 +923,9 @@ void kvm_vcpu_reload_pmu(struct kvm_vcpu *vcpu)
 
 int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu)
 {
+       if (!vcpu->kvm->arch.arm_pmu)
+               return -EINVAL;
+
        if (!vcpu->arch.pmu.created)
                return -EINVAL;
 
@@ -1021,6 +1024,14 @@ u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm)
 {
        struct arm_pmu *arm_pmu = kvm->arch.arm_pmu;
 
+       /*
+        * Under KVM_ARM_VCPU_PMU_V3_STRICT no PMU exists until userspace sets
+        * one, so this can be reached before arm_pmu is set. Report no
+        * counters in that case.
+        */
+       if (!arm_pmu)
+               return 0;
+
        /*
         * PMUv3 requires that all event counters are capable of counting any
         * event, though the same may not be true of non-PMUv3 hardware.
@@ -1062,7 +1073,8 @@ static void kvm_arm_set_pmu(struct kvm *kvm, struct 
arm_pmu *arm_pmu)
 }
 
 /**
- * kvm_arm_set_default_pmu - No PMU set, get the default one.
+ * kvm_arm_set_default_pmu - No PMU set and KVM_ARM_VCPU_PMU_V3_STRICT not
+ * set, get the default one.
  * @kvm: The kvm pointer
  *
  * The observant among you will notice that the supported_cpus
diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h
index 0a36a3d5c894..13468bd5bbf2 100644
--- a/include/kvm/arm_pmu.h
+++ b/include/kvm/arm_pmu.h
@@ -76,6 +76,9 @@ void kvm_vcpu_pmu_resync_el0(void);
 #define kvm_vcpu_has_pmu(vcpu)                                 \
        (vcpu_has_feature(vcpu, KVM_ARM_VCPU_PMU_V3))
 
+#define kvm_vcpu_has_pmuv3_strict(vcpu)                                \
+       (vcpu_has_feature(vcpu, KVM_ARM_VCPU_PMU_V3_STRICT))
+
 /*
  * Updates the vcpu's view of the pmu events for this cpu.
  * Must be called before every vcpu run after disabling interrupts, to ensure
@@ -161,6 +164,7 @@ static inline u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, 
bool pmceid1)
 }
 
 #define kvm_vcpu_has_pmu(vcpu)         ({ false; })
+#define kvm_vcpu_has_pmuv3_strict(vcpu)        ({ false; })
 static inline void kvm_pmu_update_vcpu_events(struct kvm_vcpu *vcpu) {}
 static inline void kvm_vcpu_pmu_restore_guest(struct kvm_vcpu *vcpu) {}
 static inline void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu) {}
diff --git a/tools/arch/arm64/include/uapi/asm/kvm.h 
b/tools/arch/arm64/include/uapi/asm/kvm.h
index 1c13bfa2d38a..019e5e3d892e 100644
--- a/tools/arch/arm64/include/uapi/asm/kvm.h
+++ b/tools/arch/arm64/include/uapi/asm/kvm.h
@@ -106,6 +106,7 @@ struct kvm_regs {
 #define KVM_ARM_VCPU_PTRAUTH_GENERIC   6 /* VCPU uses generic authentication */
 #define KVM_ARM_VCPU_HAS_EL2           7 /* Support nested virtualization */
 #define KVM_ARM_VCPU_HAS_EL2_E2H0      8 /* Limit NV support to E2H RES0 */
+#define KVM_ARM_VCPU_PMU_V3_STRICT     9 /* No default PMU creation */
 
 struct kvm_vcpu_init {
        __u32 target;
-- 
2.50.1


Reply via email to