[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] Nested VMX: Emulation of guest VMXON/OFF instruction.
# HG changeset patch # User Eddie Dong <eddie.dong@xxxxxxxxx> # Date 1307607849 -28800 # Node ID 3510900d5d7b8c6b4cd1157d62b64f9287ba1716 # Parent 3c926738472de07ab35cf6ce058fa6a4b1089f36 Nested VMX: Emulation of guest VMXON/OFF instruction. Signed-off-by: Qing He <qing.he@xxxxxxxxx> Signed-off-by: Eddie Dong <eddie.dong@xxxxxxxxx> Acked-by: Tim Deegan <Tim.Deegan@xxxxxxxxxx> Committed-by: Tim Deegan <Tim.Deegan@xxxxxxxxxx> --- diff -r 3c926738472d -r 3510900d5d7b xen/arch/x86/hvm/vmx/vmx.c --- a/xen/arch/x86/hvm/vmx/vmx.c Thu Jun 09 16:24:09 2011 +0800 +++ b/xen/arch/x86/hvm/vmx/vmx.c Thu Jun 09 16:24:09 2011 +0800 @@ -2429,6 +2429,16 @@ 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: @@ -2438,8 +2448,6 @@ 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 3c926738472d -r 3510900d5d7b xen/arch/x86/hvm/vmx/vvmx.c --- a/xen/arch/x86/hvm/vmx/vvmx.c Thu Jun 09 16:24:09 2011 +0800 +++ b/xen/arch/x86/hvm/vmx/vvmx.c Thu Jun 09 16:24:09 2011 +0800 @@ -86,3 +86,229 @@ 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; + if ( info.fields.segment > 5 ) + goto gp_fault; + hvm_get_segment_register(v, sreg_to_index[info.fields.segment], &seg); + 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 3c926738472d -r 3510900d5d7b xen/include/asm-x86/hvm/vmx/vvmx.h --- a/xen/include/asm-x86/hvm/vmx/vvmx.h Thu Jun 09 16:24:09 2011 +0800 +++ b/xen/include/asm-x86/hvm/vmx/vvmx.h Thu Jun 09 16:24:09 2011 +0800 @@ -35,6 +35,58 @@ #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_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__ */ _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |