On Mon, Jul 06, 2026, Jim Mattson wrote:
> On Mon, Jul 6, 2026 at 8:21 PM Sean Christopherson <[email protected]> wrote:
> > > For KVM-synthesized/emulated #PF/#NPF nested VM-Exits there is no fresh
> > > hardware GuestInstrBytes state to propagate to VMCB12. So I agree that it
> > > should be simpler to avoid plumbing instruction bytes from the emulator,
> > > and
> > > instead fetch the bytes late when constructing the nested VM-Exit for L1.
> > >
> > > I'll try this direction first.
> >
> > I'd rather not, at least not as the primary way. It's going to raise a
> > different
> > set of issues, e.g. how to behave if reading guest memory fails. That's
> > probably
> > unavoidable, e.g. if reading the INVPCID descriptor fails, then KVM doesn't
> > even
> > have a pre-decoded instruction to work with, but it should ideally be a last
> > resort.
> >
> > E.g. where the NULL case triggers an on-demand read of guest memory.
> >
> > diff --git arch/x86/kvm/svm/nested.c arch/x86/kvm/svm/nested.c
> > index ba985a02208a..812af3a8d8b9 100644
> > --- arch/x86/kvm/svm/nested.c
> > +++ arch/x86/kvm/svm/nested.c
> > @@ -42,6 +42,7 @@ static void nested_svm_inject_npf_exit(struct kvm_vcpu
> > *vcpu,
> > struct vcpu_svm *svm = to_svm(vcpu);
> > struct vmcb *vmcb = svm->vmcb;
> > u64 fault_stage;
> > + u8 *insn_bytes;
> >
> > /*
> > * For hardware NPF exits, the GUEST_FAULT_STAGE bits are only
> > @@ -68,7 +69,14 @@ static void nested_svm_inject_npf_exit(struct kvm_vcpu
> > *vcpu,
> > (fault->error_code &
> > ~PFERR_GUEST_FAULT_STAGE_MASK);
> > vmcb->control.exit_info_2 = fault->address;
> >
> > - nested_svm_vmexit(svm);
> > + if (from_hardware)
> > + insn_bytes = svm->nested.vmcb02.ptr->control.insn_bytes;
> > + else if (vcpu->arch.emulate_ctxt->eip == kvm_rip_read(vcpu))
> > + insn_bytes = vcpu->arch.emulate_ctxt->fetch.data
>
> Won't this be shy of the architected 15 bytes if RIP is within 15
> bytes of a page crossing and the instruction ends before the page
> crossing?
Ugh, x86 is so annoying. Though we need to plumb the number of bytes even when
when pulling from vmcb02.
Expose kvm_fetch_guest_virt() and wire up a wrapper for the emulator, then this?
int insn_len = 0, len;
u8 *insn = NULL;
u64 fault_stage;
/*
* For hardware NPF exits, the GUEST_FAULT_STAGE bits are only
* available in the hardware exit_info_1, since the guest_mmu
* walker doesn't know whether the faulting GPA was a page table
* page or final page from L2's perspective.
*/
if (from_hardware)
fault_stage = vmcb->control.exit_info_1 &
PFERR_GUEST_FAULT_STAGE_MASK;
else
fault_stage = fault->error_code & PFERR_GUEST_FAULT_STAGE_MASK;
/*
* All nested page faults should be annotated as occurring on the
* final translation *or* the page walk. Arbitrarily choose "final"
* if KVM is buggy and enumerated both or neither.
*/
if (WARN_ON_ONCE(hweight64(fault_stage) != 1))
fault_stage = PFERR_GUEST_FINAL_MASK;
vmcb->control.exit_code = SVM_EXIT_NPF;
vmcb->control.exit_info_1 = fault_stage |
(fault->error_code &
~PFERR_GUEST_FAULT_STAGE_MASK);
vmcb->control.exit_info_2 = fault->address;
if (fault->error_code & PFERR_FETCH_MASK)
goto synthesize_vmexit;
if (from_hardware) {
insn = svm->nested.vmcb02.ptr->control.insn_bytes;
insn_len = svm->nested.vmcb02.ptr->control.insn_len;
goto synthesize_vmexit;
}
if (WARN_ON_ONCE(!ctxt))
goto synthesize_vmexit;
insn = ctxt->fetch.data;
if (ctxt->eip == kvm_rip_read(vcpu))
insn_len = ctxt->fetch.end - ctxt->fetch.ptr;
for (len = X86_MAX_INSTRUCTION_LENGTH - insn_len; len >= 0; len--) {
if (!kvm_fetch_guest_virt(vcpu, kvm_rip_read(vcpu) + insn_len,
&insn[insn_len], len, NULL))
break;
}
synthesize_vmexit:
nested_svm_vmexit(svm, insn, insn_len);
> > + else
> > + insn_bytes = NULL;
> > +
> > + nested_svm_vmexit(svm, insn_bytes);
> > }
> >
> > static u64 nested_svm_get_tdp_pdptr(struct kvm_vcpu *vcpu, int index)