From mboxrd@z Thu Jan 1 00:00:00 1970 From: Eddie Dong Subject: [PATCH 05 of 20] Emulation of guest VMXON/OFF instruction Date: Thu, 02 Jun 2011 16:57:18 +0800 Message-ID: References: Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Return-path: In-Reply-To: List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xensource.com Errors-To: xen-devel-bounces@lists.xensource.com To: Tim.Deegan@citrix.com Cc: xen-devel@lists.xensource.com List-Id: xen-devel@lists.xenproject.org # HG changeset patch # User Eddie Dong # Date 1307003600 -28800 # Node ID c8812151acfd6d9468f3407bc6a1a278cd764567 # Parent 4e094881883f10f94575a6f69194a2393e16b7d1 Emulation of guest VMXON/OFF instruction. Signed-off-by: Qing He Signed-off-by: Eddie Dong diff -r 4e094881883f -r c8812151acfd xen/arch/x86/hvm/vmx/Makefile --- a/xen/arch/x86/hvm/vmx/Makefile Thu Jun 02 16:33:20 2011 +0800 +++ b/xen/arch/x86/hvm/vmx/Makefile Thu Jun 02 16:33:20 2011 +0800 @@ -5,3 +5,4 @@ obj-y += vmcs.o obj-y += vmx.o obj-y += vpmu_core2.o obj-y += vvmx.o +obj-y += vvmx.o diff -r 4e094881883f -r c8812151acfd xen/arch/x86/hvm/vmx/vmx.c --- a/xen/arch/x86/hvm/vmx/vmx.c Thu Jun 02 16:33:20 2011 +0800 +++ b/xen/arch/x86/hvm/vmx/vmx.c Thu Jun 02 16:33:20 2011 +0800 @@ -2434,6 +2434,16 @@ asmlinkage void vmx_vmexit_handler(struc break; } + case EXIT_REASON_VMXOFF: + if ( nvmx_handle_vmxoff(regs) == X86EMUL_OKAY ) + update_guest_eip(); + break; + + case EXIT_REASON_VMXON: + if ( nvmx_handle_vmxon(regs) == X86EMUL_OKAY ) + update_guest_eip(); + break; + case EXIT_REASON_MWAIT_INSTRUCTION: case EXIT_REASON_MONITOR_INSTRUCTION: case EXIT_REASON_VMCLEAR: @@ -2443,8 +2453,6 @@ asmlinkage void vmx_vmexit_handler(struc case EXIT_REASON_VMREAD: case EXIT_REASON_VMRESUME: case EXIT_REASON_VMWRITE: - case EXIT_REASON_VMXOFF: - case EXIT_REASON_VMXON: case EXIT_REASON_GETSEC: case EXIT_REASON_INVEPT: case EXIT_REASON_INVVPID: diff -r 4e094881883f -r c8812151acfd xen/arch/x86/hvm/vmx/vvmx.c --- a/xen/arch/x86/hvm/vmx/vvmx.c Thu Jun 02 16:33:20 2011 +0800 +++ b/xen/arch/x86/hvm/vmx/vvmx.c Thu Jun 02 16:33:20 2011 +0800 @@ -91,3 +91,228 @@ uint32_t nvmx_vcpu_asid(struct vcpu *v) return 0; } +enum x86_segment sreg_to_index[] = { + [VMX_SREG_ES] = x86_seg_es, + [VMX_SREG_CS] = x86_seg_cs, + [VMX_SREG_SS] = x86_seg_ss, + [VMX_SREG_DS] = x86_seg_ds, + [VMX_SREG_FS] = x86_seg_fs, + [VMX_SREG_GS] = x86_seg_gs, +}; + +struct vmx_inst_decoded { +#define VMX_INST_MEMREG_TYPE_MEMORY 0 +#define VMX_INST_MEMREG_TYPE_REG 1 + int type; + union { + struct { + unsigned long mem; + unsigned int len; + }; + enum vmx_regs_enc reg1; + }; + + enum vmx_regs_enc reg2; +}; + +enum vmx_ops_result { + VMSUCCEED, + VMFAIL_VALID, + VMFAIL_INVALID, +}; + +#define CASE_GET_REG(REG, reg) \ + case VMX_REG_ ## REG: value = regs->reg; break + +static unsigned long reg_read(struct cpu_user_regs *regs, + enum vmx_regs_enc index) +{ + unsigned long value = 0; + + switch ( index ) { + CASE_GET_REG(RAX, eax); + CASE_GET_REG(RCX, ecx); + CASE_GET_REG(RDX, edx); + CASE_GET_REG(RBX, ebx); + CASE_GET_REG(RBP, ebp); + CASE_GET_REG(RSI, esi); + CASE_GET_REG(RDI, edi); + CASE_GET_REG(RSP, esp); +#ifdef CONFIG_X86_64 + CASE_GET_REG(R8, r8); + CASE_GET_REG(R9, r9); + CASE_GET_REG(R10, r10); + CASE_GET_REG(R11, r11); + CASE_GET_REG(R12, r12); + CASE_GET_REG(R13, r13); + CASE_GET_REG(R14, r14); + CASE_GET_REG(R15, r15); +#endif + default: + break; + } + + return value; +} + +static int vmx_inst_check_privilege(struct cpu_user_regs *regs, int vmxop_check) +{ + struct vcpu *v = current; + struct segment_register cs; + + hvm_get_segment_register(v, x86_seg_cs, &cs); + + if ( vmxop_check ) + { + if ( !(v->arch.hvm_vcpu.guest_cr[0] & X86_CR0_PE) || + !(v->arch.hvm_vcpu.guest_cr[4] & X86_CR4_VMXE) ) + goto invalid_op; + } + else if ( !vcpu_2_nvmx(v).vmxon_region_pa ) + goto invalid_op; + + if ( (regs->eflags & X86_EFLAGS_VM) || + (hvm_long_mode_enabled(v) && cs.attr.fields.l == 0) ) + goto invalid_op; + /* TODO: check vmx operation mode */ + + if ( (cs.sel & 3) > 0 ) + goto gp_fault; + + return X86EMUL_OKAY; + +invalid_op: + gdprintk(XENLOG_ERR, "vmx_inst_check_privilege: invalid_op\n"); + hvm_inject_exception(TRAP_invalid_op, 0, 0); + return X86EMUL_EXCEPTION; + +gp_fault: + gdprintk(XENLOG_ERR, "vmx_inst_check_privilege: gp_fault\n"); + hvm_inject_exception(TRAP_gp_fault, 0, 0); + return X86EMUL_EXCEPTION; +} + +static int decode_vmx_inst(struct cpu_user_regs *regs, + struct vmx_inst_decoded *decode, + unsigned long *poperandS, int vmxon_check) +{ + struct vcpu *v = current; + union vmx_inst_info info; + struct segment_register seg; + unsigned long base, index, seg_base, disp, offset; + int scale, size; + + if ( vmx_inst_check_privilege(regs, vmxon_check) != X86EMUL_OKAY ) + return X86EMUL_EXCEPTION; + + info.word = __vmread(VMX_INSTRUCTION_INFO); + + if ( info.fields.memreg ) { + decode->type = VMX_INST_MEMREG_TYPE_REG; + decode->reg1 = info.fields.reg1; + if ( poperandS != NULL ) + *poperandS = reg_read(regs, decode->reg1); + } + else + { + decode->type = VMX_INST_MEMREG_TYPE_MEMORY; + hvm_get_segment_register(v, sreg_to_index[info.fields.segment], &seg); + /* TODO: segment type check */ + seg_base = seg.base; + + base = info.fields.base_reg_invalid ? 0 : + reg_read(regs, info.fields.base_reg); + + index = info.fields.index_reg_invalid ? 0 : + reg_read(regs, info.fields.index_reg); + + scale = 1 << info.fields.scaling; + + disp = __vmread(EXIT_QUALIFICATION); + + size = 1 << (info.fields.addr_size + 1); + + offset = base + index * scale + disp; + if ( (offset > seg.limit || offset + size > seg.limit) && + (!hvm_long_mode_enabled(v) || info.fields.segment == VMX_SREG_GS) ) + goto gp_fault; + + if ( poperandS != NULL && + hvm_copy_from_guest_virt(poperandS, seg_base + offset, size, 0) + != HVMCOPY_okay ) + return X86EMUL_EXCEPTION; + decode->mem = seg_base + offset; + decode->len = size; + } + + decode->reg2 = info.fields.reg2; + + return X86EMUL_OKAY; + +gp_fault: + hvm_inject_exception(TRAP_gp_fault, 0, 0); + return X86EMUL_EXCEPTION; +} + +static void vmreturn(struct cpu_user_regs *regs, enum vmx_ops_result ops_res) +{ + unsigned long eflags = regs->eflags; + unsigned long mask = X86_EFLAGS_CF | X86_EFLAGS_PF | X86_EFLAGS_AF | + X86_EFLAGS_ZF | X86_EFLAGS_SF | X86_EFLAGS_OF; + + eflags &= ~mask; + + switch ( ops_res ) { + case VMSUCCEED: + break; + case VMFAIL_VALID: + /* TODO: error number, useful for guest VMM debugging */ + eflags |= X86_EFLAGS_ZF; + break; + case VMFAIL_INVALID: + default: + eflags |= X86_EFLAGS_CF; + break; + } + + regs->eflags = eflags; +} + +/* + * VMX instructions handling + */ + +int nvmx_handle_vmxon(struct cpu_user_regs *regs) +{ + struct vcpu *v=current; + struct nestedvmx *nvmx = &vcpu_2_nvmx(v); + struct vmx_inst_decoded decode; + unsigned long gpa = 0; + int rc; + + rc = decode_vmx_inst(regs, &decode, &gpa, 1); + if ( rc != X86EMUL_OKAY ) + return rc; + + nvmx->vmxon_region_pa = gpa; + vmreturn(regs, VMSUCCEED); + + return X86EMUL_OKAY; +} + +int nvmx_handle_vmxoff(struct cpu_user_regs *regs) +{ + struct vcpu *v=current; + struct nestedvmx *nvmx = &vcpu_2_nvmx(v); + int rc; + + rc = vmx_inst_check_privilege(regs, 0); + if ( rc != X86EMUL_OKAY ) + return rc; + + nvmx->vmxon_region_pa = 0; + + vmreturn(regs, VMSUCCEED); + return X86EMUL_OKAY; +} + diff -r 4e094881883f -r c8812151acfd xen/include/asm-x86/hvm/vmx/vvmx.h --- a/xen/include/asm-x86/hvm/vmx/vvmx.h Thu Jun 02 16:33:20 2011 +0800 +++ b/xen/include/asm-x86/hvm/vmx/vvmx.h Thu Jun 02 16:33:20 2011 +0800 @@ -35,6 +35,58 @@ struct nestedvmx { #define vcpu_2_nvmx(v) (vcpu_nestedhvm(v).u.nvmx) +/* + * Encode of VMX instructions base on Table 24-11 & 24-12 of SDM 3B + */ + +enum vmx_regs_enc { + VMX_REG_RAX, + VMX_REG_RCX, + VMX_REG_RDX, + VMX_REG_RBX, + VMX_REG_RSP, + VMX_REG_RBP, + VMX_REG_RSI, + VMX_REG_RDI, +#ifdef CONFIG_X86_64 + VMX_REG_R8, + VMX_REG_R9, + VMX_REG_R10, + VMX_REG_R11, + VMX_REG_R12, + VMX_REG_R13, + VMX_REG_R14, + VMX_REG_R15, +#endif +}; + +enum vmx_sregs_enc { + VMX_SREG_ES, + VMX_SREG_CS, + VMX_SREG_SS, + VMX_SREG_DS, + VMX_SREG_FS, + VMX_SREG_GS, +}; + +union vmx_inst_info { + struct { + unsigned int scaling :2; /* bit 0-1 */ + unsigned int __rsvd0 :1; /* bit 2 */ + unsigned int reg1 :4; /* bit 3-6 */ + unsigned int addr_size :3; /* bit 7-9 */ + unsigned int memreg :1; /* bit 10 */ + unsigned int __rsvd1 :4; /* bit 11-14 */ + unsigned int segment :3; /* bit 15-17 */ + unsigned int index_reg :4; /* bit 18-21 */ + unsigned int index_reg_invalid :1; /* bit 22 */ + unsigned int base_reg :4; /* bit 23-26 */ + unsigned int base_reg_invalid :1; /* bit 27 */ + unsigned int reg2 :4; /* bit 28-31 */ + } fields; + u32 word; +}; + int nvmx_vcpu_initialise(struct vcpu *v); void nvmx_vcpu_destroy(struct vcpu *v); int nvmx_vcpu_reset(struct vcpu *v); @@ -42,5 +94,7 @@ uint64_t nvmx_vcpu_guestcr3(struct vcpu uint64_t nvmx_vcpu_hostcr3(struct vcpu *v); uint32_t nvmx_vcpu_asid(struct vcpu *v); +int nvmx_handle_vmxon(struct cpu_user_regs *regs); +int nvmx_handle_vmxoff(struct cpu_user_regs *regs); #endif /* __ASM_X86_HVM_VVMX_H__ */