The emulator fetch cache is not guaranteed to contain the full architected 15-byte DecodeAssist window, e.g. it may only contain the bytes needed to decode the instruction.
Keep preparation of synthesized state limited to capturing a matching emulator fetch cache. When constructing VMCB12, copy those bytes and fetch any missing tail through L2 guest page tables, stopping at a translation fault, read failure, non-canonical address, or CS limit overrun. If no matching emulator bytes are available, fetch the full window from L2 RIP, e.g. for a #PF injected by userspace. For a non-64-bit L2, truncate each incremented linear address to 32 bits so that a fetch whose CS.base makes it cross the 4GB boundary wraps as required. Do not perform tail or fallback reads for SEV guests. KVM cannot read plaintext instruction bytes from encrypted guest memory, and the existing SEV emulation path treats missing hardware DecodeAssist bytes as unavailable instead of decoding guest memory. For nested SEV, report only matching emulator bytes that are already available, potentially a zero instruction-byte count. Signed-off-by: Tina Zhang <[email protected]> --- arch/x86/kvm/svm/nested.c | 63 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index cfb686c29bb4..cf2b6bb8126f 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -77,6 +77,54 @@ static void nested_svm_clear_synthesized_insn_bytes(struct vcpu_svm *svm) svm->nested.synthesized_insn_bytes.insn_len = 0; } +static u8 nested_svm_fetch_insn_bytes(struct kvm_vcpu *vcpu, u8 *bytes, + u8 count, u8 max_bytes) +{ + struct kvm_pagewalk *gva_walk = &vcpu->arch.gva_walk; + u64 access = PFERR_FETCH_MASK; + gva_t rip = kvm_get_linear_rip(vcpu); + struct x86_exception e; + + if (kvm_x86_call(get_cpl)(vcpu) == 3) + access |= PFERR_USER_MASK; + + if (!is_64_bit_mode(vcpu)) { + u32 eip = kvm_rip_read(vcpu); + u32 limit = to_svm(vcpu)->vmcb->save.cs.limit; + + if (eip > limit) + return 0; + max_bytes = min_t(u64, max_bytes, (u64)limit - eip + 1); + } + + count = min(count, max_bytes); + + while (count < max_bytes) { + gva_t addr = rip + count; + unsigned int chunk; + gpa_t gpa; + + if (!is_64_bit_mode(vcpu)) + addr = (u32)addr; + else if (is_noncanonical_address(addr, vcpu, 0)) + break; + + chunk = min_t(unsigned int, max_bytes - count, + PAGE_SIZE - offset_in_page(addr)); + gpa = gva_walk->gva_to_gpa(vcpu, gva_walk, addr, access, &e); + + if (gpa == INVALID_GPA || + kvm_vcpu_read_guest_page(vcpu, gpa_to_gfn(gpa), + bytes + count, + offset_in_page(gpa), chunk)) + break; + + count += chunk; + } + + return count; +} + static void nested_svm_prepare_synthesized_insn_bytes(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); @@ -110,6 +158,7 @@ static void nested_svm_update_vmcb12_insn_bytes(struct kvm_vcpu *vcpu, struct vcpu_svm *svm = to_svm(vcpu); struct nested_svm_insn_bytes *insn_bytes = &svm->nested.synthesized_insn_bytes; + const u8 max_bytes = sizeof(vmcb12->control.insn_bytes); nested_svm_clear_insn_bytes(vmcb12); @@ -125,8 +174,22 @@ static void nested_svm_update_vmcb12_insn_bytes(struct kvm_vcpu *vcpu, vmcb12->control.insn_len = insn_bytes->insn_len; memcpy(vmcb12->control.insn_bytes, insn_bytes->insn_bytes, vmcb12->control.insn_len); + + if (!is_sev_guest(vcpu)) + vmcb12->control.insn_len = + nested_svm_fetch_insn_bytes(vcpu, + vmcb12->control.insn_bytes, + vmcb12->control.insn_len, + max_bytes); + goto out; } + if (!is_sev_guest(vcpu)) + vmcb12->control.insn_len = + nested_svm_fetch_insn_bytes(vcpu, + vmcb12->control.insn_bytes, + 0, max_bytes); + out: svm->nested.vmcb02_insn_bytes_fresh = false; nested_svm_clear_synthesized_insn_bytes(svm); -- 2.43.7

