svn commit: r267144 - in projects/bhyve_svm/sys/amd64/vmm: amd intel

Peter Grehan grehan at FreeBSD.org
Fri Jun 6 02:55:19 UTC 2014


Author: grehan
Date: Fri Jun  6 02:55:18 2014
New Revision: 267144
URL: http://svnweb.freebsd.org/changeset/base/267144

Log:
  ins/outs support for SVM. Modelled on the Intel VT-x code.
  
  Remove CR2 save/restore - the guest restore/save is done
  in hardware, and there is no need to save/restore the host
  version (same as VT-x).
  
  Submitted by:	neel (SVM segment descriptor 'P' bit code)
  Reviewed by:	neel

Modified:
  projects/bhyve_svm/sys/amd64/vmm/amd/svm.c
  projects/bhyve_svm/sys/amd64/vmm/intel/vmx.c

Modified: projects/bhyve_svm/sys/amd64/vmm/amd/svm.c
==============================================================================
--- projects/bhyve_svm/sys/amd64/vmm/amd/svm.c	Fri Jun  6 01:35:52 2014	(r267143)
+++ projects/bhyve_svm/sys/amd64/vmm/amd/svm.c	Fri Jun  6 02:55:18 2014	(r267144)
@@ -90,6 +90,7 @@ static bool svm_vmexit(struct svm_softc 
 static int svm_msr_rw_ok(uint8_t *btmap, uint64_t msr);
 static int svm_msr_rd_ok(uint8_t *btmap, uint64_t msr);
 static int svm_msr_index(uint64_t msr, int *index, int *bit);
+static int svm_getdesc(void *arg, int vcpu, int type, struct seg_desc *desc);
 
 static uint32_t svm_feature; /* AMD SVM features. */
 
@@ -469,6 +470,112 @@ cleanup:
 	return (NULL);
 }
 
+static int
+svm_cpl(struct vmcb_state *state)
+{
+
+	/*
+	 * From APMv2:
+	 *   "Retrieve the CPL from the CPL field in the VMCB, not
+	 *    from any segment DPL"
+	 */
+	return (state->cpl);
+}
+
+static enum vm_cpu_mode
+svm_vcpu_mode(uint64_t efer)
+{
+
+	if (efer & EFER_LMA)
+		return (CPU_MODE_64BIT);
+	else
+		return (CPU_MODE_COMPATIBILITY);
+}
+
+static enum vm_paging_mode
+svm_paging_mode(uint64_t cr0, uint64_t cr4, uint64_t efer)
+{
+
+	if ((cr0 & CR0_PG) == 0)
+		return (PAGING_MODE_FLAT);
+	if ((cr4 & CR4_PAE) == 0)
+		return (PAGING_MODE_32);
+	if (efer & EFER_LME)
+		return (PAGING_MODE_64);
+	else
+		return (PAGING_MODE_PAE);
+}
+
+/*
+ * ins/outs utility routines
+ */
+static uint64_t
+svm_inout_str_index(struct svm_regctx *regs, int in)
+{
+	uint64_t val;
+
+	val = in ? regs->e.g.sctx_rdi : regs->e.g.sctx_rsi;
+
+	return (val);
+}
+
+static uint64_t
+svm_inout_str_count(struct svm_regctx *regs, int rep)
+{
+	uint64_t val;
+
+	val = rep ? regs->sctx_rcx : 1;
+
+	return (val);
+}
+
+static void
+svm_inout_str_seginfo(struct svm_softc *svm_sc, int vcpu, int64_t info1,
+    int in, struct vm_inout_str *vis)
+{
+	int error, s;
+
+	if (in) {
+		vis->seg_name = VM_REG_GUEST_ES;
+	} else {
+		/* The segment field has standard encoding */
+		s = (info1 >> 10) & 0x7;
+		vis->seg_name = vm_segment_name(s);
+	}
+
+	error = svm_getdesc(svm_sc, vcpu, vis->seg_name, &vis->seg_desc);
+	KASSERT(error == 0, ("%s: svm_getdesc error %d", __func__, error));
+}
+
+static int
+svm_inout_str_addrsize(uint64_t info1)
+{
+        uint32_t size;
+
+        size = (info1 >> 7) & 0x7;
+        switch (size) {
+        case 1:
+                return (2);     /* 16 bit */
+        case 2:
+                return (4);     /* 32 bit */
+        case 4:
+                return (8);     /* 64 bit */
+        default:
+                panic("%s: invalid size encoding %d", __func__, size);
+        }
+}
+
+static void
+svm_paging_info(struct vmcb_state *state, struct vm_guest_paging *paging)
+{
+
+	paging->cr3 = state->cr3;
+	paging->cpl = svm_cpl(state);
+	paging->cpu_mode = svm_vcpu_mode(state->efer);
+	paging->paging_mode = svm_paging_mode(state->cr0, state->cr4,
+		   	          state->efer);
+}
+
 /*
  * Handle guest I/O intercept.
  */
@@ -477,20 +584,36 @@ svm_handle_io(struct svm_softc *svm_sc, 
 {
 	struct vmcb_ctrl *ctrl;
 	struct vmcb_state *state;
+	struct svm_regctx *regs;
+	struct vm_inout_str *vis;
 	uint64_t info1;
 	
 	state = svm_get_vmcb_state(svm_sc, vcpu);
 	ctrl  = svm_get_vmcb_ctrl(svm_sc, vcpu);
+	regs  = svm_get_guest_regctx(svm_sc, vcpu);
 	info1 = ctrl->exitinfo1;
 	
 	vmexit->exitcode 	= VM_EXITCODE_INOUT;
 	vmexit->u.inout.in 	= (info1 & BIT(0)) ? 1 : 0;
 	vmexit->u.inout.string 	= (info1 & BIT(2)) ? 1 : 0;
-	vmexit->u. inout.rep 	= (info1 & BIT(3)) ? 1 : 0;
+	vmexit->u.inout.rep 	= (info1 & BIT(3)) ? 1 : 0;
 	vmexit->u.inout.bytes 	= (info1 >> 4) & 0x7;
 	vmexit->u.inout.port 	= (uint16_t)(info1 >> 16);
 	vmexit->u.inout.eax 	= (uint32_t)(state->rax);
 
+	if (vmexit->u.inout.string) {
+		vmexit->exitcode = VM_EXITCODE_INOUT_STR;
+		vis = &vmexit->u.inout_str;
+		svm_paging_info(state, &vis->paging);
+		vis->rflags = state->rflags;
+		vis->cr0 = state->cr0;
+		vis->index = svm_inout_str_index(regs, vmexit->u.inout.in);
+		vis->count = svm_inout_str_count(regs, vmexit->u.inout.rep);
+		vis->addrsize = svm_inout_str_addrsize(info1);
+		svm_inout_str_seginfo(svm_sc, vcpu, info1,
+		    vmexit->u.inout.in, vis);
+	}
+	
 	return (false);
 }
 
@@ -546,30 +669,6 @@ svm_efer(struct svm_softc *svm_sc, int v
 	}
 }
 
-static enum vm_cpu_mode
-svm_vcpu_mode(uint64_t efer)
-{
-
-	if (efer & EFER_LMA)
-		return (CPU_MODE_64BIT);
-	else
-		return (CPU_MODE_COMPATIBILITY);
-}
-
-static enum vm_paging_mode
-svm_paging_mode(uint64_t cr0, uint64_t cr4, uint64_t efer)
-{
-
-	if ((cr0 & CR0_PG) == 0)
-		return (PAGING_MODE_FLAT);
-	if ((cr4 & CR4_PAE) == 0)
-		return (PAGING_MODE_32);
-	if (efer & EFER_LME)
-		return (PAGING_MODE_64);
-	else
-		return (PAGING_MODE_PAE);
-}
-
 /*
  * Determine the cause of virtual cpu exit and handle VMEXIT.
  * Return: false - Break vcpu execution loop and handle vmexit
@@ -976,7 +1075,6 @@ svm_vmrun(void *arg, int vcpu, register_
 	struct vlapic *vlapic;
 	struct vm *vm;
 	uint64_t vmcb_pa;
-	static uint64_t host_cr2;
 	bool loop;	/* Continue vcpu execution loop. */
 
 	loop = true;
@@ -1078,16 +1176,9 @@ svm_vmrun(void *arg, int vcpu, register_
 		 */	
 		disable_gintr();
 
-		save_cr2(&host_cr2);
-		load_cr2(&state->cr2);
-		
-	
 		/* Launch Virtual Machine. */
 		svm_launch(vmcb_pa, gctx, hctx);
 		
-		save_cr2(&state->cr2);
-		load_cr2(&host_cr2);
-
 		/*
 		 * Only GDTR and IDTR of host is saved and restore by SVM,
 		 * LDTR and TR need to be restored by VMM.
@@ -1303,6 +1394,21 @@ svm_getdesc(void *arg, int vcpu, int typ
 	desc->base = seg->base;
 	desc->limit = seg->limit;
 
+	/*
+	 * VT-x uses bit 16 (Unusable) to indicate a segment that has been
+	 * loaded with a NULL segment selector. The 'desc->access' field is
+	 * interpreted in the VT-x format by the processor-independent code.
+	 *
+	 * SVM uses the 'P' bit to convey the same information so convert it
+	 * into the VT-x format. For more details refer to section
+	 * "Segment State in the VMCB" in APMv2.
+	 */
+	if (type == VM_REG_GUEST_CS && type == VM_REG_GUEST_TR)
+		desc->access |= 0x80;		/* CS and TS always present */
+
+	if (!(desc->access & 0x80))
+		desc->access |= 0x10000;	/* Unusable segment */
+
 	return (0);
 }
 

Modified: projects/bhyve_svm/sys/amd64/vmm/intel/vmx.c
==============================================================================
--- projects/bhyve_svm/sys/amd64/vmm/intel/vmx.c	Fri Jun  6 01:35:52 2014	(r267143)
+++ projects/bhyve_svm/sys/amd64/vmm/intel/vmx.c	Fri Jun  6 02:55:18 2014	(r267144)
@@ -1603,8 +1603,6 @@ inout_str_seginfo(struct vmx *vmx, int v
 
 	error = vmx_getdesc(vmx, vcpuid, vis->seg_name, &vis->seg_desc);
 	KASSERT(error == 0, ("%s: vmx_getdesc error %d", __func__, error));
-
-	/* XXX modify svm.c to update bit 16 of seg_desc.access (unusable) */
 }
 
 static void


More information about the svn-src-projects mailing list