From owner-svn-src-head@FreeBSD.ORG Thu Aug 1 01:18:52 2013 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTP id 3A789310; Thu, 1 Aug 2013 01:18:52 +0000 (UTC) (envelope-from grehan@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 173B02FBD; Thu, 1 Aug 2013 01:18:52 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id r711IpBW083982; Thu, 1 Aug 2013 01:18:51 GMT (envelope-from grehan@svn.freebsd.org) Received: (from grehan@localhost) by svn.freebsd.org (8.14.7/8.14.5/Submit) id r711Ip7t083981; Thu, 1 Aug 2013 01:18:51 GMT (envelope-from grehan@svn.freebsd.org) Message-Id: <201308010118.r711Ip7t083981@svn.freebsd.org> From: Peter Grehan Date: Thu, 1 Aug 2013 01:18:51 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r253849 - head/sys/amd64/vmm/intel X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 01 Aug 2013 01:18:52 -0000 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 @ 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) @@ -647,10 +647,10 @@ 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); @@ -659,26 +659,24 @@ 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) @@ -784,11 +782,17 @@ 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); } @@ -1079,7 +1083,7 @@ 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; @@ -1156,11 +1160,20 @@ 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); @@ -1615,6 +1628,27 @@ 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; @@ -1633,7 +1667,7 @@ 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; @@ -1663,6 +1697,15 @@ 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);