tech@,

Continuing my vmm/vmd bug hunt, the following diff adapts
vcpu_readregs_vmx to optionally load the vmcs on the current cpu. This
has gone unnoticed as the ioctl isn't used in typical vmd usage and the
usage of vcpu_readregs_vmx in the run ioctl is after the vmcs is already
loaded on the current cpu.

This fixes `vmctl send` on Intel hosts. (A fix for `vmctl receive` comes
next.)

Currently, `vmctl send` tries to serialize the vcpu registers as part of
serializing the vm state. On an MP machine, it's highly probable that
the vmread instructions will fail as they'll be executed on a cpu that
doesn't have the vmcs loaded.

While here, I noticed the vcpu_writeregs_vmx function doesn't set the
vcpu's vmcs state variable to VMCS_CLEARED after running vmclear. This
can cause failure to vm-enter as vmm uses that state to determine which
of the two Intel instructions to call (vmlaunch or vmresume).

ok?

-dv

Index: sys/arch/amd64/amd64/vmm.c
===================================================================
RCS file: /opt/cvs/src/sys/arch/amd64/amd64/vmm.c,v
retrieving revision 1.308
diff -u -p -r1.308 vmm.c
--- sys/arch/amd64/amd64/vmm.c  4 May 2022 02:24:26 -0000       1.308
+++ sys/arch/amd64/amd64/vmm.c  8 May 2022 18:37:42 -0000
@@ -140,7 +140,7 @@ int vm_rwregs(struct vm_rwregs_params *,
 int vm_mprotect_ept(struct vm_mprotect_ept_params *);
 int vm_rwvmparams(struct vm_rwvmparams_params *, int);
 int vm_find(uint32_t, struct vm **);
-int vcpu_readregs_vmx(struct vcpu *, uint64_t, struct vcpu_reg_state *);
+int vcpu_readregs_vmx(struct vcpu *, uint64_t, int, struct vcpu_reg_state *);
 int vcpu_readregs_svm(struct vcpu *, uint64_t, struct vcpu_reg_state *);
 int vcpu_writeregs_vmx(struct vcpu *, uint64_t, int, struct vcpu_reg_state *);
 int vcpu_writeregs_svm(struct vcpu *, uint64_t, struct vcpu_reg_state *);
@@ -978,7 +978,7 @@ vm_rwregs(struct vm_rwregs_params *vrwp,
        if (vmm_softc->mode == VMM_MODE_VMX ||
            vmm_softc->mode == VMM_MODE_EPT)
                ret = (dir == 0) ?
-                   vcpu_readregs_vmx(vcpu, vrwp->vrwp_mask, vrs) :
+                   vcpu_readregs_vmx(vcpu, vrwp->vrwp_mask, 1, vrs) :
                    vcpu_writeregs_vmx(vcpu, vrwp->vrwp_mask, 1, vrs);
        else if (vmm_softc->mode == VMM_MODE_SVM ||
            vmm_softc->mode == VMM_MODE_RVI)
@@ -1986,6 +1986,7 @@ vcpu_reload_vmcs_vmx(struct vcpu *vcpu)
  * Parameters:
  *  vcpu: the vcpu to read register values from
  *  regmask: the types of registers to read
+ *  loadvmcs: bit to indicate whether the VMCS has to be loaded first
  *  vrs: output parameter where register values are stored
  *
  * Return values:
@@ -1993,7 +1994,7 @@ vcpu_reload_vmcs_vmx(struct vcpu *vcpu)
  *  EINVAL: an error reading registers occurred
  */
 int
-vcpu_readregs_vmx(struct vcpu *vcpu, uint64_t regmask,
+vcpu_readregs_vmx(struct vcpu *vcpu, uint64_t regmask, int loadvmcs,
     struct vcpu_reg_state *vrs)
 {
        int i, ret = 0;
@@ -2005,6 +2006,11 @@ vcpu_readregs_vmx(struct vcpu *vcpu, uin
        struct vcpu_segment_info *sregs = vrs->vrs_sregs;
        struct vmx_msr_store *msr_store;

+       if (loadvmcs) {
+               if (vcpu_reload_vmcs_vmx(vcpu))
+                       return (EINVAL);
+       }
+
 #ifdef VMM_DEBUG
        /* VMCS should be loaded... */
        paddr_t pa = 0ULL;
@@ -2393,6 +2399,7 @@ out:
        if (loadvmcs) {
                if (vmclear(&vcpu->vc_control_pa))
                        ret = EINVAL;
+               atomic_swap_uint(&vcpu->vc_vmx_vmcs_state, VMCS_CLEARED);
        }
        return (ret);
 }
@@ -4631,7 +4638,7 @@ vmm_translate_gva(struct vcpu *vcpu, uin

        if (vmm_softc->mode == VMM_MODE_EPT ||
            vmm_softc->mode == VMM_MODE_VMX) {
-               if (vcpu_readregs_vmx(vcpu, VM_RWREGS_ALL, &vrs))
+               if (vcpu_readregs_vmx(vcpu, VM_RWREGS_ALL, 1, &vrs))
                        return (EINVAL);
        } else if (vmm_softc->mode == VMM_MODE_RVI ||
            vmm_softc->mode == VMM_MODE_SVM) {
@@ -5111,7 +5118,7 @@ vcpu_run_vmx(struct vcpu *vcpu, struct v
        vcpu->vc_last_pcpu = curcpu();

        /* Copy the VCPU register state to the exit structure */
-       if (vcpu_readregs_vmx(vcpu, VM_RWREGS_ALL, &vcpu->vc_exit.vrs))
+       if (vcpu_readregs_vmx(vcpu, VM_RWREGS_ALL, 0, &vcpu->vc_exit.vrs))
                ret = EINVAL;
        vcpu->vc_exit.cpl = vmm_get_guest_cpu_cpl(vcpu);

Reply via email to