Author: jhb
Date: Sun Nov 18 01:07:36 2018
New Revision: 340545
URL: https://svnweb.freebsd.org/changeset/base/340545

Log:
  MFC 339312,339364: Restore more descriptors during VM exits.
  
  339312:
  Fully restore the GDTR, IDTR, and LDTR after VT-x VM exits.
  
  The VT-x VMCS only stores the base address of the GDTR and IDTR.  As a
  result, VM exits use a fixed limit of 0xffff for the host GDTR and
  IDTR losing the smaller limits set in when the initial GDT is loaded
  on each CPU during boot.  Explicitly save and restore the full GDTR
  and IDTR contents around VM entries and exits to restore the correct
  limit.
  
  Similarly, explicitly save and restore the LDT selector.  VM exits
  always clear the host LDTR as if the LDT was loaded with a NULL
  selector and a userspace hypervisor is probably using a NULL selector
  anyway, but save and restore the LDT explicitly just to be safe.
  
  339364:
  Reload the LDT selector after an AMD-v #VMEXIT.
  
  cpu_switch() always reloads the LDT, so this can only affect the
  hypervisor process itself.  Fix this by explicitly reloading the host
  LDT selector after each #VMEXIT.  The stock bhyve process on FreeBSD
  never uses a custom LDT, so this change is cosmetic.
  
  PR:           230773

Modified:
  stable/11/sys/amd64/include/cpufunc.h
  stable/11/sys/amd64/vmm/amd/svm.c
  stable/11/sys/amd64/vmm/intel/vmx.c
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sys/amd64/include/cpufunc.h
==============================================================================
--- stable/11/sys/amd64/include/cpufunc.h       Sun Nov 18 01:04:53 2018        
(r340544)
+++ stable/11/sys/amd64/include/cpufunc.h       Sun Nov 18 01:07:36 2018        
(r340545)
@@ -726,6 +726,15 @@ lldt(u_short sel)
        __asm __volatile("lldt %0" : : "r" (sel));
 }
 
+static __inline u_short
+sldt(void)
+{
+       u_short sel;
+
+       __asm __volatile("sldt %0" : "=r" (sel));
+       return (sel);
+}
+
 static __inline void
 ltr(u_short sel)
 {

Modified: stable/11/sys/amd64/vmm/amd/svm.c
==============================================================================
--- stable/11/sys/amd64/vmm/amd/svm.c   Sun Nov 18 01:04:53 2018        
(r340544)
+++ stable/11/sys/amd64/vmm/amd/svm.c   Sun Nov 18 01:07:36 2018        
(r340545)
@@ -1940,6 +1940,7 @@ svm_vmrun(void *arg, int vcpu, register_t rip, pmap_t 
        struct vm *vm;
        uint64_t vmcb_pa;
        int handled;
+       uint16_t ldt_sel;
 
        svm_sc = arg;
        vm = svm_sc->vm;
@@ -2018,6 +2019,15 @@ svm_vmrun(void *arg, int vcpu, register_t rip, pmap_t 
                        break;
                }
 
+               /*
+                * #VMEXIT resumes the host with the guest LDTR, so
+                * save the current LDT selector so it can be restored
+                * after an exit.  The userspace hypervisor probably
+                * doesn't use a LDT, but save and restore it to be
+                * safe.
+                */
+               ldt_sel = sldt();
+
                svm_inj_interrupts(svm_sc, vcpu, vlapic);
 
                /* Activate the nested pmap on 'curcpu' */
@@ -2047,6 +2057,9 @@ svm_vmrun(void *arg, int vcpu, register_t rip, pmap_t 
                 * to be restored explicitly.
                 */
                restore_host_tss();
+
+               /* Restore host LDTR. */
+               lldt(ldt_sel);
 
                /* #VMEXIT disables interrupts so re-enable them here. */ 
                enable_gintr();

Modified: stable/11/sys/amd64/vmm/intel/vmx.c
==============================================================================
--- stable/11/sys/amd64/vmm/intel/vmx.c Sun Nov 18 01:04:53 2018        
(r340544)
+++ stable/11/sys/amd64/vmm/intel/vmx.c Sun Nov 18 01:07:36 2018        
(r340545)
@@ -2701,6 +2701,8 @@ vmx_run(void *arg, int vcpu, register_t rip, pmap_t pm
        struct vm_exit *vmexit;
        struct vlapic *vlapic;
        uint32_t exit_reason;
+       struct region_descriptor gdtr, idtr;
+       uint16_t ldt_sel;
 
        vmx = arg;
        vm = vmx->vm;
@@ -2786,10 +2788,30 @@ vmx_run(void *arg, int vcpu, register_t rip, pmap_t pm
                        break;
                }
 
+               /*
+                * VM exits restore the base address but not the
+                * limits of GDTR and IDTR.  The VMCS only stores the
+                * base address, so VM exits set the limits to 0xffff.
+                * Save and restore the full GDTR and IDTR to restore
+                * the limits.
+                *
+                * The VMCS does not save the LDTR at all, and VM
+                * exits clear LDTR as if a NULL selector were loaded.
+                * The userspace hypervisor probably doesn't use a
+                * LDT, but save and restore it to be safe.
+                */
+               sgdt(&gdtr);
+               sidt(&idtr);
+               ldt_sel = sldt();
+
                vmx_run_trace(vmx, vcpu);
                vmx_dr_enter_guest(vmxctx);
                rc = vmx_enter_guest(vmxctx, vmx, launched);
                vmx_dr_leave_guest(vmxctx);
+
+               bare_lgdt(&gdtr);
+               lidt(&idtr);
+               lldt(ldt_sel);
 
                /* Collect some information for VM exit processing */
                vmexit->rip = rip = vmcs_guest_rip();
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to