svn commit: r253849 - head/sys/amd64/vmm/intel

From: Peter Grehan <grehan_at_FreeBSD.org>
Date: Thu, 1 Aug 2013 01:18:51 +0000 (UTC)
Author: grehan
Date: Thu Aug  1 01:18:51 2013
New Revision: 253849
URL: http://svnweb.freebsd.org/changeset/base/253849

Log:
  Correctly maintain the CR0/CR4 shadow registers.
  This was exposed with AP spinup of Linux, and
  booting OpenBSD, where the CR0 register is unconditionally
  written to prior to the longjump to enter protected
  mode. The CR-vmexit handling was not updating CPU state which
  resulted in a vmentry failure with invalid guest state.
  
  A follow-on submit will fix the CPU state issue, but this
  fix prevents the CR-vmexit prior to entering protected
  mode by properly initializing and maintaining CR* state.
  
  Reviewed by:	neel
  Reported by:	Gopakumar.T _at_ netapp

Modified:
  head/sys/amd64/vmm/intel/vmx.c

Modified: head/sys/amd64/vmm/intel/vmx.c
==============================================================================
--- head/sys/amd64/vmm/intel/vmx.c	Wed Jul 31 22:54:02 2013	(r253848)
+++ head/sys/amd64/vmm/intel/vmx.c	Thu Aug  1 01:18:51 2013	(r253849)
_at__at_ -647,10 +647,10 _at__at_ vmx_vpid(void)
 }
 
 static int
-vmx_setup_cr_shadow(int which, struct vmcs *vmcs)
+vmx_setup_cr_shadow(int which, struct vmcs *vmcs, uint32_t initial)
 {
 	int error, mask_ident, shadow_ident;
-	uint64_t mask_value, shadow_value;
+	uint64_t mask_value;
 
 	if (which != 0 && which != 4)
 		panic("vmx_setup_cr_shadow: unknown cr%d", which);
_at__at_ -659,26 +659,24 _at__at_ vmx_setup_cr_shadow(int which, struct vm
 		mask_ident = VMCS_CR0_MASK;
 		mask_value = cr0_ones_mask | cr0_zeros_mask;
 		shadow_ident = VMCS_CR0_SHADOW;
-		shadow_value = cr0_ones_mask;
 	} else {
 		mask_ident = VMCS_CR4_MASK;
 		mask_value = cr4_ones_mask | cr4_zeros_mask;
 		shadow_ident = VMCS_CR4_SHADOW;
-		shadow_value = cr4_ones_mask;
 	}
 
 	error = vmcs_setreg(vmcs, 0, VMCS_IDENT(mask_ident), mask_value);
 	if (error)
 		return (error);
 
-	error = vmcs_setreg(vmcs, 0, VMCS_IDENT(shadow_ident), shadow_value);
+	error = vmcs_setreg(vmcs, 0, VMCS_IDENT(shadow_ident), initial);
 	if (error)
 		return (error);
 
 	return (0);
 }
-#define	vmx_setup_cr0_shadow(vmcs)	vmx_setup_cr_shadow(0, (vmcs))
-#define	vmx_setup_cr4_shadow(vmcs)	vmx_setup_cr_shadow(4, (vmcs))
+#define	vmx_setup_cr0_shadow(vmcs,init)	vmx_setup_cr_shadow(0, (vmcs), (init))
+#define	vmx_setup_cr4_shadow(vmcs,init)	vmx_setup_cr_shadow(4, (vmcs), (init))
 
 static void *
 vmx_vminit(struct vm *vm)
_at__at_ -784,11 +782,17 _at__at_ vmx_vminit(struct vm *vm)
 		if (error != 0)
 			panic("vmcs_set_msr_save error %d", error);
 
-		error = vmx_setup_cr0_shadow(&vmx->vmcs[i]);
+		/*
+		 * Set up the CR0/4 shadows, and init the read shadow
+		 * to the power-on register value from the Intel Sys Arch.
+		 *  CR0 - 0x60000010
+		 *  CR4 - 0
+		 */
+		error = vmx_setup_cr0_shadow(&vmx->vmcs[i], 0x60000010);
 		if (error != 0)
 			panic("vmx_setup_cr0_shadow %d", error);
 
-		error = vmx_setup_cr4_shadow(&vmx->vmcs[i]);
+		error = vmx_setup_cr4_shadow(&vmx->vmcs[i], 0);
 		if (error != 0)
 			panic("vmx_setup_cr4_shadow %d", error);
 	}
_at__at_ -1079,7 +1083,7 _at__at_ cantinject:
 static int
 vmx_emulate_cr_access(struct vmx *vmx, int vcpu, uint64_t exitqual)
 {
-	int error, cr, vmcs_guest_cr;
+	int error, cr, vmcs_guest_cr, vmcs_shadow_cr;
 	uint64_t regval, ones_mask, zeros_mask;
 	const struct vmxctx *vmxctx;
 
_at__at_ -1156,11 +1160,20 _at__at_ vmx_emulate_cr_access(struct vmx *vmx, i
 		ones_mask = cr0_ones_mask;
 		zeros_mask = cr0_zeros_mask;
 		vmcs_guest_cr = VMCS_GUEST_CR0;
+		vmcs_shadow_cr = VMCS_CR0_SHADOW;
 	} else {
 		ones_mask = cr4_ones_mask;
 		zeros_mask = cr4_zeros_mask;
 		vmcs_guest_cr = VMCS_GUEST_CR4;
+		vmcs_shadow_cr = VMCS_CR4_SHADOW;
+	}
+
+	error = vmwrite(vmcs_shadow_cr, regval);
+	if (error) {
+		panic("vmx_emulate_cr_access: error %d writing cr%d shadow",
+		      error, cr);
 	}
+
 	regval |= ones_mask;
 	regval &= ~zeros_mask;
 	error = vmwrite(vmcs_guest_cr, regval);
_at__at_ -1615,6 +1628,27 _at__at_ vmxctx_setreg(struct vmxctx *vmxctx, int
 }
 
 static int
+vmx_shadow_reg(int reg)
+{
+	int shreg;
+
+	shreg = -1;
+
+	switch (reg) {
+	case VM_REG_GUEST_CR0:
+		shreg = VMCS_CR0_SHADOW;
+                break;
+        case VM_REG_GUEST_CR4:
+		shreg = VMCS_CR4_SHADOW;
+		break;
+	default:
+		break;
+	}
+
+	return (shreg);
+}
+
+static int
 vmx_getreg(void *arg, int vcpu, int reg, uint64_t *retval)
 {
 	int running, hostcpu;
_at__at_ -1633,7 +1667,7 _at__at_ vmx_getreg(void *arg, int vcpu, int reg,
 static int
 vmx_setreg(void *arg, int vcpu, int reg, uint64_t val)
 {
-	int error, hostcpu, running;
+	int error, hostcpu, running, shadow;
 	uint64_t ctls;
 	struct vmx *vmx = arg;
 
_at__at_ -1663,6 +1697,15 _at__at_ vmx_setreg(void *arg, int vcpu, int reg,
 			vmcs_setreg(&vmx->vmcs[vcpu], running,
 				    VMCS_IDENT(VMCS_ENTRY_CTLS), ctls);
 		}
+
+		shadow = vmx_shadow_reg(reg);
+		if (shadow > 0) {
+			/*
+			 * Store the unmodified value in the shadow
+			 */			
+			error = vmcs_setreg(&vmx->vmcs[vcpu], running,
+				    VMCS_IDENT(shadow), val);
+		}
 	}
 
 	return (error);
Received on Thu Aug 01 2013 - 01:18:52 UTC