Skip to content

Commit c9f0440

Browse files
dmatlackrkrcmar
authored andcommitted
KVM: nVMX: mark vmcs12 pages dirty on L2 exit
The host physical addresses of L1's Virtual APIC Page and Posted Interrupt descriptor are loaded into the VMCS02. The CPU may write to these pages via their host physical address while L2 is running, bypassing address-translation-based dirty tracking (e.g. EPT write protection). Mark them dirty on every exit from L2 to prevent them from getting out of sync with dirty tracking. Also mark the virtual APIC page and the posted interrupt descriptor dirty when KVM is virtualizing posted interrupt processing. Signed-off-by: David Matlack <[email protected]> Reviewed-by: Paolo Bonzini <[email protected]> Signed-off-by: Radim Krčmář <[email protected]>
1 parent 8ca44e8 commit c9f0440

File tree

1 file changed

+43
-10
lines changed

1 file changed

+43
-10
lines changed

arch/x86/kvm/vmx.c

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4995,25 +4995,44 @@ static bool vmx_get_enable_apicv(void)
49954995
return enable_apicv;
49964996
}
49974997

4998+
static void nested_mark_vmcs12_pages_dirty(struct kvm_vcpu *vcpu)
4999+
{
5000+
struct vmcs12 *vmcs12 = get_vmcs12(vcpu);
5001+
gfn_t gfn;
5002+
5003+
/*
5004+
* Don't need to mark the APIC access page dirty; it is never
5005+
* written to by the CPU during APIC virtualization.
5006+
*/
5007+
5008+
if (nested_cpu_has(vmcs12, CPU_BASED_TPR_SHADOW)) {
5009+
gfn = vmcs12->virtual_apic_page_addr >> PAGE_SHIFT;
5010+
kvm_vcpu_mark_page_dirty(vcpu, gfn);
5011+
}
5012+
5013+
if (nested_cpu_has_posted_intr(vmcs12)) {
5014+
gfn = vmcs12->posted_intr_desc_addr >> PAGE_SHIFT;
5015+
kvm_vcpu_mark_page_dirty(vcpu, gfn);
5016+
}
5017+
}
5018+
5019+
49985020
static void vmx_complete_nested_posted_interrupt(struct kvm_vcpu *vcpu)
49995021
{
50005022
struct vcpu_vmx *vmx = to_vmx(vcpu);
50015023
int max_irr;
50025024
void *vapic_page;
50035025
u16 status;
50045026

5005-
if (vmx->nested.pi_desc &&
5006-
vmx->nested.pi_pending) {
5007-
vmx->nested.pi_pending = false;
5008-
if (!pi_test_and_clear_on(vmx->nested.pi_desc))
5009-
return;
5010-
5011-
max_irr = find_last_bit(
5012-
(unsigned long *)vmx->nested.pi_desc->pir, 256);
5027+
if (!vmx->nested.pi_desc || !vmx->nested.pi_pending)
5028+
return;
50135029

5014-
if (max_irr == 256)
5015-
return;
5030+
vmx->nested.pi_pending = false;
5031+
if (!pi_test_and_clear_on(vmx->nested.pi_desc))
5032+
return;
50165033

5034+
max_irr = find_last_bit((unsigned long *)vmx->nested.pi_desc->pir, 256);
5035+
if (max_irr != 256) {
50175036
vapic_page = kmap(vmx->nested.virtual_apic_page);
50185037
__kvm_apic_update_irr(vmx->nested.pi_desc->pir, vapic_page);
50195038
kunmap(vmx->nested.virtual_apic_page);
@@ -5025,6 +5044,8 @@ static void vmx_complete_nested_posted_interrupt(struct kvm_vcpu *vcpu)
50255044
vmcs_write16(GUEST_INTR_STATUS, status);
50265045
}
50275046
}
5047+
5048+
nested_mark_vmcs12_pages_dirty(vcpu);
50285049
}
50295050

50305051
static inline bool kvm_vcpu_trigger_posted_interrupt(struct kvm_vcpu *vcpu,
@@ -8072,6 +8093,18 @@ static bool nested_vmx_exit_reflected(struct kvm_vcpu *vcpu, u32 exit_reason)
80728093
vmcs_read32(VM_EXIT_INTR_ERROR_CODE),
80738094
KVM_ISA_VMX);
80748095

8096+
/*
8097+
* The host physical addresses of some pages of guest memory
8098+
* are loaded into VMCS02 (e.g. L1's Virtual APIC Page). The CPU
8099+
* may write to these pages via their host physical address while
8100+
* L2 is running, bypassing any address-translation-based dirty
8101+
* tracking (e.g. EPT write protection).
8102+
*
8103+
* Mark them dirty on every exit from L2 to prevent them from
8104+
* getting out of sync with dirty tracking.
8105+
*/
8106+
nested_mark_vmcs12_pages_dirty(vcpu);
8107+
80758108
if (vmx->nested.nested_run_pending)
80768109
return false;
80778110

0 commit comments

Comments
 (0)