@@ -16,11 +16,13 @@ static __always_inline bool kvm_supports_spe(void)
void kvm_spe_init_supported_cpus(void);
void kvm_spe_vm_init(struct kvm *kvm);
+int kvm_spe_check_supported_cpus(struct kvm_vcpu *vcpu);
#else
#define kvm_supports_spe() (false)
static inline void kvm_spe_init_supported_cpus(void) {}
static inline void kvm_spe_vm_init(struct kvm *kvm) {}
+static inline int kvm_spe_check_supported_cpus(struct kvm_vcpu *vcpu) { return -ENOEXEC; }
#endif /* CONFIG_KVM_ARM_SPE */
#endif /* __ARM64_KVM_SPE_H__ */
@@ -633,6 +633,9 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
if (!kvm_arm_vcpu_is_finalized(vcpu))
return -EPERM;
+ if (kvm_vcpu_has_spe(vcpu) && kvm_spe_check_supported_cpus(vcpu))
+ return -EPERM;
+
vcpu->arch.has_run_once = true;
kvm_arm_vcpu_init_debug(vcpu);
@@ -30,3 +30,15 @@ void kvm_spe_vm_init(struct kvm *kvm)
/* Set supported_cpus if it isn't already initialized. */
kvm_spe_init_supported_cpus();
}
+
+int kvm_spe_check_supported_cpus(struct kvm_vcpu *vcpu)
+{
+ /* SPE is supported on all CPUs, we don't care about the VCPU mask */
+ if (cpumask_equal(supported_cpus, cpu_possible_mask))
+ return 0;
+
+ if (!cpumask_subset(&vcpu->arch.supported_cpus, supported_cpus))
+ return -ENOEXEC;
+
+ return 0;
+}
The kernel allows heterogeneous systems where FEAT_SPE is not present on all CPUs. This presents a challenge for KVM, as it will have to touch the SPE registers when emulating SPE for a guest, and those accesses will cause an undefined exception if SPE is not present on the CPU. Avoid this situation by comparing the cpumask of the physical CPUs that support SPE with the cpu list provided by userspace via the KVM_ARM_VCPU_SUPPORTED_CPUS ioctl and refusing the run the VCPU if there is a mismatch. Signed-off-by: Alexandru Elisei <alexandru.elisei@arm.com> --- arch/arm64/include/asm/kvm_spe.h | 2 ++ arch/arm64/kvm/arm.c | 3 +++ arch/arm64/kvm/spe.c | 12 ++++++++++++ 3 files changed, 17 insertions(+)