Preserve the bytes used by the emulator when synthesizing a data #NPF. Reuse the fetch cache only when the fault is the current emulator exception, identified by fault == &ctxt->exception.
Add an emulator accessor to copy cached instruction bytes, checking the RIP and cache bounds. These checks alone do not establish that the cache belongs to the current emulation. Write the cached bytes directly to VMCB02 and fetch any missing tail through the instruction-fetch helper. If the tail cannot be read, report only the bytes already available. For SEV guests, retain only the cached bytes because KVM cannot fetch plaintext guest instructions. Signed-off-by: Tina Zhang <[email protected]> --- arch/x86/kvm/emulate.c | 22 ++++++++++++++++++++++ arch/x86/kvm/kvm_emulate.h | 3 +++ arch/x86/kvm/svm/nested.c | 34 ++++++++++++++++++++++++++++++++-- 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 1dfece6af81e..5017a9aa1d4b 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -913,6 +913,28 @@ static __always_inline int do_insn_fetch_bytes(struct x86_emulate_ctxt *ctxt, return X86EMUL_CONTINUE; } +/* + * The caller is responsible for ensuring that the fetch cache belongs to + * the current instruction. Matching EIP alone does not guarantee this. + */ +unsigned int x86_emulator_copy_insn_bytes(struct x86_emulate_ctxt *ctxt, + unsigned long eip, u8 *bytes, + unsigned int max_bytes) +{ + unsigned int nr_bytes; + + if (!ctxt || ctxt->eip != eip || + ctxt->fetch.end < ctxt->fetch.data || + ctxt->fetch.end > ctxt->fetch.data + sizeof(ctxt->fetch.data)) + return 0; + + nr_bytes = min_t(unsigned int, ctxt->fetch.end - ctxt->fetch.data, + max_bytes); + memcpy(bytes, ctxt->fetch.data, nr_bytes); + return nr_bytes; +} +EXPORT_SYMBOL_FOR_KVM_INTERNAL(x86_emulator_copy_insn_bytes); + /* Fetch next part of the instruction being emulated. */ #define insn_fetch(_type, _ctxt) \ ({ _type _x; \ diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h index d05558ef28ae..5361d42e6e05 100644 --- a/arch/x86/kvm/kvm_emulate.h +++ b/arch/x86/kvm/kvm_emulate.h @@ -528,6 +528,9 @@ enum x86_intercept { }; int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len, int emulation_type); +unsigned int x86_emulator_copy_insn_bytes(struct x86_emulate_ctxt *ctxt, + unsigned long eip, u8 *bytes, + unsigned int max_bytes); bool x86_page_table_writing_insn(struct x86_emulate_ctxt *ctxt); #define EMULATION_FAILED -1 #define EMULATION_OK 0 diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index 5cb6a9d6907b..2dc515d73eb6 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -93,13 +93,13 @@ static u8 nested_svm_get_insn_bytes_len(struct kvm_vcpu *vcpu, u8 max_bytes) } static u8 nested_svm_fetch_insn_bytes(struct kvm_vcpu *vcpu, u8 *bytes, - u8 max_bytes) + u8 count, u8 max_bytes) { struct x86_exception e; gva_t rip = kvm_get_linear_rip(vcpu); - u8 count = 0; max_bytes = nested_svm_get_insn_bytes_len(vcpu, max_bytes); + count = min(count, max_bytes); while (count < max_bytes) { gva_t addr = rip + count; @@ -125,6 +125,31 @@ static u8 nested_svm_fetch_insn_bytes(struct kvm_vcpu *vcpu, u8 *bytes, return count; } +static void nested_svm_prepare_synthesized_insn_bytes(struct kvm_vcpu *vcpu) +{ + struct vcpu_svm *svm = to_svm(vcpu); + struct vmcb *vmcb02 = svm->nested.vmcb02.ptr; + struct x86_emulate_ctxt *ctxt = vcpu->arch.emulate_ctxt; + u8 max_bytes = sizeof(vmcb02->control.insn_bytes); + u8 count; + + nested_svm_invalidate_vmcb02_insn_bytes(svm); + + if (!guest_cpu_cap_has(vcpu, X86_FEATURE_DECODEASSISTS)) + return; + + max_bytes = nested_svm_get_insn_bytes_len(vcpu, max_bytes); + count = x86_emulator_copy_insn_bytes(ctxt, kvm_rip_read(vcpu), + vmcb02->control.insn_bytes, max_bytes); + if (!is_sev_guest(vcpu)) + count = nested_svm_fetch_insn_bytes(vcpu, + vmcb02->control.insn_bytes, + count, max_bytes); + vmcb02->control.insn_len = count; + + svm->nested.vmcb02_insn_bytes_valid = true; +} + static void nested_svm_update_vmcb12_insn_bytes(struct kvm_vcpu *vcpu, struct vmcb *vmcb12, const struct vmcb *vmcb02) @@ -149,6 +174,7 @@ static void nested_svm_update_vmcb12_insn_bytes(struct kvm_vcpu *vcpu, vmcb12->control.insn_len = nested_svm_fetch_insn_bytes(vcpu, vmcb12->control.insn_bytes, + 0, max_bytes); else nested_svm_invalidate_insn_bytes(vmcb12); @@ -163,6 +189,8 @@ static void nested_svm_inject_npf_exit(struct kvm_vcpu *vcpu, { struct vcpu_svm *svm = to_svm(vcpu); struct vmcb *vmcb = svm->vmcb; + struct x86_emulate_ctxt *ctxt = vcpu->arch.emulate_ctxt; + bool from_emulation = ctxt && fault == &ctxt->exception; u64 fault_stage; /* @@ -192,6 +220,8 @@ static void nested_svm_inject_npf_exit(struct kvm_vcpu *vcpu, if (from_hardware) nested_svm_set_vmcb02_insn_bytes_valid(svm); + else if (from_emulation && !(fault->error_code & PFERR_FETCH_MASK)) + nested_svm_prepare_synthesized_insn_bytes(vcpu); else svm->nested.vmcb02_insn_bytes_valid = false; nested_svm_vmexit(svm); -- 2.43.7

