462 lines
16 KiB
Diff
462 lines
16 KiB
Diff
From: Jan Beulich
|
|
Bugzilla #214568
|
|
|
|
Index: xen-3.0.3-testing/xen/arch/x86/hvm/platform.c
|
|
===================================================================
|
|
--- xen-3.0.3-testing.orig/xen/arch/x86/hvm/platform.c
|
|
+++ xen-3.0.3-testing/xen/arch/x86/hvm/platform.c
|
|
@@ -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: xen-3.0.3-testing/xen/arch/x86/hvm/svm/svm.c
|
|
===================================================================
|
|
--- xen-3.0.3-testing.orig/xen/arch/x86/hvm/svm/svm.c
|
|
+++ xen-3.0.3-testing/xen/arch/x86/hvm/svm/svm.c
|
|
@@ -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;
|
|
|
|
Index: xen-3.0.3-testing/xen/arch/x86/hvm/vmx/vmx.c
|
|
===================================================================
|
|
--- xen-3.0.3-testing.orig/xen/arch/x86/hvm/vmx/vmx.c
|
|
+++ xen-3.0.3-testing/xen/arch/x86/hvm/vmx/vmx.c
|
|
@@ -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;
|
|
|
|
Index: xen-3.0.3-testing/xen/include/asm-x86/hvm/hvm.h
|
|
===================================================================
|
|
--- xen-3.0.3-testing.orig/xen/include/asm-x86/hvm/hvm.h
|
|
+++ xen-3.0.3-testing/xen/include/asm-x86/hvm/hvm.h
|
|
@@ -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: xen-3.0.3-testing/xen/arch/x86/hvm/instrlen.c
|
|
===================================================================
|
|
--- xen-3.0.3-testing.orig/xen/arch/x86/hvm/instrlen.c
|
|
+++ xen-3.0.3-testing/xen/arch/x86/hvm/instrlen.c
|
|
@@ -20,7 +20,6 @@
|
|
#include <xen/config.h>
|
|
#include <xen/sched.h>
|
|
#include <xen/mm.h>
|
|
-#include <asm/regs.h>
|
|
#include <asm-x86/x86_emulate.h>
|
|
|
|
/* 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;
|
|
}
|