aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLadi Prosek <lprosek@redhat.com>2016-11-30 10:03:08 -0500
committerPaolo Bonzini <pbonzini@redhat.com>2016-12-08 09:31:09 -0500
commit7ca29de21362de242025fbc1c22436e19e39dddc (patch)
tree189d5baa255835aba81fa87155f0c3122c2dbe51
parent5a6a9748b4b41c1708dca12342256a19434513f2 (diff)
KVM: nVMX: fix CR3 load if L2 uses PAE paging and EPT
KVM does not correctly handle L1 hypervisors that emulate L2 real mode with PAE and EPT, such as Hyper-V. In this mode, the L1 hypervisor populates guest PDPTE VMCS fields and leaves guest CR3 uninitialized because it is not used (see 26.3.2.4 Loading Page-Directory-Pointer-Table Entries). KVM always dereferences CR3 and tries to load PDPTEs if PAE is on. This leads to two related issues: 1) On the first nested vmentry, the guest PDPTEs, as populated by L1, are overwritten in ept_load_pdptrs because the registers are believed to have been loaded in load_pdptrs as part of kvm_set_cr3. This is incorrect. L2 is running with PAE enabled but PDPTRs have been set up by L1. 2) When L2 is about to enable paging and loads its CR3, we, again, attempt to load PDPTEs in load_pdptrs called from kvm_set_cr3. There are no guarantees that this will succeed (it's just a CR3 load, paging is not enabled yet) and if it doesn't, kvm_set_cr3 returns early without persisting the CR3 which is then lost and L2 crashes right after it enables paging. This patch replaces the kvm_set_cr3 call with a simple register write if PAE and EPT are both on. CR3 is not to be interpreted in this case. Signed-off-by: Ladi Prosek <lprosek@redhat.com> Signed-off-by: Radim Krčmář <rkrcmar@redhat.com>
-rw-r--r--arch/x86/kvm/vmx.c16
1 files changed, 14 insertions, 2 deletions
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index d1a4f42f6c97..bcad2eb11404 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -9981,6 +9981,7 @@ static void prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
9981{ 9981{
9982 struct vcpu_vmx *vmx = to_vmx(vcpu); 9982 struct vcpu_vmx *vmx = to_vmx(vcpu);
9983 u32 exec_control; 9983 u32 exec_control;
9984 bool nested_ept_enabled = false;
9984 9985
9985 vmcs_write16(GUEST_ES_SELECTOR, vmcs12->guest_es_selector); 9986 vmcs_write16(GUEST_ES_SELECTOR, vmcs12->guest_es_selector);
9986 vmcs_write16(GUEST_CS_SELECTOR, vmcs12->guest_cs_selector); 9987 vmcs_write16(GUEST_CS_SELECTOR, vmcs12->guest_cs_selector);
@@ -10145,6 +10146,7 @@ static void prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
10145 vmcs12->guest_intr_status); 10146 vmcs12->guest_intr_status);
10146 } 10147 }
10147 10148
10149 nested_ept_enabled = (exec_control & SECONDARY_EXEC_ENABLE_EPT) != 0;
10148 vmcs_write32(SECONDARY_VM_EXEC_CONTROL, exec_control); 10150 vmcs_write32(SECONDARY_VM_EXEC_CONTROL, exec_control);
10149 } 10151 }
10150 10152
@@ -10295,8 +10297,18 @@ static void prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
10295 /* Note: modifies VM_ENTRY/EXIT_CONTROLS and GUEST/HOST_IA32_EFER */ 10297 /* Note: modifies VM_ENTRY/EXIT_CONTROLS and GUEST/HOST_IA32_EFER */
10296 vmx_set_efer(vcpu, vcpu->arch.efer); 10298 vmx_set_efer(vcpu, vcpu->arch.efer);
10297 10299
10298 /* shadow page tables on either EPT or shadow page tables */ 10300 /*
10299 kvm_set_cr3(vcpu, vmcs12->guest_cr3); 10301 * Shadow page tables on either EPT or shadow page tables.
10302 * If PAE and EPT are both on, CR3 is not used by the CPU and must not
10303 * be dereferenced.
10304 */
10305 if (is_pae(vcpu) && is_paging(vcpu) && !is_long_mode(vcpu) &&
10306 nested_ept_enabled) {
10307 vcpu->arch.cr3 = vmcs12->guest_cr3;
10308 __set_bit(VCPU_EXREG_CR3, (ulong *)&vcpu->arch.regs_avail);
10309 } else
10310 kvm_set_cr3(vcpu, vmcs12->guest_cr3);
10311
10300 kvm_mmu_reset_context(vcpu); 10312 kvm_mmu_reset_context(vcpu);
10301 10313
10302 if (!enable_ept) 10314 if (!enable_ept)