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

Marcelo Tosatti
2008-05-14T21:29:53Z

Hi Guillaume,

On Wed, May 14, 2008 at 09:29:11AM +0200, Guillaume Thouvenin wrote:
> 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...

This appears to be due to the vmport save/load bug:
https://bugs.launchpad.net/ubuntu/+source/kvm/+bug/219165

I'll look into it if nobody beats me to it.

Regarding FreeDOS, it necessary to emulate software interrupts and NOP
to get the "HIMEM XMS-memory driver" version to boot (with the FreeOSZOO
image).

The "maximum RAM free, using EMM86" version is more complicated, requiring
ldt, ltr and a few other things.

There are two problems remaining:

1) add is storing the result in the wrong register

6486: 66 64 89 3e 72 01 mov %edi,%fs:0x172
648c: 66 be 8d 03 00 00 mov $0x38d,%esi
6492: 66 c1 e6 04 shl $0x4,%esi
6496: 66 b8 98 0a 00 00 mov $0xa98,%eax
649c: 66 03 f0 add %eax,%esi

The destination for the add is "%esi", but the emulation stores the
result in eax, because:

if ((c->d & ModRM) && c->modrm_mod == 3) {
u8 reg;
c->dst.bytes = (c->d & ByteOp) ? 1 : c->op_bytes;
c->dst.ptr = decode_register(c->modrm_rm, c->regs, c->d & ByteOp);
}

modrm_reg contains "6", which is the correct register index, but
modrm_rm contains 0, so the result is stored in "eax" (see hack).

2) iretl generates pagefaults

1226df: 0f 06 clts
1226e1: b8 14 00 mov $0x14,%ax
1226e4: 8e e0 mov %ax,%fs
1226e6: 66 64 a1 50 01 mov %fs:0x150,%eax
1226eb: 0f 22 d8 mov %eax,%cr3
1226ee: 0f 20 c0 mov %cr0,%eax
1226f1: 66 0d 00 00 00 80 or $0x80000000,%eax
1226f7: 0f 22 c0 mov %eax,%cr0
1226fa: 66 cf iretl

The iretl which happens after enabling paging faults in different ways:

kvm_inject_page_fault: EIP=1226fa
kvm_inject_page_fault: ADDR=1226fa

kvm_inject_page_fault: EIP=1226fa
kvm_inject_page_fault: ADDR=1237d1
kvm: inject_page_fault: double fault 0x1237d1

Index: kvm.tip/arch/x86/kvm/vmx.c
===================================================================
--- kvm.tip.orig/arch/x86/kvm/vmx.c
+++ kvm.tip/arch/x86/kvm/vmx.c
@@ -194,6 +194,12 @@ static inline int is_external_interrupt(
== (INTR_TYPE_EXT_INTR | INTR_INFO_VALID_MASK);
}

+static inline int is_software_interrupt(u32 intr_info)
+{
+ return (intr_info & (INTR_TYPE_SOFT_INTR | INTR_INFO_VALID_MASK))
+ == (INTR_TYPE_SOFT_INTR | INTR_INFO_VALID_MASK);
+}
+
static inline int cpu_has_vmx_msr_bitmap(void)
{
return (vmcs_config.cpu_based_exec_ctrl & CPU_BASED_USE_MSR_BITMAPS);
@@ -2190,8 +2196,10 @@ static void kvm_guest_debug_pre(struct k
}

static int handle_rmode_exception(struct kvm_vcpu *vcpu,
- int vec, u32 err_code)
+ u32 intr_info, u32 err_code)
{
+ int vec = intr_info & INTR_INFO_VECTOR_MASK;
+
if (!vcpu->arch.rmode.active)
return 0;

@@ -2202,6 +2210,10 @@ static int handle_rmode_exception(struct
if (((vec == GP_VECTOR) || (vec == SS_VECTOR)) && err_code == 0)
if (emulate_instruction(vcpu, NULL, 0, 0, 0) == EMULATE_DONE)
return 1;
+ if (is_software_interrupt(intr_info) && err_code == 0) {
+ if (emulate_instruction(vcpu, NULL, 0, 0, 0) == EMULATE_DONE)
+ return 1;
+ }
return 0;
}

@@ -2257,8 +2269,7 @@ static int handle_exception(struct kvm_v
}

if (vcpu->arch.rmode.active &&
- handle_rmode_exception(vcpu, intr_info & INTR_INFO_VECTOR_MASK,
- error_code)) {
+ handle_rmode_exception(vcpu, intr_info, error_code)) {
if (vcpu->arch.halt_request) {
vcpu->arch.halt_request = 0;
return kvm_emulate_halt(vcpu);
Index: kvm.tip/arch/x86/kvm/x86.c
===================================================================
--- kvm.tip.orig/arch/x86/kvm/x86.c
+++ kvm.tip/arch/x86/kvm/x86.c
@@ -3294,13 +3294,21 @@ int load_segment_descriptor(struct kvm_v

if (load_segment_descriptor_to_kvm_desct(vcpu, selector, &kvm_seg))
return 1;
- kvm_seg.type |= type_bits;

if (seg != VCPU_SREG_SS && seg != VCPU_SREG_CS &&
seg != VCPU_SREG_LDTR)
if (!kvm_seg.s)
kvm_seg.unusable = 1;

+ if (seg == VCPU_SREG_CS && !kvm_seg.s) {
+ switch (kvm_seg.type) {
+ case 9: /* TSS */
+ return kvm_task_switch(vcpu, selector, TASK_SWITCH_JMP);
+ default:
+ }
+ }
+
+ kvm_seg.type |= type_bits;
set_segment(vcpu, &kvm_seg, seg);
return 0;
}
Index: kvm.tip/arch/x86/kvm/x86_emulate.c
===================================================================
--- kvm.tip.orig/arch/x86/kvm/x86_emulate.c
+++ kvm.tip/arch/x86/kvm/x86_emulate.c
@@ -99,7 +99,7 @@ static u16 opcode_table[256] = {
/* 0x28 - 0x2F */
ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
- 0, 0, 0, 0,
+ DstReg | SrcImm, DstReg | SrcImm, 0, 0,
/* 0x30 - 0x37 */
ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
@@ -107,7 +107,7 @@ static u16 opcode_table[256] = {
/* 0x38 - 0x3F */
ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
- 0, 0, 0, 0,
+ 0, ByteOp | DstReg | SrcImm, 0, 0,
/* 0x40 - 0x47 */
DstReg, DstReg, DstReg, DstReg, DstReg, DstReg, DstReg, DstReg,
/* 0x48 - 0x4F */
@@ -154,8 +154,10 @@ 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,
DstReg | SrcImm | Mov, 0, 0, 0, 0, 0, 0, 0,
+ DstReg | SrcImm | Mov, DstReg | SrcImm | Mov, DstReg | SrcImm | Mov,
+ DstReg | SrcImm | Mov, DstReg | SrcImm | Mov, DstReg | SrcImm | Mov,
+ DstReg | SrcImm | Mov, DstReg | SrcImm | Mov,
/* 0xC0 - 0xC7 */
ByteOp | DstMem | SrcImm | ModRM, DstMem | SrcImmByte | ModRM,
0, ImplicitOps | Stack, 0, 0,
@@ -169,7 +171,7 @@ static u16 opcode_table[256] = {
/* 0xD8 - 0xDF */
0, 0, 0, 0, 0, 0, 0, 0,
/* 0xE0 - 0xE7 */
- 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, SrcImmByte, 0, 0, 0, 0, 0,
/* 0xE8 - 0xEF */
ImplicitOps | Stack, SrcImm | ImplicitOps, ImplicitOps, SrcImmByte | ImplicitOps,
0, 0, 0, 0,
@@ -183,7 +185,8 @@ static u16 opcode_table[256] = {

static u16 twobyte_table[256] = {
/* 0x00 - 0x0F */
- 0, Group | GroupDual | Group7, 0, 0, 0, 0, ImplicitOps, 0,
+ SrcReg|SrcMem16|ModRM,
+ Group | GroupDual | Group7, 0, 0, 0, 0, ImplicitOps, 0,
ImplicitOps, ImplicitOps, 0, 0, 0, ImplicitOps | ModRM, 0, 0,
/* 0x10 - 0x1F */
0, 0, 0, 0, 0, 0, 0, 0, ImplicitOps | ModRM, 0, 0, 0, 0, 0, 0, 0,
@@ -275,7 +278,8 @@ static u16 group_table[] = {
0, 0, 0, 0, 0, 0,
[Group5*8] =
DstMem | SrcNone | ModRM, DstMem | SrcNone | ModRM, 0, 0,
- SrcMem | ModRM, 0, SrcMem | ModRM | Stack, 0,
+ SrcMem | ModRM, ImplicitOps | ModRM, SrcMem | ModRM | Stack, 0,
+
[Group7*8] =
0, 0, ModRM | SrcMem, ModRM | SrcMem,
SrcNone | ModRM | DstMem | Mov, 0,
@@ -951,8 +955,8 @@ done_prefixes:
}

/* Unrecognised? */
- if (c->d == 0) {
- DPRINTF("Cannot emulate %02x\n", c->b);
+ if (c->d == 0 && (c->b != 0xcc) && (c->b != 0x90) && (c->b != 0xf)) {
+ DPRINTF("Cannot emulate %02x %x\n", c->b, c->eip);
return -1;
}

@@ -1359,8 +1363,15 @@ special_insn:
case 0x00 ... 0x05:
add: /* add */
if ((c->d & ModRM) && c->modrm_mod == 3) {
+ u8 reg;
c->dst.bytes = (c->d & ByteOp) ? 1 : c->op_bytes;
- c->dst.ptr = decode_register(c->modrm_rm, c->regs, c->d & ByteOp);
+
+ if (ctxt->cs_base + c->eip == 0x649f)
+ reg = c->modrm_rm|c->modrm_reg;
+ else
+ reg = c->modrm_rm;
+
+ c->dst.ptr = decode_register(reg, c->regs, c->d & ByteOp);
}
emulate_2op_SrcV("add", c->src, c->dst, ctxt->eflags);
break;
@@ -1616,8 +1627,14 @@ special_insn:
if (rc != 0)
goto done;
break;
- case 0xb8: /* mov r, imm */
- goto mov;
+ case 0xb8 ... 0xbf: /* mov r, imm */
+ {
+ int reg = c->b & 0x7;
+ c->dst.ptr = (unsigned long *)&c->regs[VCPU_REGS_RAX + reg];
+ goto mov;
+ }
+ case 0x90: /* nop */
+ break;
case 0x9c: /* pushf */
c->src.val = (unsigned long) ctxt->eflags;
emulate_push(ctxt);
@@ -1732,6 +1749,11 @@ special_insn:
mov:
c->dst.val = c->src.val;
break;
+ case 0xcc ... 0xcd: /* int */
+ /* FIXME: do a proper jump through idt */
+ if (ctxt->mode == X86EMUL_MODE_REAL) {
+ }
+ break;
case 0xd0 ... 0xd1: /* Grp2 */
c->src.val = 1;
emulate_grp2(ctxt);
@@ -1740,6 +1762,12 @@ special_insn:
c->src.val = c->regs[VCPU_REGS_RCX];
emulate_grp2(ctxt);
break;
+ case 0xe2: /* loop */
+ c->regs[VCPU_REGS_RCX]--;
+ if (c->regs[VCPU_REGS_RCX])
+ c->eip = c->eip + c->src.val;
+ c->dst.type = OP_NONE;
+ break;
case 0xe8: /* call (near) */ {
long int rel;
switch (c->op_bytes) {
@@ -1763,13 +1791,38 @@ special_insn:
jmp_rel(c, c->src.val);
c->dst.type = OP_NONE; /* Disable writeback. */
break;
- case 0xea: /* jmp far */ {
+ case 0xea:
+ jmpfar: /* jmp far */ {
uint32_t eip;
uint16_t sel;

/* enable switch_perso */
switch_perso = 1;

+ if (c->b == 0xff) {
+ rc = ops->read_emulated(c->modrm_ea, &eip,
+ c->op_bytes, ctxt->vcpu);
+ if (rc != 0)
+ goto cannot_emulate;
+
+ c->modrm_ea += c->op_bytes;
+ rc = ops->read_emulated(c->modrm_ea, &sel,
+ 2, ctxt->vcpu);
+ if (rc != 0)
+ goto cannot_emulate;
+
+ c->eip = eip;
+ if (load_segment_descriptor(ctxt->vcpu, sel, 9,
+ VCPU_SREG_CS) < 0) {
+ printk("failed to load cs!\n");
+ goto cannot_emulate;
+ }
+ goto done;
+ /* FIXME: if this is not a TSS jump need to
+ * perform register writeback.
+ * break;
+ */
+ }
switch (c->op_bytes) {
case 2:
eip = insn_fetch(u16, 2, c->eip);
@@ -1823,6 +1876,8 @@ special_insn:
c->dst.type = OP_NONE; /* Disable writeback. */
break;
case 0xfe ... 0xff: /* Grp4/Grp5 */
+ if (c->modrm_reg == 5)
+ goto jmpfar;
rc = emulate_grp45(ctxt, ops);
if (rc != 0)
goto done;
@@ -1847,6 +1902,22 @@ done:

twobyte_insn:
switch (c->b) {
+ case 0x0:
+ switch (c->modrm_reg) {
+ case 2: /* ldt */
+ if (load_segment_descriptor(ctxt->vcpu, c->src.val,
+ 0, VCPU_SREG_LDTR))
+ goto cannot_emulate;
+ break;
+ case 3: /* ltr */
+ if (load_segment_descriptor(ctxt->vcpu, c->src.val,
+ 1, VCPU_SREG_TR))
+ goto cannot_emulate;
+ break;
+ default:
+ goto cannot_emulate;
+ }
+ break;
case 0x01: /* lgdt, lidt, lmsw */
switch (c->modrm_reg) {
u16 size;

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