mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	KVM: arm64: Refuse to run VCPU if the PMU doesn't match the physical CPU
Userspace can assign a PMU to a VCPU with the KVM_ARM_VCPU_PMU_V3_SET_PMU device ioctl. If the VCPU is scheduled on a physical CPU which has a different PMU, the perf events needed to emulate a guest PMU won't be scheduled in and the guest performance counters will stop counting. Treat it as an userspace error and refuse to run the VCPU in this situation. Suggested-by: Marc Zyngier <maz@kernel.org> Signed-off-by: Alexandru Elisei <alexandru.elisei@arm.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20220127161759.53553-7-alexandru.elisei@arm.com
This commit is contained in:
		
							parent
							
								
									6ee7fca2a4
								
							
						
					
					
						commit
						583cda1b0e
					
				
					 5 changed files with 38 additions and 1 deletions
				
			
		| 
						 | 
					@ -131,7 +131,11 @@ if a PMU event filter is already present.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Note that KVM will not make any attempts to run the VCPU on the physical CPUs
 | 
					Note that KVM will not make any attempts to run the VCPU on the physical CPUs
 | 
				
			||||||
associated with the PMU specified by this attribute. This is entirely left to
 | 
					associated with the PMU specified by this attribute. This is entirely left to
 | 
				
			||||||
userspace.
 | 
					userspace. However, attempting to run the VCPU on a physical CPU not supported
 | 
				
			||||||
 | 
					by the PMU will fail and KVM_RUN will return with
 | 
				
			||||||
 | 
					exit_reason = KVM_EXIT_FAIL_ENTRY and populate the fail_entry struct by setting
 | 
				
			||||||
 | 
					hardare_entry_failure_reason field to KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED and
 | 
				
			||||||
 | 
					the cpu field to the processor id.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
2. GROUP: KVM_ARM_VCPU_TIMER_CTRL
 | 
					2. GROUP: KVM_ARM_VCPU_TIMER_CTRL
 | 
				
			||||||
=================================
 | 
					=================================
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -131,6 +131,8 @@ struct kvm_arch {
 | 
				
			||||||
	unsigned long *pmu_filter;
 | 
						unsigned long *pmu_filter;
 | 
				
			||||||
	struct arm_pmu *arm_pmu;
 | 
						struct arm_pmu *arm_pmu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cpumask_var_t supported_cpus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	u8 pfr0_csv2;
 | 
						u8 pfr0_csv2;
 | 
				
			||||||
	u8 pfr0_csv3;
 | 
						u8 pfr0_csv3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -436,6 +438,7 @@ struct kvm_vcpu_arch {
 | 
				
			||||||
#define KVM_ARM64_DEBUG_STATE_SAVE_SPE	(1 << 12) /* Save SPE context if active  */
 | 
					#define KVM_ARM64_DEBUG_STATE_SAVE_SPE	(1 << 12) /* Save SPE context if active  */
 | 
				
			||||||
#define KVM_ARM64_DEBUG_STATE_SAVE_TRBE	(1 << 13) /* Save TRBE context if active  */
 | 
					#define KVM_ARM64_DEBUG_STATE_SAVE_TRBE	(1 << 13) /* Save TRBE context if active  */
 | 
				
			||||||
#define KVM_ARM64_FP_FOREIGN_FPSTATE	(1 << 14)
 | 
					#define KVM_ARM64_FP_FOREIGN_FPSTATE	(1 << 14)
 | 
				
			||||||
 | 
					#define KVM_ARM64_ON_UNSUPPORTED_CPU	(1 << 15) /* Physical CPU not in supported_cpus */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define KVM_GUESTDBG_VALID_MASK (KVM_GUESTDBG_ENABLE | \
 | 
					#define KVM_GUESTDBG_VALID_MASK (KVM_GUESTDBG_ENABLE | \
 | 
				
			||||||
				 KVM_GUESTDBG_USE_SW_BP | \
 | 
									 KVM_GUESTDBG_USE_SW_BP | \
 | 
				
			||||||
| 
						 | 
					@ -454,6 +457,15 @@ struct kvm_vcpu_arch {
 | 
				
			||||||
#define vcpu_has_ptrauth(vcpu)		false
 | 
					#define vcpu_has_ptrauth(vcpu)		false
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define vcpu_on_unsupported_cpu(vcpu)					\
 | 
				
			||||||
 | 
						((vcpu)->arch.flags & KVM_ARM64_ON_UNSUPPORTED_CPU)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define vcpu_set_on_unsupported_cpu(vcpu)				\
 | 
				
			||||||
 | 
						((vcpu)->arch.flags |= KVM_ARM64_ON_UNSUPPORTED_CPU)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define vcpu_clear_on_unsupported_cpu(vcpu)				\
 | 
				
			||||||
 | 
						((vcpu)->arch.flags &= ~KVM_ARM64_ON_UNSUPPORTED_CPU)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define vcpu_gp_regs(v)		(&(v)->arch.ctxt.regs)
 | 
					#define vcpu_gp_regs(v)		(&(v)->arch.ctxt.regs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -414,6 +414,9 @@ struct kvm_arm_copy_mte_tags {
 | 
				
			||||||
#define KVM_PSCI_RET_INVAL		PSCI_RET_INVALID_PARAMS
 | 
					#define KVM_PSCI_RET_INVAL		PSCI_RET_INVALID_PARAMS
 | 
				
			||||||
#define KVM_PSCI_RET_DENIED		PSCI_RET_DENIED
 | 
					#define KVM_PSCI_RET_DENIED		PSCI_RET_DENIED
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* run->fail_entry.hardware_entry_failure_reason codes. */
 | 
				
			||||||
 | 
					#define KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED	(1ULL << 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* __ARM_KVM_H__ */
 | 
					#endif /* __ARM_KVM_H__ */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -150,6 +150,10 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 | 
				
			||||||
	if (ret)
 | 
						if (ret)
 | 
				
			||||||
		goto out_free_stage2_pgd;
 | 
							goto out_free_stage2_pgd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!zalloc_cpumask_var(&kvm->arch.supported_cpus, GFP_KERNEL))
 | 
				
			||||||
 | 
							goto out_free_stage2_pgd;
 | 
				
			||||||
 | 
						cpumask_copy(kvm->arch.supported_cpus, cpu_possible_mask);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kvm_vgic_early_init(kvm);
 | 
						kvm_vgic_early_init(kvm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* The maximum number of VCPUs is limited by the host's GIC model */
 | 
						/* The maximum number of VCPUs is limited by the host's GIC model */
 | 
				
			||||||
| 
						 | 
					@ -176,6 +180,7 @@ vm_fault_t kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf)
 | 
				
			||||||
void kvm_arch_destroy_vm(struct kvm *kvm)
 | 
					void kvm_arch_destroy_vm(struct kvm *kvm)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	bitmap_free(kvm->arch.pmu_filter);
 | 
						bitmap_free(kvm->arch.pmu_filter);
 | 
				
			||||||
 | 
						free_cpumask_var(kvm->arch.supported_cpus);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kvm_vgic_destroy(kvm);
 | 
						kvm_vgic_destroy(kvm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -411,6 +416,9 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 | 
				
			||||||
	if (vcpu_has_ptrauth(vcpu))
 | 
						if (vcpu_has_ptrauth(vcpu))
 | 
				
			||||||
		vcpu_ptrauth_disable(vcpu);
 | 
							vcpu_ptrauth_disable(vcpu);
 | 
				
			||||||
	kvm_arch_vcpu_load_debug_state_flags(vcpu);
 | 
						kvm_arch_vcpu_load_debug_state_flags(vcpu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!cpumask_test_cpu(smp_processor_id(), vcpu->kvm->arch.supported_cpus))
 | 
				
			||||||
 | 
							vcpu_set_on_unsupported_cpu(vcpu);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
 | 
					void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
 | 
				
			||||||
| 
						 | 
					@ -423,6 +431,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
 | 
				
			||||||
	kvm_vgic_put(vcpu);
 | 
						kvm_vgic_put(vcpu);
 | 
				
			||||||
	kvm_vcpu_pmu_restore_host(vcpu);
 | 
						kvm_vcpu_pmu_restore_host(vcpu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vcpu_clear_on_unsupported_cpu(vcpu);
 | 
				
			||||||
	vcpu->cpu = -1;
 | 
						vcpu->cpu = -1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -796,6 +805,14 @@ static bool kvm_vcpu_exit_request(struct kvm_vcpu *vcpu, int *ret)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (unlikely(vcpu_on_unsupported_cpu(vcpu))) {
 | 
				
			||||||
 | 
							run->exit_reason = KVM_EXIT_FAIL_ENTRY;
 | 
				
			||||||
 | 
							run->fail_entry.hardware_entry_failure_reason = KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED;
 | 
				
			||||||
 | 
							run->fail_entry.cpu = smp_processor_id();
 | 
				
			||||||
 | 
							*ret = 0;
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return kvm_request_pending(vcpu) ||
 | 
						return kvm_request_pending(vcpu) ||
 | 
				
			||||||
			need_new_vmid_gen(&vcpu->arch.hw_mmu->vmid) ||
 | 
								need_new_vmid_gen(&vcpu->arch.hw_mmu->vmid) ||
 | 
				
			||||||
			xfer_to_guest_mode_work_pending();
 | 
								xfer_to_guest_mode_work_pending();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -968,6 +968,7 @@ static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			kvm->arch.arm_pmu = arm_pmu;
 | 
								kvm->arch.arm_pmu = arm_pmu;
 | 
				
			||||||
 | 
								cpumask_copy(kvm->arch.supported_cpus, &arm_pmu->supported_cpus);
 | 
				
			||||||
			ret = 0;
 | 
								ret = 0;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue