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)

Reply via email to