When host doesn't support 5-level EPT, bits 51:48 of the guest physical address must all be zero, otherwise an EPT violation always occurs and current handler can't resolve this if the gpa is in RAM region. Hence, instruction will keep being executed repeatedly, which causes infinite EPT violation. Six KVM selftests are timeout due to this issue: kvm:access_tracking_perf_test kvm:demand_paging_test kvm:dirty_log_test kvm:dirty_log_perf_test kvm:kvm_page_table_test kvm:memslot_modification_stress_test The above selftests add a RAM region close to max_gfn, if host has 52 physical bits but doesn't support 5-level EPT, these will trigger infinite EPT violation when access the RAM region. Since current Intel CPUID doesn't report max guest physical bits like AMD, introduce kvm_mmu_tdp_maxphyaddr() to limit guest physical bits when tdp is enabled and report the max guest physical bits which is smaller than host. When guest physical bits is smaller than host, some GPA are illegal from guest's perspective, but are still legal from hardware's perspective, which should be trapped to inject #PF. Current KVM already has a parameter allow_smaller_maxphyaddr to support the case when guest.MAXPHYADDR < host.MAXPHYADDR, which is disabled by default when EPT is enabled, user can enable it when loading kvm-intel module. When allow_smaller_maxphyaddr is enabled and guest accesses an illegal address from guest's perspective, KVM will utilize EPT violation and emulate the instruction to inject #PF and determine #PF error code. Reported-by: Yi Lai <yi1.lai@xxxxxxxxx> Signed-off-by: Tao Su <tao1.su@xxxxxxxxxxxxxxx> Tested-by: Yi Lai <yi1.lai@xxxxxxxxx> Tested-by: Xudong Hao <xudong.hao@xxxxxxxxx> --- arch/x86/kvm/cpuid.c | 5 +++-- arch/x86/kvm/mmu.h | 1 + arch/x86/kvm/mmu/mmu.c | 7 +++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index dda6fc4cfae8..91933ca739ad 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -1212,12 +1212,13 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function) * * If TDP is enabled but an explicit guest MAXPHYADDR is not * provided, use the raw bare metal MAXPHYADDR as reductions to - * the HPAs do not affect GPAs. + * the HPAs do not affect GPAs, but ensure guest MAXPHYADDR + * doesn't exceed the bits that TDP can translate. */ if (!tdp_enabled) g_phys_as = boot_cpu_data.x86_phys_bits; else if (!g_phys_as) - g_phys_as = phys_as; + g_phys_as = min(phys_as, kvm_mmu_tdp_maxphyaddr()); entry->eax = g_phys_as | (virt_as << 8); entry->ecx &= ~(GENMASK(31, 16) | GENMASK(11, 8)); diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h index bb8c86eefac0..1c7d649fcf6b 100644 --- a/arch/x86/kvm/mmu.h +++ b/arch/x86/kvm/mmu.h @@ -115,6 +115,7 @@ int kvm_handle_page_fault(struct kvm_vcpu *vcpu, u64 error_code, u64 fault_address, char *insn, int insn_len); void __kvm_mmu_refresh_passthrough_bits(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu); +unsigned int kvm_mmu_tdp_maxphyaddr(void); int kvm_mmu_load(struct kvm_vcpu *vcpu); void kvm_mmu_unload(struct kvm_vcpu *vcpu); diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index c57e181bba21..72634d6b61b2 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -5177,6 +5177,13 @@ void __kvm_mmu_refresh_passthrough_bits(struct kvm_vcpu *vcpu, reset_guest_paging_metadata(vcpu, mmu); } +/* guest-physical-address bits limited by TDP */ +unsigned int kvm_mmu_tdp_maxphyaddr(void) +{ + return max_tdp_level == 5 ? 57 : 48; +} +EXPORT_SYMBOL_GPL(kvm_mmu_tdp_maxphyaddr); + static inline int kvm_mmu_get_tdp_level(struct kvm_vcpu *vcpu) { /* tdp_root_level is architecture forced level, use it if nonzero */ -- 2.34.1