Re: Protected mode transitions and big real mode... still an issue

Guillaume Thouvenin
2008-05-14T07:29:11Z

On Tue, 6 May 2008 20:05:39 +0300
"Mohammed Gamal" <m.gamal005@gmail.com> wrote:

> > > > WinXP fails with the patch applied too. Ubuntu 7.10 live CD and
> > > > FreeDOS don't boot but complain about instruction mov 0x11,sreg not
> > > > being emulated.

Mohammed, can you try the patch at the end of this mail? Here it's
working with FreeDOS now (I added the emulation of 0x90 that is an xchg
instruction). I can also boot winXP Professional X64 edition. I still
have a weird issue with Ubuntu 7.10 that crashes sometimes with the
error:

kvm_run: failed entry, reason 5
kvm_run returned -8

It's a little bit strange because this error appears very often with
the wmii window manager but never with XFCE. And with wmii, it only
occurs when I move the mouse above the Qemu/KVM window. If I wait 30s
until the automatic boot it works...

So to give a summary, on my box:

OpensSuse 10.3 -> OK
WinXP Pro X64 -> OK
FreeDOS -> OK
Ubuntu 7.10 -> NOK

Regards,
Guillaume

---

diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index e94a8c3..efde223 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -1287,7 +1287,9 @@ static void enter_pmode(struct kvm_vcpu *vcpu)
fix_pmode_dataseg(VCPU_SREG_GS, &vcpu->arch.rmode.gs);
fix_pmode_dataseg(VCPU_SREG_FS, &vcpu->arch.rmode.fs);

+#if 0
vmcs_write16(GUEST_SS_SELECTOR, 0);
+#endif
vmcs_write32(GUEST_SS_AR_BYTES, 0x93);

vmcs_write16(GUEST_CS_SELECTOR,
@@ -2648,6 +2650,73 @@ static int handle_ept_violation(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
return 1;
}

+static int invalid_guest_state(struct kvm_vcpu *vcpu,
+ struct kvm_run *kvm_run, u32 failure_reason)
+{
+ u16 ss, cs;
+ u8 opcodes[4];
+ unsigned long rip = vcpu->arch.rip;
+ unsigned long rip_linear;
+
+ ss = vmcs_read16(GUEST_SS_SELECTOR);
+ cs = vmcs_read16(GUEST_CS_SELECTOR);
+
+ if ((ss & 0x03) != (cs & 0x03)) {
+ int err;
+ rip_linear = rip + vmx_get_segment_base(vcpu, VCPU_SREG_CS);
+ emulator_read_std(rip_linear, (void *)opcodes, 4, vcpu);
+#if 0
+ printk(KERN_INFO "emulation at (%lx) rip %lx: %02x %02x %02x %02x\n",
+ rip_linear,
+ rip, opcodes[0], opcodes[1], opcodes[2], opcodes[3]);
+#endif
+ err = emulate_instruction(vcpu, kvm_run, 0, 0, 0);
+ switch (err) {
+ case EMULATE_DONE:
+#if 0
+ printk(KERN_INFO "successfully emulated instruction\n");
+#endif
+ return 1;
+ case EMULATE_DO_MMIO:
+ printk(KERN_INFO "mmio?\n");
+ return 0;
+ default:
+ kvm_report_emulation_failure(vcpu, "vmentry failure");
+ break;
+ }
+ }
+
+ kvm_run->exit_reason = KVM_EXIT_UNKNOWN;
+ kvm_run->hw.hardware_exit_reason = failure_reason;
+ return 0;
+}
+
+static int handle_vmentry_failure(struct kvm_vcpu *vcpu,
+ struct kvm_run *kvm_run,
+ u32 failure_reason)
+{
+ unsigned long exit_qualification = vmcs_readl(EXIT_QUALIFICATION);
+#if 0
+ printk(KERN_INFO "Failed vm entry (exit reason 0x%x) ", failure_reason);
+#endif
+ switch (failure_reason) {
+ case EXIT_REASON_INVALID_GUEST_STATE:
+#if 0
+ printk("invalid guest state \n");
+#endif
+ return invalid_guest_state(vcpu, kvm_run, failure_reason);
+ case EXIT_REASON_MSR_LOADING:
+ printk("caused by MSR entry %ld loading.\n", exit_qualification);
+ break;
+ case EXIT_REASON_MACHINE_CHECK:
+ printk("caused by machine check.\n");
+ break;
+ default:
+ printk("reason not known yet!\n");
+ break;
+ }
+ return 0;
+}
/*
* The exit handlers return 1 if the exit was handled fully and guest execution
* may resume. Otherwise they set the kvm_run parameter to indicate what needs
@@ -2709,6 +2778,12 @@ static int kvm_handle_exit(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
exit_reason != EXIT_REASON_EPT_VIOLATION))
printk(KERN_WARNING "%s: unexpected, valid vectoring info and "
"exit reason is 0x%x\n", __func__, exit_reason);
+
+ if ((exit_reason & VMX_EXIT_REASONS_FAILED_VMENTRY)) {
+ exit_reason &= ~VMX_EXIT_REASONS_FAILED_VMENTRY;
+ return handle_vmentry_failure(vcpu, kvm_run, exit_reason);
+ }
+
if (exit_reason < kvm_vmx_max_exit_handlers
&& kvm_vmx_exit_handlers[exit_reason])
return kvm_vmx_exit_handlers[exit_reason](vcpu, kvm_run);
diff --git a/arch/x86/kvm/vmx.h b/arch/x86/kvm/vmx.h
index 79d94c6..2cebf48 100644
--- a/arch/x86/kvm/vmx.h
+++ b/arch/x86/kvm/vmx.h
@@ -238,7 +238,10 @@ enum vmcs_field {
#define EXIT_REASON_IO_INSTRUCTION 30
#define EXIT_REASON_MSR_READ 31
#define EXIT_REASON_MSR_WRITE 32
+#define EXIT_REASON_INVALID_GUEST_STATE 33
+#define EXIT_REASON_MSR_LOADING 34
#define EXIT_REASON_MWAIT_INSTRUCTION 36
+#define EXIT_REASON_MACHINE_CHECK 41
#define EXIT_REASON_TPR_BELOW_THRESHOLD 43
#define EXIT_REASON_APIC_ACCESS 44
#define EXIT_REASON_EPT_VIOLATION 48
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index dab3d4f..eb7db67 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -3009,8 +3009,8 @@ int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
return 0;
}

-static void get_segment(struct kvm_vcpu *vcpu,
- struct kvm_segment *var, int seg)
+void get_segment(struct kvm_vcpu *vcpu,
+ struct kvm_segment *var, int seg)
{
kvm_x86_ops->get_segment(vcpu, var, seg);
}
@@ -3093,8 +3093,8 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
return 0;
}

-static void set_segment(struct kvm_vcpu *vcpu,
- struct kvm_segment *var, int seg)
+void set_segment(struct kvm_vcpu *vcpu,
+ struct kvm_segment *var, int seg)
{
kvm_x86_ops->set_segment(vcpu, var, seg);
}
@@ -3252,8 +3252,8 @@ static int load_segment_descriptor_to_kvm_desct(struct kvm_vcpu *vcpu,
return 0;
}

-static int load_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector,
- int type_bits, int seg)
+int load_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector,
+ int type_bits, int seg)
{
struct kvm_segment kvm_seg;

diff --git a/arch/x86/kvm/x86_emulate.c b/arch/x86/kvm/x86_emulate.c
index 8a96320..40ebb46 100644
--- a/arch/x86/kvm/x86_emulate.c
+++ b/arch/x86/kvm/x86_emulate.c
@@ -69,6 +69,7 @@
#define GroupDual (1<<15) /* Alternate decoding of mod == 3 */
#define GroupMask 0xff /* Group number stored in bits 0:7 */

+int switch_perso = 0;
enum {
Group1_80, Group1_81, Group1_82, Group1_83,
Group1A, Group3_Byte, Group3, Group4, Group5, Group7,
@@ -138,9 +139,10 @@ static u16 opcode_table[256] = {
/* 0x88 - 0x8F */
ByteOp | DstMem | SrcReg | ModRM | Mov, DstMem | SrcReg | ModRM | Mov,
ByteOp | DstReg | SrcMem | ModRM | Mov, DstReg | SrcMem | ModRM | Mov,
- 0, ModRM | DstReg, 0, Group | Group1A,
+ DstMem | SrcReg | ModRM | Mov, ModRM | DstReg,
+ DstReg | SrcMem | ModRM | Mov, Group | Group1A,
/* 0x90 - 0x9F */
- 0, 0, 0, 0, 0, 0, 0, 0,
+ ImplicitOps, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, ImplicitOps | Stack, ImplicitOps | Stack, 0, 0,
/* 0xA0 - 0xA7 */
ByteOp | DstReg | SrcMem | Mov | MemAbs, DstReg | SrcMem | Mov | MemAbs,
@@ -152,7 +154,8 @@ static u16 opcode_table[256] = {
ByteOp | ImplicitOps | Mov | String, ImplicitOps | Mov | String,
ByteOp | ImplicitOps | String, ImplicitOps | String,
/* 0xB0 - 0xBF */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ DstReg | SrcImm | Mov, 0, 0, 0, 0, 0, 0, 0,
/* 0xC0 - 0xC7 */
ByteOp | DstMem | SrcImm | ModRM, DstMem | SrcImmByte | ModRM,
0, ImplicitOps | Stack, 0, 0,
@@ -168,7 +171,7 @@ static u16 opcode_table[256] = {
/* 0xE0 - 0xE7 */
0, 0, 0, 0, 0, 0, 0, 0,
/* 0xE8 - 0xEF */
- ImplicitOps | Stack, SrcImm|ImplicitOps, 0, SrcImmByte|ImplicitOps,
+ ImplicitOps | Stack, SrcImm | ImplicitOps, ImplicitOps, SrcImmByte | ImplicitOps,
0, 0, 0, 0,
/* 0xF0 - 0xF7 */
0, 0, 0, 0,
@@ -1246,6 +1249,19 @@ static inline int writeback(struct x86_emulate_ctxt *ctxt,
default:
break;
}
+#if 0
+ if (switch_perso) {
+ printk(KERN_INFO " writeback: dst.byte %d\n" , c->dst.bytes);
+ printk(KERN_INFO " writeback: dst.ptr 0x%p\n" , c->dst.ptr);
+ printk(KERN_INFO " writeback: dst.val 0x%lx\n", c->dst.val);
+ printk(KERN_INFO " writeback: src.ptr 0x%p\n", c->src.ptr);
+ printk(KERN_INFO " writeback: src.val 0x%lx\n", c->src.val);
+ printk(KERN_INFO " writeback: RAX 0x%lx\n", c->regs[VCPU_REGS_RAX]);
+ printk(KERN_INFO " writeback: RSP 0x%lx\n", c->regs[VCPU_REGS_RSP]);
+ printk(KERN_INFO " writeback: CS 0x%lx\n", c->regs[VCPU_SREG_CS]);
+ printk(KERN_INFO " writeback: SS 0x%lx\n", c->regs[VCPU_SREG_SS]);
+ }
+#endif
return 0;
}

@@ -1342,6 +1358,10 @@ special_insn:
switch (c->b) {
case 0x00 ... 0x05:
add: /* add */
+ if ((c->d & ModRM) && c->modrm_mod == 3) {
+ c->dst.bytes = (c->d & ByteOp) ? 1 : c->op_bytes;
+ c->dst.ptr = decode_register(c->modrm_rm, c->regs, c->d & ByteOp);
+ }
emulate_2op_SrcV("add", c->src, c->dst, ctxt->eflags);
break;
case 0x08 ... 0x0d:
@@ -1514,14 +1534,119 @@ special_insn:
break;
case 0x88 ... 0x8b: /* mov */
goto mov;
+ case 0x8c: { /* mov r/m, sreg */
+ struct kvm_segment segreg;
+
+ if (c->modrm_mod == 0x3)
+ c->src.val = c->modrm_val;
+
+ switch ( c->modrm_reg ) {
+ case 0:
+ get_segment(ctxt->vcpu, &segreg, VCPU_SREG_ES);
+ break;
+ case 1:
+ get_segment(ctxt->vcpu, &segreg, VCPU_SREG_CS);
+ break;
+ case 2:
+ get_segment(ctxt->vcpu, &segreg, VCPU_SREG_SS);
+ break;
+ case 3:
+ get_segment(ctxt->vcpu, &segreg, VCPU_SREG_DS);
+ break;
+ case 4:
+ get_segment(ctxt->vcpu, &segreg, VCPU_SREG_FS);
+ break;
+ case 5:
+ get_segment(ctxt->vcpu, &segreg, VCPU_SREG_GS);
+ break;
+ default:
+ printk(KERN_INFO "0x8c: Invalid segreg in modrm byte 0x%02x\n",
+ c->modrm);
+ goto cannot_emulate;
+ }
+ c->dst.val = segreg.selector;
+ c->dst.bytes = 2;
+ c->dst.ptr = (unsigned long *)decode_register(c->modrm_rm, c->regs,
+ c->d & ByteOp);
+ break;
+ }
case 0x8d: /* lea r16/r32, m */
c->dst.val = c->modrm_ea;
break;
+ case 0x8e: { /* mov seg, r/m16 */
+ uint16_t sel;
+
+ sel = c->src.val;
+ switch ( c->modrm_reg ) {
+ case 0:
+ if (load_segment_descriptor(ctxt->vcpu, sel, 1, VCPU_SREG_ES) < 0)
+ goto cannot_emulate;
+ break;
+ case 1:
+ if (load_segment_descriptor(ctxt->vcpu, sel, 9, VCPU_SREG_CS) < 0)
+ goto cannot_emulate;
+ break;
+ case 2:
+ if (load_segment_descriptor(ctxt->vcpu, sel, 1, VCPU_SREG_SS) < 0)
+ goto cannot_emulate;
+ break;
+ case 3:
+ if (load_segment_descriptor(ctxt->vcpu, sel, 1, VCPU_SREG_DS) < 0)
+ goto cannot_emulate;
+ break;
+ case 4:
+ if (load_segment_descriptor(ctxt->vcpu, sel, 1, VCPU_SREG_FS) < 0)
+ goto cannot_emulate;
+ break;
+ case 5:
+ if (load_segment_descriptor(ctxt->vcpu, sel, 1, VCPU_SREG_GS) < 0)
+ goto cannot_emulate;
+ break;
+ default:
+ printk(KERN_INFO "Invalid segreg in modrm byte 0x%02x\n",
+ c->modrm);
+ goto cannot_emulate;
+ }
+
+ c->dst.type = OP_NONE; /* Disable writeback. */
+ break;
+ }
case 0x8f: /* pop (sole member of Grp1a) */
rc = emulate_grp1a(ctxt, ops);
if (rc != 0)
goto done;
break;
+ case 0x90: /* xhcg r8, rAx */
+ c->src.ptr = & c->regs[c->b & 0x7];
+ c->dst.ptr = & c->regs[VCPU_REGS_RAX];
+
+ switch (c->op_bytes) {
+ case 2:
+ c->dst.val = *(u16*) c->dst.ptr;
+ c->src.val = *(u16*) c->src.ptr;
+ *(u16 *) c->dst.ptr = (u16) c->src.val;
+ *(u16 *) c->src.ptr = (u16) c->dst.val;
+ break;
+ case 4:
+ c->dst.val = *(u32*) c->dst.ptr;
+ c->src.val = *(u32*) c->src.ptr;
+ *(u32 *) c->dst.ptr = (u32) c->src.val;
+ *(u32 *) c->src.ptr = (u32) c->dst.val;
+ break;
+ case 8:
+ c->dst.val = *(u64*) c->dst.ptr;
+ c->src.val = *(u64*) c->src.ptr;
+ *(u64 *) c->dst.ptr = (u64) c->src.val;
+ *(u64 *) c->src.ptr = (u64) c->dst.val;
+ break;
+ default:
+ printk("xchg: op_bytes=%d is not supported.\n", c->op_bytes);
+ goto cannot_emulate;
+ }
+ c->dst.type = OP_NONE; /* Disable writeback. */
+ break;
+ case 0xb8: /* mov r, imm */
+ goto mov;
case 0x9c: /* pushf */
c->src.val = (unsigned long) ctxt->eflags;
emulate_push(ctxt);
@@ -1623,6 +1748,10 @@ special_insn:
DPRINTF("Urk! I don't handle SCAS.\n");
goto cannot_emulate;
case 0xc0 ... 0xc1:
+ if ((c->d & ModRM) && c->modrm_mod == 3) {
+ c->dst.bytes = (c->d & ByteOp) ? 1 : c->op_bytes;
+ c->dst.ptr = decode_register(c->modrm_rm, c->regs, c->d & ByteOp);
+ }
emulate_grp2(ctxt);
break;
case 0xc3: /* ret */
@@ -1660,6 +1789,39 @@ special_insn:
break;
}
case 0xe9: /* jmp rel */
+ jmp_rel(c, c->src.val);
+ c->dst.type = OP_NONE; /* Disable writeback. */
+ break;
+ case 0xea: /* jmp far */ {
+ uint32_t eip;
+ uint16_t sel;
+
+ /* enable switch_perso */
+ switch_perso = 1;
+
+ switch (c->op_bytes) {
+ case 2:
+ eip = insn_fetch(u16, 2, c->eip);
+ eip = eip & 0x0000FFFF; /* clear upper 16 bits */
+ break;
+ case 4:
+ eip = insn_fetch(u32, 4, c->eip);
+ break;
+ default:
+ DPRINTF("jmp far: Invalid op_bytes\n");
+ goto cannot_emulate;
+ }
+ sel = insn_fetch(u16, 2, c->eip);
+ if (ctxt->mode == X86EMUL_MODE_REAL)
+ eip |= (sel << 4);
+ else if (load_segment_descriptor(ctxt->vcpu, sel, 9, VCPU_SREG_CS) < 0) {
+ DPRINTF("jmp far: Failed to load CS descriptor\n");
+ goto cannot_emulate;
+ }
+
+ c->eip = eip;
+ break;
+ }
case 0xeb: /* jmp rel short */
jmp_rel(c, c->src.val);
c->dst.type = OP_NONE; /* Disable writeback. */
diff --git a/include/asm-x86/kvm_host.h b/include/asm-x86/kvm_host.h
index 1466c3f..99e343e 100644
--- a/include/asm-x86/kvm_host.h
+++ b/include/asm-x86/kvm_host.h
@@ -494,6 +494,10 @@ int emulator_get_dr(struct x86_emulate_ctxt *ctxt, int dr,
int emulator_set_dr(struct x86_emulate_ctxt *ctxt, int dr,
unsigned long value);

+void set_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg);
+void get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg);
+int load_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector,
+ int type_bits, int seg);
int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason);

void kvm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0);

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.