# 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
|