@@ -7872,6 +7872,51 @@ int x86_decode_emulated_instruction(struct kvm_vcpu *vcpu, int emulation_type,
}
EXPORT_SYMBOL_GPL(x86_decode_emulated_instruction);
+/*
+ * With introspection enabled, emulation failures translate in events being
+ * missed because the read/write callbacks are not invoked. All we have is
+ * the fetch event (kvm_page_track_preexec). Below we use the EPT/NPT VMEXIT
+ * information to generate the events, but without providing accurate
+ * data and size (the emulator would have computed those). If an instruction
+ * would happen to read and write in the same page, the second event will
+ * initially be missed and we rely on the page tracking mechanism to bring
+ * us back here to send it.
+ */
+static bool kvm_page_track_emulation_failure(struct kvm_vcpu *vcpu, gpa_t gpa)
+{
+ u64 error_code = vcpu->arch.error_code;
+ u8 data = 0;
+ gva_t gva;
+ bool ret;
+
+ /* MMIO emulation failures should be treated the normal way */
+ if (unlikely(error_code & PFERR_RSVD_MASK))
+ return true;
+
+ /* EPT/NTP must be enabled */
+ if (unlikely(!vcpu->arch.mmu->direct_map))
+ return true;
+
+ /*
+ * The A/D bit emulation should make this test unneeded, but just
+ * in case
+ */
+ if (unlikely((error_code & PFERR_NESTED_GUEST_PAGE) ==
+ PFERR_NESTED_GUEST_PAGE))
+ return true;
+
+ gva = static_call(kvm_x86_fault_gla)(vcpu);
+
+ if (error_code & PFERR_WRITE_MASK)
+ ret = kvm_page_track_prewrite(vcpu, gpa, gva, &data, 0);
+ else if (error_code & PFERR_USER_MASK)
+ ret = kvm_page_track_preread(vcpu, gpa, gva, 0);
+ else
+ ret = true;
+
+ return ret;
+}
+
int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
int emulation_type, void *insn, int insn_len)
{
@@ -7905,6 +7950,8 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
kvm_queue_exception(vcpu, UD_VECTOR);
return 1;
}
+ if (!kvm_page_track_emulation_failure(vcpu, cr2_or_gpa))
+ return 1;
if (reexecute_instruction(vcpu, cr2_or_gpa,
write_fault_to_spt,
emulation_type))
@@ -7974,6 +8021,8 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
return 1;
if (r == EMULATION_FAILED) {
+ if (!kvm_page_track_emulation_failure(vcpu, cr2_or_gpa))
+ return 1;
if (reexecute_instruction(vcpu, cr2_or_gpa, write_fault_to_spt,
emulation_type))
return 1;