Skip to content

Commit 43fea4e

Browse files
Peter Shierbonzini
authored andcommitted
KVM: nVMX: Update VMCS02 when L2 PAE PDPTE updates detected
When L2 uses PAE, L0 intercepts of L2 writes to CR0/CR3/CR4 call load_pdptrs to read the possibly updated PDPTEs from the guest physical address referenced by CR3. It loads them into vcpu->arch.walk_mmu->pdptrs and sets VCPU_EXREG_PDPTR in vcpu->arch.regs_dirty. At the subsequent assumed reentry into L2, the mmu will call vmx_load_mmu_pgd which calls ept_load_pdptrs. ept_load_pdptrs sees VCPU_EXREG_PDPTR set in vcpu->arch.regs_dirty and loads VMCS02.GUEST_PDPTRn from vcpu->arch.walk_mmu->pdptrs[]. This all works if the L2 CRn write intercept always resumes L2. The resume path calls vmx_check_nested_events which checks for exceptions, MTF, and expired VMX preemption timers. If vmx_check_nested_events finds any of these conditions pending it will reflect the corresponding exit into L1. Live migration at this point would also cause a missed immediate reentry into L2. After L1 exits, vmx_vcpu_run calls vmx_register_cache_reset which clears VCPU_EXREG_PDPTR in vcpu->arch.regs_dirty. When L2 next resumes, ept_load_pdptrs finds VCPU_EXREG_PDPTR clear in vcpu->arch.regs_dirty and does not load VMCS02.GUEST_PDPTRn from vcpu->arch.walk_mmu->pdptrs[]. prepare_vmcs02 will then load VMCS02.GUEST_PDPTRn from vmcs12->pdptr0/1/2/3 which contain the stale values stored at last L2 exit. A repro of this bug showed L2 entering triple fault immediately due to the bad VMCS02.GUEST_PDPTRn values. When L2 is in PAE paging mode add a call to ept_load_pdptrs before leaving L2. This will update VMCS02.GUEST_PDPTRn if they are dirty in vcpu->arch.walk_mmu->pdptrs[]. Tested: kvm-unit-tests with new directed test: vmx_mtf_pdpte_test. Verified that test fails without the fix. Also ran Google internal VMM with an Ubuntu 16.04 4.4.0-83 guest running a custom hypervisor with a 32-bit Windows XP L2 guest using PAE. Prior to fix would repro readily. Ran 14 simultaneous L2s for 140 iterations with no failures. Signed-off-by: Peter Shier <[email protected]> Reviewed-by: Jim Mattson <[email protected]> Message-Id: <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent 1b67fd0 commit 43fea4e

File tree

3 files changed

+11
-2
lines changed

3 files changed

+11
-2
lines changed

arch/x86/kvm/vmx/nested.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4404,6 +4404,14 @@ void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 vm_exit_reason,
44044404
if (kvm_check_request(KVM_REQ_TLB_FLUSH_CURRENT, vcpu))
44054405
kvm_vcpu_flush_tlb_current(vcpu);
44064406

4407+
/*
4408+
* VCPU_EXREG_PDPTR will be clobbered in arch/x86/kvm/vmx/vmx.h between
4409+
* now and the new vmentry. Ensure that the VMCS02 PDPTR fields are
4410+
* up-to-date before switching to L1.
4411+
*/
4412+
if (enable_ept && is_pae_paging(vcpu))
4413+
vmx_ept_load_pdptrs(vcpu);
4414+
44074415
leave_guest_mode(vcpu);
44084416

44094417
if (nested_cpu_has_preemption_timer(vmcs12))

arch/x86/kvm/vmx/vmx.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2971,7 +2971,7 @@ static void vmx_flush_tlb_guest(struct kvm_vcpu *vcpu)
29712971
vpid_sync_context(to_vmx(vcpu)->vpid);
29722972
}
29732973

2974-
static void ept_load_pdptrs(struct kvm_vcpu *vcpu)
2974+
void vmx_ept_load_pdptrs(struct kvm_vcpu *vcpu)
29752975
{
29762976
struct kvm_mmu *mmu = vcpu->arch.walk_mmu;
29772977

@@ -3114,7 +3114,7 @@ static void vmx_load_mmu_pgd(struct kvm_vcpu *vcpu, unsigned long pgd,
31143114
guest_cr3 = vcpu->arch.cr3;
31153115
else /* vmcs01.GUEST_CR3 is already up-to-date. */
31163116
update_guest_cr3 = false;
3117-
ept_load_pdptrs(vcpu);
3117+
vmx_ept_load_pdptrs(vcpu);
31183118
} else {
31193119
guest_cr3 = pgd;
31203120
}

arch/x86/kvm/vmx/vmx.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ void vmx_update_host_rsp(struct vcpu_vmx *vmx, unsigned long host_rsp);
356356
int vmx_find_msr_index(struct vmx_msrs *m, u32 msr);
357357
int vmx_handle_memory_failure(struct kvm_vcpu *vcpu, int r,
358358
struct x86_exception *e);
359+
void vmx_ept_load_pdptrs(struct kvm_vcpu *vcpu);
359360

360361
#define POSTED_INTR_ON 0
361362
#define POSTED_INTR_SN 1

0 commit comments

Comments
 (0)