Index: 2006-11-17/xen/arch/x86/hvm/platform.c =================================================================== --- 2006-11-17.orig/xen/arch/x86/hvm/platform.c 2006-11-21 17:40:27.000000000 +0100 +++ 2006-11-17/xen/arch/x86/hvm/platform.c 2006-11-22 10:20:51.000000000 +0100 @@ -883,19 +883,14 @@ void handle_mmio(unsigned long va, unsig memcpy(regs, guest_cpu_user_regs(), HVM_CONTEXT_STACK_BYTES); hvm_store_cpu_guest_regs(v, regs, NULL); - inst_len = hvm_instruction_length(regs, hvm_guest_x86_mode(v)); + inst_addr = hvm_get_segment_base(current, seg_cs) + regs->eip; + inst_len = hvm_instruction_length(inst_addr, hvm_guest_x86_mode(v)); if ( inst_len <= 0 ) { printk("handle_mmio: failed to get instruction length\n"); domain_crash_synchronous(); } - realmode = hvm_realmode(v); - if (realmode) - inst_addr = (regs->cs << 4) + regs->eip; - else - inst_addr = regs->eip; - memset(inst, 0, MAX_INST_LEN); ret = inst_copy_from_guest(inst, inst_addr, inst_len); if (ret != inst_len) { @@ -904,6 +899,7 @@ void handle_mmio(unsigned long va, unsig } init_instruction(&mmio_inst); + realmode = hvm_realmode(v); if (hvm_decode(realmode, inst, &mmio_inst) == DECODE_failure) { printk("handle_mmio: failed to decode instruction\n"); Index: 2006-11-17/xen/arch/x86/hvm/svm/svm.c =================================================================== --- 2006-11-17.orig/xen/arch/x86/hvm/svm/svm.c 2006-11-21 17:40:27.000000000 +0100 +++ 2006-11-17/xen/arch/x86/hvm/svm/svm.c 2006-11-22 11:28:56.000000000 +0100 @@ -507,6 +507,24 @@ unsigned long svm_get_ctrl_reg(struct vc return 0; /* dummy */ } +static unsigned long svm_get_segment_base(struct vcpu *v, enum segment seg) +{ + switch ( seg ) + { + case seg_cs: return v->arch.hvm_svm.vmcb->cs.base; + case seg_ds: return v->arch.hvm_svm.vmcb->ds.base; + case seg_es: return v->arch.hvm_svm.vmcb->es.base; + case seg_fs: return v->arch.hvm_svm.vmcb->fs.base; + case seg_gs: return v->arch.hvm_svm.vmcb->gs.base; + case seg_ss: return v->arch.hvm_svm.vmcb->ss.base; + case seg_tr: return v->arch.hvm_svm.vmcb->tr.base; + case seg_gdtr: return v->arch.hvm_svm.vmcb->gdtr.base; + case seg_idtr: return v->arch.hvm_svm.vmcb->idtr.base; + case seg_ldtr: return v->arch.hvm_svm.vmcb->ldtr.base; + } + BUG(); + return 0; +} /* Make sure that xen intercepts any FP accesses from current */ static void svm_stts(struct vcpu *v) @@ -887,6 +905,7 @@ int start_svm(void) hvm_funcs.pae_enabled = svm_pae_enabled; hvm_funcs.guest_x86_mode = svm_guest_x86_mode; hvm_funcs.get_guest_ctrl_reg = svm_get_ctrl_reg; + hvm_funcs.get_segment_base = svm_get_segment_base; hvm_funcs.update_host_cr3 = svm_update_host_cr3; @@ -1023,7 +1042,7 @@ static void svm_do_general_protection_fa printf("Huh? We got a GP Fault with an invalid IDTR!\n"); svm_dump_vmcb(__func__, vmcb); svm_dump_regs(__func__, regs); - svm_dump_inst(vmcb->rip); + svm_dump_inst(svm_rip2pointer(vmcb)); __hvm_bug(regs); } Index: 2006-11-17/xen/arch/x86/hvm/vmx/vmx.c =================================================================== --- 2006-11-17.orig/xen/arch/x86/hvm/vmx/vmx.c 2006-11-21 17:40:27.000000000 +0100 +++ 2006-11-17/xen/arch/x86/hvm/vmx/vmx.c 2006-11-22 16:23:43.000000000 +0100 @@ -610,7 +610,27 @@ static unsigned long vmx_get_ctrl_reg(st return 0; /* dummy */ } +static unsigned long vmx_get_segment_base(struct vcpu *v, enum segment seg) +{ + unsigned long base; + BUG_ON(v != current); + switch ( seg ) + { + case seg_cs: __vmread(GUEST_CS_BASE, &base); break; + case seg_ds: __vmread(GUEST_DS_BASE, &base); break; + case seg_es: __vmread(GUEST_ES_BASE, &base); break; + case seg_fs: __vmread(GUEST_FS_BASE, &base); break; + case seg_gs: __vmread(GUEST_GS_BASE, &base); break; + case seg_ss: __vmread(GUEST_SS_BASE, &base); break; + case seg_tr: __vmread(GUEST_TR_BASE, &base); break; + case seg_gdtr: __vmread(GUEST_GDTR_BASE, &base); break; + case seg_idtr: __vmread(GUEST_IDTR_BASE, &base); break; + case seg_ldtr: __vmread(GUEST_LDTR_BASE, &base); break; + default: BUG(); base = 0; break; + } + return base; +} /* Make sure that xen intercepts any FP accesses from current */ static void vmx_stts(struct vcpu *v) @@ -753,6 +773,7 @@ static void vmx_setup_hvm_funcs(void) hvm_funcs.pae_enabled = vmx_pae_enabled; hvm_funcs.guest_x86_mode = vmx_guest_x86_mode; hvm_funcs.get_guest_ctrl_reg = vmx_get_ctrl_reg; + hvm_funcs.get_segment_base = vmx_get_segment_base; hvm_funcs.update_host_cr3 = vmx_update_host_cr3; @@ -2120,7 +2141,7 @@ static void vmx_reflect_exception(struct asmlinkage void vmx_vmexit_handler(struct cpu_user_regs *regs) { unsigned int exit_reason; - unsigned long exit_qualification, rip, inst_len = 0; + unsigned long exit_qualification, inst_len = 0; struct vcpu *v = current; __vmread(VM_EXIT_REASON, &exit_reason); @@ -2306,14 +2327,12 @@ asmlinkage void vmx_vmexit_handler(struc { inst_len = __get_instruction_length(); /* Safe: VMCALL */ __update_guest_eip(inst_len); - __vmread(GUEST_RIP, &rip); __vmread(EXIT_QUALIFICATION, &exit_qualification); hvm_do_hypercall(regs); break; } case EXIT_REASON_CR_ACCESS: { - __vmread(GUEST_RIP, &rip); __vmread(EXIT_QUALIFICATION, &exit_qualification); inst_len = __get_instruction_length(); /* Safe: MOV Cn, LMSW, CLTS */ if ( vmx_cr_access(exit_qualification, regs) ) Index: 2006-11-17/xen/include/asm-x86/hvm/hvm.h =================================================================== --- 2006-11-17.orig/xen/include/asm-x86/hvm/hvm.h 2006-11-21 17:40:27.000000000 +0100 +++ 2006-11-17/xen/include/asm-x86/hvm/hvm.h 2006-11-22 10:00:54.000000000 +0100 @@ -20,6 +20,19 @@ #ifndef __ASM_X86_HVM_HVM_H__ #define __ASM_X86_HVM_HVM_H__ +enum segment { + seg_cs, + seg_ss, + seg_ds, + seg_es, + seg_fs, + seg_gs, + seg_tr, + seg_ldtr, + seg_gdtr, + seg_idtr +}; + /* * The hardware virtual machine (HVM) interface abstracts away from the * x86/x86_64 CPU virtualization assist specifics. Currently this interface @@ -52,6 +65,7 @@ struct hvm_function_table { * 1) determine whether the guest is in real or vm8086 mode, * 2) determine whether paging is enabled, * 3) return the current guest control-register value + * 4) return the current guest segment descriptor base */ int (*realmode)(struct vcpu *v); int (*paging_enabled)(struct vcpu *v); @@ -59,6 +73,7 @@ struct hvm_function_table { int (*pae_enabled)(struct vcpu *v); int (*guest_x86_mode)(struct vcpu *v); unsigned long (*get_guest_ctrl_reg)(struct vcpu *v, unsigned int num); + unsigned long (*get_segment_base)(struct vcpu *v, enum segment seg); /* * Re-set the value of CR3 that Xen runs on when handling VM exits @@ -157,7 +172,7 @@ hvm_guest_x86_mode(struct vcpu *v) return hvm_funcs.guest_x86_mode(v); } -int hvm_instruction_length(struct cpu_user_regs *regs, int mode); +int hvm_instruction_length(unsigned long pc, int mode); static inline void hvm_update_host_cr3(struct vcpu *v) @@ -176,6 +191,12 @@ hvm_get_guest_ctrl_reg(struct vcpu *v, u return 0; /* force to fail */ } +static inline unsigned long +hvm_get_segment_base(struct vcpu *v, enum segment seg) +{ + return hvm_funcs.get_segment_base(v, seg); +} + void hvm_stts(struct vcpu *v); void hvm_set_guest_time(struct vcpu *v, u64 gtime); void hvm_do_resume(struct vcpu *v); Index: 2006-11-17/xen/arch/x86/hvm/instrlen.c =================================================================== --- 2006-11-17.orig/xen/arch/x86/hvm/instrlen.c 2006-09-27 21:51:54.000000000 +0200 +++ 2006-11-17/xen/arch/x86/hvm/instrlen.c 2006-11-22 10:38:38.000000000 +0100 @@ -20,7 +20,6 @@ #include #include #include -#include #include /* read from guest memory */ @@ -195,54 +194,51 @@ static uint8_t twobyte_table[256] = { }; /* - * insn_fetch - fetch the next 1 to 4 bytes from instruction stream - * @_type: u8, u16, u32, s8, s16, or s32 - * @_size: 1, 2, or 4 bytes + * insn_fetch - fetch the next byte from instruction stream */ -#define insn_fetch(_type, _size) \ -({ unsigned long _x, _ptr = _regs.eip; \ - if ( mode == X86EMUL_MODE_REAL ) _ptr += _regs.cs << 4; \ - rc = inst_copy_from_guest((unsigned char *)(&(_x)), _ptr, _size); \ - if ( rc != _size ) goto done; \ - _regs.eip += (_size); \ - length += (_size); \ - (_type)_x; \ +#define insn_fetch() \ +({ uint8_t _x; \ + if ( inst_copy_from_guest(&_x, pc, 1) != 1 ) { \ + DPRINTK("Cannot read from address %lx (eip %lx, mode %d)\n", \ + pc, org_pc, mode); \ + return -1; \ + } \ + pc += 1; \ + length += 1; \ + _x; \ }) /** * hvm_instruction_length - returns the current instructions length * - * @regs: guest register state + * @org_pc: guest instruction pointer * @mode: guest operating mode * * EXTERNAL this routine calculates the length of the current instruction - * pointed to by eip. The guest state is _not_ changed by this routine. + * pointed to by org_pc. The guest state is _not_ changed by this routine. */ -int hvm_instruction_length(struct cpu_user_regs *regs, int mode) +int hvm_instruction_length(unsigned long org_pc, int mode) { uint8_t b, d, twobyte = 0, rex_prefix = 0; uint8_t modrm, modrm_mod = 0, modrm_reg = 0, modrm_rm = 0; - unsigned int op_bytes, ad_bytes, i; - int rc = 0; + unsigned int op_default, op_bytes, ad_default, ad_bytes, i; int length = 0; unsigned int tmp; - - /* Shadow copy of register state. Committed on successful emulation. */ - struct cpu_user_regs _regs = *regs; + unsigned long pc = org_pc; switch ( mode ) { case X86EMUL_MODE_REAL: case X86EMUL_MODE_PROT16: - op_bytes = ad_bytes = 2; + op_bytes = op_default = ad_bytes = ad_default = 2; break; case X86EMUL_MODE_PROT32: - op_bytes = ad_bytes = 4; + op_bytes = op_default = ad_bytes = ad_default = 4; break; #ifdef __x86_64__ case X86EMUL_MODE_PROT64: - op_bytes = 4; - ad_bytes = 8; + op_bytes = op_default = 4; + ad_bytes = ad_default = 8; break; #endif default: @@ -252,16 +248,16 @@ int hvm_instruction_length(struct cpu_us /* Legacy prefixes. */ for ( i = 0; i < 8; i++ ) { - switch ( b = insn_fetch(uint8_t, 1) ) + switch ( b = insn_fetch() ) { case 0x66: /* operand-size override */ - op_bytes ^= 6; /* switch between 2/4 bytes */ + op_bytes = op_default ^ 6; /* switch between 2/4 bytes */ break; case 0x67: /* address-size override */ if ( mode == X86EMUL_MODE_PROT64 ) - ad_bytes ^= 12; /* switch between 4/8 bytes */ + ad_bytes = ad_default ^ 12; /* switch between 4/8 bytes */ else - ad_bytes ^= 6; /* switch between 2/4 bytes */ + ad_bytes = ad_default ^ 6; /* switch between 2/4 bytes */ break; case 0x2e: /* CS override */ case 0x3e: /* DS override */ @@ -273,21 +269,30 @@ int hvm_instruction_length(struct cpu_us case 0xf3: /* REP/REPE/REPZ */ case 0xf2: /* REPNE/REPNZ */ break; +#ifdef __x86_64__ + case 0x40 ... 0x4f: + if ( mode == X86EMUL_MODE_PROT64 ) + { + rex_prefix = b; + continue; + } + /* FALLTHRU */ +#endif default: goto done_prefixes; } + rex_prefix = 0; } done_prefixes: /* REX prefix. */ - if ( (mode == X86EMUL_MODE_PROT64) && ((b & 0xf0) == 0x40) ) + if ( rex_prefix ) { - rex_prefix = b; - if ( b & 8 ) - op_bytes = 8; /* REX.W */ - modrm_reg = (b & 4) << 1; /* REX.R */ + if ( rex_prefix & 8 ) + op_bytes = 8; /* REX.W */ + modrm_reg = (rex_prefix & 4) << 1; /* REX.R */ /* REX.B and REX.X do not need to be decoded. */ - b = insn_fetch(uint8_t, 1); + b = insn_fetch(); } /* Opcode byte(s). */ @@ -298,7 +303,7 @@ done_prefixes: if ( b == 0x0f ) { twobyte = 1; - b = insn_fetch(uint8_t, 1); + b = insn_fetch(); d = twobyte_table[b]; } @@ -310,7 +315,7 @@ done_prefixes: /* ModRM and SIB bytes. */ if ( d & ModRM ) { - modrm = insn_fetch(uint8_t, 1); + modrm = insn_fetch(); modrm_mod |= (modrm & 0xc0) >> 6; modrm_reg |= (modrm & 0x38) >> 3; modrm_rm |= (modrm & 0x07); @@ -330,16 +335,16 @@ done_prefixes: if ( modrm_rm == 6 ) { length += 2; - _regs.eip += 2; /* skip disp16 */ + pc += 2; /* skip disp16 */ } break; case 1: length += 1; - _regs.eip += 1; /* skip disp8 */ + pc += 1; /* skip disp8 */ break; case 2: length += 2; - _regs.eip += 2; /* skip disp16 */ + pc += 2; /* skip disp16 */ break; } } @@ -350,33 +355,34 @@ done_prefixes: { case 0: if ( (modrm_rm == 4) && - (((insn_fetch(uint8_t, 1)) & 7) - == 5) ) + ((insn_fetch() & 7) == 5) ) { length += 4; - _regs.eip += 4; /* skip disp32 specified by SIB.base */ + pc += 4; /* skip disp32 specified by SIB.base */ } else if ( modrm_rm == 5 ) { length += 4; - _regs.eip += 4; /* skip disp32 */ + pc += 4; /* skip disp32 */ } break; case 1: if ( modrm_rm == 4 ) { - insn_fetch(uint8_t, 1); + length += 1; + pc += 1; } length += 1; - _regs.eip += 1; /* skip disp8 */ + pc += 1; /* skip disp8 */ break; case 2: if ( modrm_rm == 4 ) { - insn_fetch(uint8_t, 1); + length += 1; + pc += 1; } length += 4; - _regs.eip += 4; /* skip disp32 */ + pc += 4; /* skip disp32 */ break; } } @@ -397,15 +403,12 @@ done_prefixes: tmp = (d & ByteOp) ? 1 : op_bytes; if ( tmp == 8 ) tmp = 4; /* NB. Immediates are sign-extended as necessary. */ - switch ( tmp ) - { - case 1: insn_fetch(int8_t, 1); break; - case 2: insn_fetch(int16_t, 2); break; - case 4: insn_fetch(int32_t, 4); break; - } + length += tmp; + pc += tmp; break; case SrcImmByte: - insn_fetch(int8_t, 1); + length += 1; + pc += 1; break; } @@ -414,13 +417,9 @@ done_prefixes: switch ( b ) { - case 0xa0 ... 0xa1: /* mov */ - length += ad_bytes; - _regs.eip += ad_bytes; /* skip src displacement */ - break; - case 0xa2 ... 0xa3: /* mov */ + case 0xa0 ... 0xa3: /* mov */ length += ad_bytes; - _regs.eip += ad_bytes; /* skip dst displacement */ + pc += ad_bytes; /* skip src/dst displacement */ break; case 0xf6 ... 0xf7: /* Grp3 */ switch ( modrm_reg ) @@ -429,12 +428,8 @@ done_prefixes: /* Special case in Grp3: test has an immediate source operand. */ tmp = (d & ByteOp) ? 1 : op_bytes; if ( tmp == 8 ) tmp = 4; - switch ( tmp ) - { - case 1: insn_fetch(int8_t, 1); break; - case 2: insn_fetch(int16_t, 2); break; - case 4: insn_fetch(int32_t, 4); break; - } + length += tmp; + pc += tmp; goto done; } break; @@ -445,6 +440,6 @@ done: cannot_emulate: DPRINTK("Cannot emulate %02x at address %lx (eip %lx, mode %d)\n", - b, (unsigned long)_regs.eip, (unsigned long)regs->eip, mode); + b, pc, org_pc, mode); return -1; }