--- /dev/null
+/*
+ * vm86.c: A vm86 emulator. The main purpose of this emulator is to do as
+ * little work as possible.
+ *
+ * Leendert van Doorn, leendert@watson.ibm.com
+ * Copyright (c) 2005-2006, International Business Machines Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+#include "vm86.h"
+#include "util.h"
+#include "machine.h"
+
+#define HIGHMEM (1 << 20) /* 1MB */
+#define MASK16(v) ((v) & 0xFFFF)
+
+#define DATA32 0x0001
+#define ADDR32 0x0002
+#define SEG_CS 0x0004
+#define SEG_DS 0x0008
+#define SEG_ES 0x0010
+#define SEG_SS 0x0020
+#define SEG_FS 0x0040
+#define SEG_GS 0x0080
+
+static unsigned prev_eip = 0;
+enum vm86_mode mode = 0;
+
+static struct regs saved_rm_regs;
+
+#ifdef DEBUG
+int traceset = 0;
+
+char *states[] = {
+ "<VM86_REAL>",
+ "<VM86_REAL_TO_PROTECTED>",
+ "<VM86_PROTECTED_TO_REAL>",
+ "<VM86_PROTECTED>"
+};
+
+static char *rnames[] = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di" };
+#endif /* DEBUG */
+
+#define PDE_PS (1 << 7)
+#define PT_ENTRY_PRESENT 0x1
+
+/* We only support access to <=4G physical memory due to 1:1 mapping */
+static unsigned
+guest_linear_to_real(uint32_t base)
+{
+ uint32_t gcr3 = oldctx.cr3;
+ uint64_t l2_mfn;
+ uint64_t l1_mfn;
+ uint64_t l0_mfn;
+
+ if (!(oldctx.cr0 & CR0_PG))
+ return base;
+
+ if (!(oldctx.cr4 & CR4_PAE)) {
+ l1_mfn = ((uint32_t *)(long)gcr3)[(base >> 22) & 0x3ff];
+ if (!(l1_mfn & PT_ENTRY_PRESENT))
+ panic("l2 entry not present\n");
+
+ if ((oldctx.cr4 & CR4_PSE) && (l1_mfn & PDE_PS)) {
+ l0_mfn = l1_mfn & 0xffc00000;
+ return l0_mfn + (base & 0x3fffff);
+ }
+
+ l1_mfn &= 0xfffff000;
+
+ l0_mfn = ((uint32_t *)(long)l1_mfn)[(base >> 12) & 0x3ff];
+ if (!(l0_mfn & PT_ENTRY_PRESENT))
+ panic("l1 entry not present\n");
+ l0_mfn &= 0xfffff000;
+
+ return l0_mfn + (base & 0xfff);
+ } else {
+ l2_mfn = ((uint64_t *)(long)gcr3)[(base >> 30) & 0x3];
+ if (!(l2_mfn & PT_ENTRY_PRESENT))
+ panic("l3 entry not present\n");
+ l2_mfn &= 0x3fffff000ULL;
+
+ l1_mfn = ((uint64_t *)(long)l2_mfn)[(base >> 21) & 0x1ff];
+ if (!(l1_mfn & PT_ENTRY_PRESENT))
+ panic("l2 entry not present\n");
+
+ if (l1_mfn & PDE_PS) { /* CR4.PSE is ignored in PAE mode */
+ l0_mfn = l1_mfn & 0x3ffe00000ULL;
+ return l0_mfn + (base & 0x1fffff);
+ }
+
+ l1_mfn &= 0x3fffff000ULL;
+
+ l0_mfn = ((uint64_t *)(long)l1_mfn)[(base >> 12) & 0x1ff];
+ if (!(l0_mfn & PT_ENTRY_PRESENT))
+ panic("l1 entry not present\n");
+ l0_mfn &= 0x3fffff000ULL;
+
+ return l0_mfn + (base & 0xfff);
+ }
+}
+
+static unsigned
+address(struct regs *regs, unsigned seg, unsigned off)
+{
+ unsigned long long entry;
+ unsigned seg_base, seg_limit;
+ unsigned entry_low, entry_high;
+
+ if (seg == 0) {
+ if (mode == VM86_REAL || mode == VM86_REAL_TO_PROTECTED)
+ return off;
+ else
+ panic("segment is zero, but not in real mode!\n");
+ }
+
+ if (mode == VM86_REAL || seg > oldctx.gdtr_limit ||
+ (mode == VM86_REAL_TO_PROTECTED && regs->cs == seg))
+ return ((seg & 0xFFFF) << 4) + off;
+
+ entry = ((unsigned long long *)
+ guest_linear_to_real(oldctx.gdtr_base))[seg >> 3];
+ entry_high = entry >> 32;
+ entry_low = entry & 0xFFFFFFFF;
+
+ seg_base = (entry_high & 0xFF000000) | ((entry >> 16) & 0xFFFFFF);
+ seg_limit = (entry_high & 0xF0000) | (entry_low & 0xFFFF);
+
+ if (entry_high & 0x8000 &&
+ ((entry_high & 0x800000 && off >> 12 <= seg_limit) ||
+ (!(entry_high & 0x800000) && off <= seg_limit)))
+ return seg_base + off;
+
+ panic("should never reach here in function address():\n\t"
+ "entry=0x%08x%08x, mode=%d, seg=0x%08x, offset=0x%08x\n",
+ entry_high, entry_low, mode, seg, off);
+
+ return 0;
+}
+
+#ifdef DEBUG
+void
+trace(struct regs *regs, int adjust, char *fmt, ...)
+{
+ unsigned off = regs->eip - adjust;
+ va_list ap;
+
+ if ((traceset & (1 << mode)) &&
+ (mode == VM86_REAL_TO_PROTECTED || mode == VM86_REAL)) {
+ /* 16-bit, seg:off addressing */
+ unsigned addr = address(regs, regs->cs, off);
+ printf("0x%08x: 0x%x:0x%04x ", addr, regs->cs, off);
+ printf("(%d) ", mode);
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ printf("\n");
+ }
+ if ((traceset & (1 << mode)) &&
+ (mode == VM86_PROTECTED_TO_REAL || mode == VM86_PROTECTED)) {
+ /* 16-bit, gdt addressing */
+ unsigned addr = address(regs, regs->cs, off);
+ printf("0x%08x: 0x%x:0x%08x ", addr, regs->cs, off);
+ printf("(%d) ", mode);
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ printf("\n");
+ }
+}
+#endif /* DEBUG */
+
+static inline unsigned
+read32(unsigned addr)
+{
+ return *(unsigned long *) addr;
+}
+
+static inline unsigned
+read16(unsigned addr)
+{
+ return *(unsigned short *) addr;
+}
+
+static inline unsigned
+read8(unsigned addr)
+{
+ return *(unsigned char *) addr;
+}
+
+static inline void
+write32(unsigned addr, unsigned value)
+{
+ *(unsigned long *) addr = value;
+}
+
+static inline void
+write16(unsigned addr, unsigned value)
+{
+ *(unsigned short *) addr = value;
+}
+
+static inline void
+write8(unsigned addr, unsigned value)
+{
+ *(unsigned char *) addr = value;
+}
+
+static inline void
+push32(struct regs *regs, unsigned value)
+{
+ regs->uesp -= 4;
+ write32(address(regs, regs->uss, MASK16(regs->uesp)), value);
+}
+
+static inline void
+push16(struct regs *regs, unsigned value)
+{
+ regs->uesp -= 2;
+ write16(address(regs, regs->uss, MASK16(regs->uesp)), value);
+}
+
+static inline unsigned
+pop32(struct regs *regs)
+{
+ unsigned value = read32(address(regs, regs->uss, MASK16(regs->uesp)));
+ regs->uesp += 4;
+ return value;
+}
+
+static inline unsigned
+pop16(struct regs *regs)
+{
+ unsigned value = read16(address(regs, regs->uss, MASK16(regs->uesp)));
+ regs->uesp += 2;
+ return value;
+}
+
+static inline unsigned
+fetch32(struct regs *regs)
+{
+ unsigned addr = address(regs, regs->cs, MASK16(regs->eip));
+
+ regs->eip += 4;
+ return read32(addr);
+}
+
+static inline unsigned
+fetch16(struct regs *regs)
+{
+ unsigned addr = address(regs, regs->cs, MASK16(regs->eip));
+
+ regs->eip += 2;
+ return read16(addr);
+}
+
+static inline unsigned
+fetch8(struct regs *regs)
+{
+ unsigned addr = address(regs, regs->cs, MASK16(regs->eip));
+
+ regs->eip++;
+ return read8(addr);
+}
+
+static unsigned
+getreg32(struct regs *regs, int r)
+{
+ switch (r & 7) {
+ case 0: return regs->eax;
+ case 1: return regs->ecx;
+ case 2: return regs->edx;
+ case 3: return regs->ebx;
+ case 4: return regs->esp;
+ case 5: return regs->ebp;
+ case 6: return regs->esi;
+ case 7: return regs->edi;
+ }
+ return ~0;
+}
+
+static unsigned
+getreg16(struct regs *regs, int r)
+{
+ return MASK16(getreg32(regs, r));
+}
+
+static unsigned
+getreg8(struct regs *regs, int r)
+{
+ switch (r & 7) {
+ case 0: return regs->eax & 0xFF; /* al */
+ case 1: return regs->ecx & 0xFF; /* cl */
+ case 2: return regs->edx & 0xFF; /* dl */
+ case 3: return regs->ebx & 0xFF; /* bl */
+ case 4: return (regs->esp >> 8) & 0xFF; /* ah */
+ case 5: return (regs->ebp >> 8) & 0xFF; /* ch */
+ case 6: return (regs->esi >> 8) & 0xFF; /* dh */
+ case 7: return (regs->edi >> 8) & 0xFF; /* bh */
+ }
+ return ~0;
+}
+
+static void
+setreg32(struct regs *regs, int r, unsigned v)
+{
+ switch (r & 7) {
+ case 0: regs->eax = v; break;
+ case 1: regs->ecx = v; break;
+ case 2: regs->edx = v; break;
+ case 3: regs->ebx = v; break;
+ case 4: regs->esp = v; break;
+ case 5: regs->ebp = v; break;
+ case 6: regs->esi = v; break;
+ case 7: regs->edi = v; break;
+ }
+}
+
+static void
+setreg16(struct regs *regs, int r, unsigned v)
+{
+ setreg32(regs, r, (getreg32(regs, r) & ~0xFFFF) | MASK16(v));
+}
+
+static void
+setreg8(struct regs *regs, int r, unsigned v)
+{
+ v &= 0xFF;
+ switch (r & 7) {
+ case 0: regs->eax = (regs->eax & ~0xFF) | v; break;
+ case 1: regs->ecx = (regs->ecx & ~0xFF) | v; break;
+ case 2: regs->edx = (regs->edx & ~0xFF) | v; break;
+ case 3: regs->ebx = (regs->ebx & ~0xFF) | v; break;
+ case 4: regs->esp = (regs->esp & ~0xFF00) | (v << 8); break;
+ case 5: regs->ebp = (regs->ebp & ~0xFF00) | (v << 8); break;
+ case 6: regs->esi = (regs->esi & ~0xFF00) | (v << 8); break;
+ case 7: regs->edi = (regs->edi & ~0xFF00) | (v << 8); break;
+ }
+}
+
+static unsigned
+segment(unsigned prefix, struct regs *regs, unsigned seg)
+{
+ if (prefix & SEG_ES)
+ seg = regs->ves;
+ if (prefix & SEG_DS)
+ seg = regs->vds;
+ if (prefix & SEG_CS)
+ seg = regs->cs;
+ if (prefix & SEG_SS)
+ seg = regs->uss;
+ if (prefix & SEG_FS)
+ seg = regs->fs;
+ if (prefix & SEG_GS)
+ seg = regs->gs;
+ return seg;
+}
+
+static unsigned
+sib(struct regs *regs, int mod, unsigned byte)
+{
+ unsigned scale = (byte >> 6) & 3;
+ int index = (byte >> 3) & 7;
+ int base = byte & 7;
+ unsigned addr = 0;
+
+ switch (mod) {
+ case 0:
+ if (base == 5)
+ addr = fetch32(regs);
+ else
+ addr = getreg32(regs, base);
+ break;
+ case 1:
+ addr = getreg32(regs, base) + (char) fetch8(regs);
+ break;
+ case 2:
+ addr = getreg32(regs, base) + fetch32(regs);
+ break;
+ }
+
+ if (index != 4)
+ addr += getreg32(regs, index) << scale;
+
+ return addr;
+}
+
+/*
+ * Operand (modrm) decode
+ */
+static unsigned
+operand(unsigned prefix, struct regs *regs, unsigned modrm)
+{
+ int mod, disp = 0, seg;
+
+ seg = segment(prefix, regs, regs->vds);
+
+ if (prefix & ADDR32) { /* 32-bit addressing */
+ switch ((mod = (modrm >> 6) & 3)) {
+ case 0:
+ switch (modrm & 7) {
+ case 0: return address(regs, seg, regs->eax);
+ case 1: return address(regs, seg, regs->ecx);
+ case 2: return address(regs, seg, regs->edx);
+ case 3: return address(regs, seg, regs->ebx);
+ case 4: return address(regs, seg,
+ sib(regs, mod, fetch8(regs)));
+ case 5: return address(regs, seg, fetch32(regs));
+ case 6: return address(regs, seg, regs->esi);
+ case 7: return address(regs, seg, regs->edi);
+ }
+ break;
+ case 1:
+ case 2:
+ if ((modrm & 7) != 4) {
+ if (mod == 1)
+ disp = (char) fetch8(regs);
+ else
+ disp = (int) fetch32(regs);
+ }
+ switch (modrm & 7) {
+ case 0: return address(regs, seg, regs->eax + disp);
+ case 1: return address(regs, seg, regs->ecx + disp);
+ case 2: return address(regs, seg, regs->edx + disp);
+ case 3: return address(regs, seg, regs->ebx + disp);
+ case 4: return address(regs, seg,
+ sib(regs, mod, fetch8(regs)));
+ case 5: return address(regs, seg, regs->ebp + disp);
+ case 6: return address(regs, seg, regs->esi + disp);
+ case 7: return address(regs, seg, regs->edi + disp);
+ }
+ break;
+ case 3:
+ return getreg32(regs, modrm);
+ }
+ } else { /* 16-bit addressing */
+ switch ((mod = (modrm >> 6) & 3)) {
+ case 0:
+ switch (modrm & 7) {
+ case 0: return address(regs, seg, MASK16(regs->ebx) +
+ MASK16(regs->esi));
+ case 1: return address(regs, seg, MASK16(regs->ebx) +
+ MASK16(regs->edi));
+ case 2: return address(regs, seg, MASK16(regs->ebp) +
+ MASK16(regs->esi));
+ case 3: return address(regs, seg, MASK16(regs->ebp) +
+ MASK16(regs->edi));
+ case 4: return address(regs, seg, MASK16(regs->esi));
+ case 5: return address(regs, seg, MASK16(regs->edi));
+ case 6: return address(regs, seg, fetch16(regs));
+ case 7: return address(regs, seg, MASK16(regs->ebx));
+ }
+ break;
+ case 1:
+ case 2:
+ if (mod == 1)
+ disp = (char) fetch8(regs);
+ else
+ disp = (int) fetch16(regs);
+ switch (modrm & 7) {
+ case 0: return address(regs, seg, MASK16(regs->ebx) +
+ MASK16(regs->esi) + disp);
+ case 1: return address(regs, seg, MASK16(regs->ebx) +
+ MASK16(regs->edi) + disp);
+ case 2: return address(regs, seg, MASK16(regs->ebp) +
+ MASK16(regs->esi) + disp);
+ case 3: return address(regs, seg, MASK16(regs->ebp) +
+ MASK16(regs->edi) + disp);
+ case 4: return address(regs, seg,
+ MASK16(regs->esi) + disp);
+ case 5: return address(regs, seg,
+ MASK16(regs->edi) + disp);
+ case 6: return address(regs, seg,
+ MASK16(regs->ebp) + disp);
+ case 7: return address(regs, seg,
+ MASK16(regs->ebx) + disp);
+ }
+ break;
+ case 3:
+ return getreg16(regs, modrm);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Load new IDT
+ */
+static int
+lidt(struct regs *regs, unsigned prefix, unsigned modrm)
+{
+ unsigned eip = regs->eip - 3;
+ unsigned addr = operand(prefix, regs, modrm);
+
+ oldctx.idtr_limit = ((struct dtr *) addr)->size;
+ if ((prefix & DATA32) == 0)
+ oldctx.idtr_base = ((struct dtr *) addr)->base & 0xFFFFFF;
+ else
+ oldctx.idtr_base = ((struct dtr *) addr)->base;
+ TRACE((regs, regs->eip - eip, "lidt 0x%x <%d, 0x%x>",
+ addr, oldctx.idtr_limit, oldctx.idtr_base));
+
+ return 1;
+}
+
+/*
+ * Load new GDT
+ */
+static int
+lgdt(struct regs *regs, unsigned prefix, unsigned modrm)
+{
+ unsigned eip = regs->eip - 3;
+ unsigned addr = operand(prefix, regs, modrm);
+
+ oldctx.gdtr_limit = ((struct dtr *) addr)->size;
+ if ((prefix & DATA32) == 0)
+ oldctx.gdtr_base = ((struct dtr *) addr)->base & 0xFFFFFF;
+ else
+ oldctx.gdtr_base = ((struct dtr *) addr)->base;
+ TRACE((regs, regs->eip - eip, "lgdt 0x%x <%d, 0x%x>",
+ addr, oldctx.gdtr_limit, oldctx.gdtr_base));
+
+ return 1;
+}
+
+/*
+ * Modify CR0 either through an lmsw instruction.
+ */
+static int
+lmsw(struct regs *regs, unsigned prefix, unsigned modrm)
+{
+ unsigned eip = regs->eip - 3;
+ unsigned ax = operand(prefix, regs, modrm) & 0xF;
+ unsigned cr0 = (oldctx.cr0 & 0xFFFFFFF0) | ax;
+
+ TRACE((regs, regs->eip - eip, "lmsw 0x%x", ax));
+#ifndef TEST
+ oldctx.cr0 = cr0 | CR0_PE | CR0_NE;
+#else
+ oldctx.cr0 = cr0 | CR0_PE | CR0_NE | CR0_PG;
+#endif
+ if (cr0 & CR0_PE)
+ set_mode(regs, VM86_REAL_TO_PROTECTED);
+
+ return 1;
+}
+
+/*
+ * We need to handle moves that address memory beyond the 64KB segment
+ * limit that VM8086 mode enforces.
+ */
+static int
+movr(struct regs *regs, unsigned prefix, unsigned opc)
+{
+ unsigned eip = regs->eip - 1;
+ unsigned modrm = fetch8(regs);
+ unsigned addr = operand(prefix, regs, modrm);
+ unsigned val, r = (modrm >> 3) & 7;
+
+ if ((modrm & 0xC0) == 0xC0) /* no registers */
+ return 0;
+
+ switch (opc) {
+ case 0x88: /* addr32 mov r8, r/m8 */
+ val = getreg8(regs, r);
+ TRACE((regs, regs->eip - eip,
+ "movb %%e%s, *0x%x", rnames[r], addr));
+ write8(addr, val);
+ break;
+
+ case 0x8A: /* addr32 mov r/m8, r8 */
+ TRACE((regs, regs->eip - eip,
+ "movb *0x%x, %%%s", addr, rnames[r]));
+ setreg8(regs, r, read8(addr));
+ break;
+
+ case 0x89: /* addr32 mov r16, r/m16 */
+ val = getreg32(regs, r);
+ if (prefix & DATA32) {
+ TRACE((regs, regs->eip - eip,
+ "movl %%e%s, *0x%x", rnames[r], addr));
+ write32(addr, val);
+ } else {
+ TRACE((regs, regs->eip - eip,
+ "movw %%%s, *0x%x", rnames[r], addr));
+ write16(addr, MASK16(val));
+ }
+ break;
+
+ case 0x8B: /* addr32 mov r/m16, r16 */
+ if (prefix & DATA32) {
+ TRACE((regs, regs->eip - eip,
+ "movl *0x%x, %%e%s", addr, rnames[r]));
+ setreg32(regs, r, read32(addr));
+ } else {
+ TRACE((regs, regs->eip - eip,
+ "movw *0x%x, %%%s", addr, rnames[r]));
+ setreg16(regs, r, read16(addr));
+ }
+ break;
+
+ case 0xC6: /* addr32 movb $imm, r/m8 */
+ if ((modrm >> 3) & 7)
+ return 0;
+ val = fetch8(regs);
+ write8(addr, val);
+ TRACE((regs, regs->eip - eip, "movb $0x%x, *0x%x",
+ val, addr));
+ break;
+ }
+ return 1;
+}
+
+/*
+ * Move to and from a control register.
+ */
+static int
+movcr(struct regs *regs, unsigned prefix, unsigned opc)
+{
+ unsigned eip = regs->eip - 2;
+ unsigned modrm = fetch8(regs);
+ unsigned cr = (modrm >> 3) & 7;
+
+ if ((modrm & 0xC0) != 0xC0) /* only registers */
+ return 0;
+
+ switch (opc) {
+ case 0x20: /* mov Rd, Cd */
+ TRACE((regs, regs->eip - eip, "movl %%cr%d, %%eax", cr));
+ switch (cr) {
+ case 0:
+#ifndef TEST
+ setreg32(regs, modrm,
+ oldctx.cr0 & ~(CR0_PE | CR0_NE));
+#else
+ setreg32(regs, modrm,
+ oldctx.cr0 & ~(CR0_PE | CR0_NE | CR0_PG));
+#endif
+ break;
+ case 2:
+ setreg32(regs, modrm, get_cr2());
+ break;
+ case 3:
+ setreg32(regs, modrm, oldctx.cr3);
+ break;
+ case 4:
+ setreg32(regs, modrm, oldctx.cr4);
+ break;
+ }
+ break;
+ case 0x22: /* mov Cd, Rd */
+ TRACE((regs, regs->eip - eip, "movl %%eax, %%cr%d", cr));
+ switch (cr) {
+ case 0:
+ oldctx.cr0 = getreg32(regs, modrm) | (CR0_PE | CR0_NE);
+#ifdef TEST
+ oldctx.cr0 |= CR0_PG;
+#endif
+ if (getreg32(regs, modrm) & CR0_PE)
+ set_mode(regs, VM86_REAL_TO_PROTECTED);
+ else
+ set_mode(regs, VM86_REAL);
+ break;
+ case 3:
+ oldctx.cr3 = getreg32(regs, modrm);
+ break;
+ case 4:
+ oldctx.cr4 = getreg32(regs, modrm);
+ break;
+ }
+ break;
+ }
+
+ return 1;
+}
+
+static inline void set_eflags_ZF(unsigned mask, unsigned v1, struct regs *regs)
+{
+ if ((v1 & mask) == 0)
+ regs->eflags |= EFLAGS_ZF;
+ else
+ regs->eflags &= ~EFLAGS_ZF;
+}
+
+/*
+ * We need to handle cmp opcodes that address memory beyond the 64KB
+ * segment limit that VM8086 mode enforces.
+ */
+static int
+cmp(struct regs *regs, unsigned prefix, unsigned opc)
+{
+ unsigned eip = regs->eip - 1;
+ unsigned modrm = fetch8(regs);
+ unsigned addr = operand(prefix, regs, modrm);
+ unsigned diff, val, r = (modrm >> 3) & 7;
+
+ if ((modrm & 0xC0) == 0xC0) /* no registers */
+ return 0;
+
+ switch (opc) {
+ case 0x39: /* addr32 cmp r16, r/m16 */
+ val = getreg32(regs, r);
+ if (prefix & DATA32) {
+ diff = read32(addr) - val;
+ set_eflags_ZF(~0, diff, regs);
+
+ TRACE((regs, regs->eip - eip,
+ "cmp %%e%s, *0x%x (0x%x)",
+ rnames[r], addr, diff));
+ } else {
+ diff = read16(addr) - val;
+ set_eflags_ZF(0xFFFF, diff, regs);
+
+ TRACE((regs, regs->eip - eip,
+ "cmp %%%s, *0x%x (0x%x)",
+ rnames[r], addr, diff));
+ }
+ break;
+
+ /* other cmp opcodes ... */
+ }
+ return 1;
+}
+
+/*
+ * We need to handle test opcodes that address memory beyond the 64KB
+ * segment limit that VM8086 mode enforces.
+ */
+static int
+test(struct regs *regs, unsigned prefix, unsigned opc)
+{
+ unsigned eip = regs->eip - 1;
+ unsigned modrm = fetch8(regs);
+ unsigned addr = operand(prefix, regs, modrm);
+ unsigned val, diff;
+
+ if ((modrm & 0xC0) == 0xC0) /* no registers */
+ return 0;
+
+ switch (opc) {
+ case 0xF6: /* testb $imm, r/m8 */
+ if ((modrm >> 3) & 7)
+ return 0;
+ val = fetch8(regs);
+ diff = read8(addr) & val;
+ set_eflags_ZF(0xFF, diff, regs);
+
+ TRACE((regs, regs->eip - eip, "testb $0x%x, *0x%x (0x%x)",
+ val, addr, diff));
+ break;
+
+ /* other test opcodes ... */
+ }
+
+ return 1;
+}
+
+/*
+ * We need to handle pop opcodes that address memory beyond the 64KB
+ * segment limit that VM8086 mode enforces.
+ */
+static int
+pop(struct regs *regs, unsigned prefix, unsigned opc)
+{
+ unsigned eip = regs->eip - 1;
+ unsigned modrm = fetch8(regs);
+ unsigned addr = operand(prefix, regs, modrm);
+
+ if ((modrm & 0xC0) == 0xC0) /* no registers */
+ return 0;
+
+ switch (opc) {
+ case 0x8F: /* pop r/m16 */
+ if ((modrm >> 3) & 7)
+ return 0;
+ if (prefix & DATA32)
+ write32(addr, pop32(regs));
+ else
+ write16(addr, pop16(regs));
+ TRACE((regs, regs->eip - eip, "pop *0x%x", addr));
+ break;
+
+ /* other pop opcodes ... */
+ }
+
+ return 1;
+}
+
+/*
+ * Emulate a segment load in protected mode
+ */
+static int
+load_seg(unsigned long sel, uint32_t *base, uint32_t *limit, union vmcs_arbytes *arbytes)
+{
+ unsigned long long entry;
+
+ /* protected mode: use seg as index into gdt */
+ if (sel > oldctx.gdtr_limit)
+ return 0;
+
+ if (sel == 0) {
+ arbytes->fields.null_bit = 1;
+ return 1;
+ }
+
+ entry = ((unsigned long long *)
+ guest_linear_to_real(oldctx.gdtr_base))[sel >> 3];
+
+ /* Check the P bit first */
+ if (!((entry >> (15+32)) & 0x1) && sel != 0)
+ return 0;
+
+ *base = (((entry >> (56-24)) & 0xFF000000) |
+ ((entry >> (32-16)) & 0x00FF0000) |
+ ((entry >> ( 16)) & 0x0000FFFF));
+ *limit = (((entry >> (48-16)) & 0x000F0000) |
+ ((entry ) & 0x0000FFFF));
+
+ arbytes->bytes = 0;
+ arbytes->fields.seg_type = (entry >> (8+32)) & 0xF; /* TYPE */
+ arbytes->fields.s = (entry >> (12+32)) & 0x1; /* S */
+ if (arbytes->fields.s)
+ arbytes->fields.seg_type |= 1; /* accessed */
+ arbytes->fields.dpl = (entry >> (13+32)) & 0x3; /* DPL */
+ arbytes->fields.p = (entry >> (15+32)) & 0x1; /* P */
+ arbytes->fields.avl = (entry >> (20+32)) & 0x1; /* AVL */
+ arbytes->fields.default_ops_size = (entry >> (22+32)) & 0x1; /* D */
+
+ if (entry & (1ULL << (23+32))) { /* G */
+ arbytes->fields.g = 1;
+ *limit = (*limit << 12) | 0xFFF;
+ }
+
+ return 1;
+}
+
+/*
+ * Transition to protected mode
+ */
+static void
+protected_mode(struct regs *regs)
+{
+ regs->eflags &= ~(EFLAGS_TF|EFLAGS_VM);
+
+ oldctx.eip = regs->eip;
+ oldctx.esp = regs->uesp;
+ oldctx.eflags = regs->eflags;
+
+ memset(&saved_rm_regs, 0, sizeof(struct regs));
+
+ /* reload all segment registers */
+ if (!load_seg(regs->cs, &oldctx.cs_base,
+ &oldctx.cs_limit, &oldctx.cs_arbytes))
+ panic("Invalid %%cs=0x%x for protected mode\n", regs->cs);
+ oldctx.cs_sel = regs->cs;
+
+ if (load_seg(regs->ves, &oldctx.es_base,
+ &oldctx.es_limit, &oldctx.es_arbytes))
+ oldctx.es_sel = regs->ves;
+ else {
+ load_seg(0, &oldctx.es_base,
+ &oldctx.es_limit, &oldctx.es_arbytes);
+ oldctx.es_sel = 0;
+ saved_rm_regs.ves = regs->ves;
+ }
+
+ if (load_seg(regs->uss, &oldctx.ss_base,
+ &oldctx.ss_limit, &oldctx.ss_arbytes))
+ oldctx.ss_sel = regs->uss;
+ else {
+ load_seg(0, &oldctx.ss_base,
+ &oldctx.ss_limit, &oldctx.ss_arbytes);
+ oldctx.ss_sel = 0;
+ saved_rm_regs.uss = regs->uss;
+ }
+
+ if (load_seg(regs->vds, &oldctx.ds_base,
+ &oldctx.ds_limit, &oldctx.ds_arbytes))
+ oldctx.ds_sel = regs->vds;
+ else {
+ load_seg(0, &oldctx.ds_base,
+ &oldctx.ds_limit, &oldctx.ds_arbytes);
+ oldctx.ds_sel = 0;
+ saved_rm_regs.vds = regs->vds;
+ }
+
+ if (load_seg(regs->vfs, &oldctx.fs_base,
+ &oldctx.fs_limit, &oldctx.fs_arbytes))
+ oldctx.fs_sel = regs->vfs;
+ else {
+ load_seg(0, &oldctx.fs_base,
+ &oldctx.fs_limit, &oldctx.fs_arbytes);
+ oldctx.fs_sel = 0;
+ saved_rm_regs.vfs = regs->vfs;
+ }
+
+ if (load_seg(regs->vgs, &oldctx.gs_base,
+ &oldctx.gs_limit, &oldctx.gs_arbytes))
+ oldctx.gs_sel = regs->vgs;
+ else {
+ load_seg(0, &oldctx.gs_base,
+ &oldctx.gs_limit, &oldctx.gs_arbytes);
+ oldctx.gs_sel = 0;
+ saved_rm_regs.vgs = regs->vgs;
+ }
+
+ /* initialize jump environment to warp back to protected mode */
+ regs->cs = CODE_SELECTOR;
+ regs->ds = DATA_SELECTOR;
+ regs->es = DATA_SELECTOR;
+ regs->fs = DATA_SELECTOR;
+ regs->gs = DATA_SELECTOR;
+ regs->eip = (unsigned) &switch_to_protected_mode;
+
+ /* this should get us into 32-bit mode */
+}
+
+/*
+ * Start real-mode emulation
+ */
+static void
+real_mode(struct regs *regs)
+{
+ regs->eflags |= EFLAGS_VM | 0x02;
+ regs->ds = DATA_SELECTOR;
+ regs->es = DATA_SELECTOR;
+ regs->fs = DATA_SELECTOR;
+ regs->gs = DATA_SELECTOR;
+
+ /*
+ * When we transition from protected to real-mode and we
+ * have not reloaded the segment descriptors yet, they are
+ * interpreted as if they were in protect mode.
+ * We emulate this behavior by assuming that these memory
+ * reference are below 1MB and set %ss, %ds, %es accordingly.
+ */
+ if (regs->uss != 0) {
+ if (regs->uss >= HIGHMEM)
+ panic("%%ss 0x%lx higher than 1MB", regs->uss);
+ regs->uss = address(regs, regs->uss, 0) >> 4;
+ } else {
+ regs->uss = saved_rm_regs.uss;
+ }
+ if (regs->vds != 0) {
+ if (regs->vds >= HIGHMEM)
+ panic("%%ds 0x%lx higher than 1MB", regs->vds);
+ regs->vds = address(regs, regs->vds, 0) >> 4;
+ } else {
+ regs->vds = saved_rm_regs.vds;
+ }
+ if (regs->ves != 0) {
+ if (regs->ves >= HIGHMEM)
+ panic("%%es 0x%lx higher than 1MB", regs->ves);
+ regs->ves = address(regs, regs->ves, 0) >> 4;
+ } else {
+ regs->ves = saved_rm_regs.ves;
+ }
+
+ /* this should get us into 16-bit mode */
+}
+
+/*
+ * This is the smarts of the emulator and handles the mode transitions. The
+ * emulator handles 4 different modes. 1) VM86_REAL: emulated real-mode,
+ * Just handle those instructions that are not supported under VM8086.
+ * 2) VM86_REAL_TO_PROTECTED: going from real-mode to protected mode. In
+ * this we single step through the instructions until we reload the
+ * new %cs (some OSes do a lot of computations before reloading %cs). 2)
+ * VM86_PROTECTED_TO_REAL when we are going from protected to real mode. In
+ * this case we emulate the instructions by hand. Finally, 4) VM86_PROTECTED
+ * when we transitioned to protected mode and we should abandon the
+ * emulator. No instructions are emulated when in VM86_PROTECTED mode.
+ */
+void
+set_mode(struct regs *regs, enum vm86_mode newmode)
+{
+ switch (newmode) {
+ case VM86_REAL:
+ if ((mode == VM86_PROTECTED_TO_REAL) ||
+ (mode == VM86_REAL_TO_PROTECTED)) {
+ regs->eflags &= ~EFLAGS_TF;
+ real_mode(regs);
+ break;
+ } else if (mode == VM86_REAL) {
+ break;
+ } else
+ panic("unexpected real mode transition");
+ break;
+
+ case VM86_REAL_TO_PROTECTED:
+ if (mode == VM86_REAL) {
+ regs->eflags |= EFLAGS_TF;
+ break;
+ } else if (mode == VM86_REAL_TO_PROTECTED) {
+ break;
+ } else
+ panic("unexpected real-to-protected mode transition");
+ break;
+
+ case VM86_PROTECTED_TO_REAL:
+ if (mode == VM86_PROTECTED) {
+ break;
+ } else
+ panic("unexpected protected-to-real mode transition");
+ break;
+
+ case VM86_PROTECTED:
+ if (mode == VM86_REAL_TO_PROTECTED) {
+ protected_mode(regs);
+// printf("<VM86_PROTECTED>\n");
+ mode = newmode;
+ return;
+ } else
+ panic("unexpected protected mode transition");
+ break;
+ }
+
+ mode = newmode;
+ TRACE((regs, 0, states[mode]));
+}
+
+static void
+jmpl(struct regs *regs, int prefix)
+{
+ unsigned n = regs->eip;
+ unsigned cs, eip;
+
+ if (mode == VM86_REAL_TO_PROTECTED) { /* jump to protected mode */
+ eip = (prefix & DATA32) ? fetch32(regs) : fetch16(regs);
+ cs = fetch16(regs);
+
+ TRACE((regs, (regs->eip - n) + 1, "jmpl 0x%x:0x%x", cs, eip));
+
+ regs->cs = cs;
+ regs->eip = eip;
+ set_mode(regs, VM86_PROTECTED);
+ } else if (mode == VM86_PROTECTED_TO_REAL) { /* jump to real mode */
+ eip = (prefix & DATA32) ? fetch32(regs) : fetch16(regs);
+ cs = fetch16(regs);
+
+ TRACE((regs, (regs->eip - n) + 1, "jmpl 0x%x:0x%x", cs, eip));
+
+ regs->cs = cs;
+ regs->eip = eip;
+ set_mode(regs, VM86_REAL);
+ } else
+ panic("jmpl");
+}
+
+static void
+jmpl_indirect(struct regs *regs, int prefix, unsigned modrm)
+{
+ unsigned n = regs->eip;
+ unsigned cs, eip;
+ unsigned addr;
+
+ addr = operand(prefix, regs, modrm);
+
+ if (mode == VM86_REAL_TO_PROTECTED) { /* jump to protected mode */
+ eip = (prefix & DATA32) ? read32(addr) : read16(addr);
+ addr += (prefix & DATA32) ? 4 : 2;
+ cs = read16(addr);
+
+ TRACE((regs, (regs->eip - n) + 1, "jmpl 0x%x:0x%x", cs, eip));
+
+ regs->cs = cs;
+ regs->eip = eip;
+ set_mode(regs, VM86_PROTECTED);
+ } else if (mode == VM86_PROTECTED_TO_REAL) { /* jump to real mode */
+ eip = (prefix & DATA32) ? read32(addr) : read16(addr);
+ addr += (prefix & DATA32) ? 4 : 2;
+ cs = read16(addr);
+
+ TRACE((regs, (regs->eip - n) + 1, "jmpl 0x%x:0x%x", cs, eip));
+
+ regs->cs = cs;
+ regs->eip = eip;
+ set_mode(regs, VM86_REAL);
+ } else
+ panic("jmpl");
+}
+
+static void
+retl(struct regs *regs, int prefix)
+{
+ unsigned cs, eip;
+
+ if (prefix & DATA32) {
+ eip = pop32(regs);
+ cs = MASK16(pop32(regs));
+ } else {
+ eip = pop16(regs);
+ cs = pop16(regs);
+ }
+
+ TRACE((regs, 1, "retl (to 0x%x:0x%x)", cs, eip));
+
+ if (mode == VM86_REAL_TO_PROTECTED) { /* jump to protected mode */
+ regs->cs = cs;
+ regs->eip = eip;
+ set_mode(regs, VM86_PROTECTED);
+ } else if (mode == VM86_PROTECTED_TO_REAL) { /* jump to real mode */
+ regs->cs = cs;
+ regs->eip = eip;
+ set_mode(regs, VM86_REAL);
+ } else
+ panic("retl");
+}
+
+static void
+interrupt(struct regs *regs, int n)
+{
+ TRACE((regs, 0, "external interrupt %d", n));
+ push16(regs, regs->eflags);
+ push16(regs, regs->cs);
+ push16(regs, regs->eip);
+ regs->eflags &= ~EFLAGS_IF;
+ regs->eip = read16(address(regs, 0, n * 4));
+ regs->cs = read16(address(regs, 0, n * 4 + 2));
+}
+
+/*
+ * Most port I/O operations are passed unmodified. We do have to be
+ * careful and make sure the emulated program isn't remapping the
+ * interrupt vectors. The following simple state machine catches
+ * these attempts and rewrites them.
+ */
+static int
+outbyte(struct regs *regs, unsigned prefix, unsigned opc)
+{
+ static char icw2[2] = { 0 };
+ int al, port;
+
+ switch (opc) {
+ case 0xE6: /* outb port, al */
+ port = fetch8(regs);
+ break;
+ case 0xEE: /* outb (%dx), al */
+ port = MASK16(regs->edx);
+ break;
+ default:
+ return 0;
+ }
+
+ al = regs->eax & 0xFF;
+
+ switch (port) {
+ case PIC_MASTER + PIC_CMD:
+ if (al & (1 << 4)) /* A0=0,D4=1 -> ICW1 */
+ icw2[0] = 1;
+ break;
+ case PIC_MASTER + PIC_IMR:
+ if (icw2[0]) {
+ icw2[0] = 0;
+ printf("Remapping master: ICW2 0x%x -> 0x%x\n",
+ al, NR_EXCEPTION_HANDLER);
+ al = NR_EXCEPTION_HANDLER;
+ }
+ break;
+
+ case PIC_SLAVE + PIC_CMD:
+ if (al & (1 << 4)) /* A0=0,D4=1 -> ICW1 */
+ icw2[1] = 1;
+ break;
+ case PIC_SLAVE + PIC_IMR:
+ if (icw2[1]) {
+ icw2[1] = 0;
+ printf("Remapping slave: ICW2 0x%x -> 0x%x\n",
+ al, NR_EXCEPTION_HANDLER+8);
+ al = NR_EXCEPTION_HANDLER+8;
+ }
+ break;
+ }
+
+ outb(port, al);
+ return 1;
+}
+
+static int
+inbyte(struct regs *regs, unsigned prefix, unsigned opc)
+{
+ int port;
+
+ switch (opc) {
+ case 0xE4: /* inb al, port */
+ port = fetch8(regs);
+ break;
+ case 0xEC: /* inb al, (%dx) */
+ port = MASK16(regs->edx);
+ break;
+ default:
+ return 0;
+ }
+
+ regs->eax = (regs->eax & ~0xFF) | inb(port);
+ return 1;
+}
+
+static void
+pushrm(struct regs *regs, int prefix, unsigned modrm)
+{
+ unsigned n = regs->eip;
+ unsigned addr;
+ unsigned data;
+
+ addr = operand(prefix, regs, modrm);
+
+ if (prefix & DATA32) {
+ data = read32(addr);
+ push32(regs, data);
+ } else {
+ data = read16(addr);
+ push16(regs, data);
+ }
+
+ TRACE((regs, (regs->eip - n) + 1, "push *0x%x", addr));
+}
+
+enum { OPC_INVALID, OPC_EMULATED };
+
+#define rdmsr(msr,val1,val2) \
+ __asm__ __volatile__( \
+ "rdmsr" \
+ : "=a" (val1), "=d" (val2) \
+ : "c" (msr))
+
+#define wrmsr(msr,val1,val2) \
+ __asm__ __volatile__( \
+ "wrmsr" \
+ : /* no outputs */ \
+ : "c" (msr), "a" (val1), "d" (val2))
+
+/*
+ * Emulate a single instruction, including all its prefixes. We only implement
+ * a small subset of the opcodes, and not all opcodes are implemented for each
+ * of the four modes we can operate in.
+ */
+static int
+opcode(struct regs *regs)
+{
+ unsigned eip = regs->eip;
+ unsigned opc, modrm, disp;
+ unsigned prefix = 0;
+
+ for (;;) {
+ switch ((opc = fetch8(regs))) {
+ case 0x07:
+ if (prefix & DATA32)
+ regs->ves = pop32(regs);
+ else
+ regs->ves = pop16(regs);
+ TRACE((regs, regs->eip - eip, "pop %%es"));
+ return OPC_EMULATED;
+
+ case 0x0F: /* two byte opcode */
+ if (mode == VM86_PROTECTED)
+ goto invalid;
+ switch ((opc = fetch8(regs))) {
+ case 0x01:
+ switch (((modrm = fetch8(regs)) >> 3) & 7) {
+ case 0: /* sgdt */
+ case 1: /* sidt */
+ goto invalid;
+ case 2: /* lgdt */
+ if (!lgdt(regs, prefix, modrm))
+ goto invalid;
+ return OPC_EMULATED;
+ case 3: /* lidt */
+ if (!lidt(regs, prefix, modrm))
+ goto invalid;
+ return OPC_EMULATED;
+ case 4: /* smsw */
+ goto invalid;
+ case 5:
+ goto invalid;
+ case 6: /* lmsw */
+ if (!lmsw(regs, prefix, modrm))
+ goto invalid;
+ return OPC_EMULATED;
+ case 7: /* invlpg */
+ goto invalid;
+ }
+ break;
+ case 0x09: /* wbinvd */
+ return OPC_EMULATED;
+ case 0x20: /* mov Rd, Cd (1h) */
+ case 0x22:
+ if (!movcr(regs, prefix, opc))
+ goto invalid;
+ return OPC_EMULATED;
+ case 0x30: /* WRMSR */
+ wrmsr(regs->ecx, regs->eax, regs->edx);
+ return OPC_EMULATED;
+ case 0x32: /* RDMSR */
+ rdmsr(regs->ecx, regs->eax, regs->edx);
+ return OPC_EMULATED;
+ default:
+ goto invalid;
+ }
+ goto invalid;
+
+ case 0x26:
+ TRACE((regs, regs->eip - eip, "%%es:"));
+ prefix |= SEG_ES;
+ continue;
+
+ case 0x2E:
+ TRACE((regs, regs->eip - eip, "%%cs:"));
+ prefix |= SEG_CS;
+ continue;
+
+ case 0x36:
+ TRACE((regs, regs->eip - eip, "%%ss:"));
+ prefix |= SEG_SS;
+ continue;
+
+ case 0x39: /* addr32 cmp r16, r/m16 */
+ case 0x3B: /* addr32 cmp r/m16, r16 */
+ if (mode != VM86_REAL && mode != VM86_REAL_TO_PROTECTED)
+ goto invalid;
+ if ((prefix & ADDR32) == 0)
+ goto invalid;
+ if (!cmp(regs, prefix, opc))
+ goto invalid;
+ return OPC_EMULATED;
+
+ case 0x3E:
+ TRACE((regs, regs->eip - eip, "%%ds:"));
+ prefix |= SEG_DS;
+ continue;
+
+ case 0x64:
+ TRACE((regs, regs->eip - eip, "%%fs:"));
+ prefix |= SEG_FS;
+ continue;
+
+ case 0x65:
+ TRACE((regs, regs->eip - eip, "%%gs:"));
+ prefix |= SEG_GS;
+ continue;
+
+ case 0x66:
+ TRACE((regs, regs->eip - eip, "data32"));
+ prefix |= DATA32;
+ continue;
+
+ case 0x67:
+ TRACE((regs, regs->eip - eip, "addr32"));
+ prefix |= ADDR32;
+ continue;
+
+ case 0x88: /* addr32 mov r8, r/m8 */
+ case 0x8A: /* addr32 mov r/m8, r8 */
+ if (mode != VM86_REAL && mode != VM86_REAL_TO_PROTECTED)
+ goto invalid;
+ if ((prefix & ADDR32) == 0)
+ goto invalid;
+ if (!movr(regs, prefix, opc))
+ goto invalid;
+ return OPC_EMULATED;
+
+ case 0x89: /* addr32 mov r16, r/m16 */
+ if (mode == VM86_PROTECTED_TO_REAL) {
+ unsigned modrm = fetch8(regs);
+ unsigned addr = operand(prefix, regs, modrm);
+ unsigned val, r = (modrm >> 3) & 7;
+
+ if (prefix & DATA32) {
+ val = getreg16(regs, r);
+ write32(addr, val);
+ } else {
+ val = getreg32(regs, r);
+ write16(addr, MASK16(val));
+ }
+ TRACE((regs, regs->eip - eip,
+ "mov %%%s, *0x%x", rnames[r], addr));
+ return OPC_EMULATED;
+ }
+ case 0x8B: /* addr32 mov r/m16, r16 */
+ if (mode != VM86_REAL && mode != VM86_REAL_TO_PROTECTED)
+ goto invalid;
+ if ((prefix & ADDR32) == 0)
+ goto invalid;
+ if (!movr(regs, prefix, opc))
+ goto invalid;
+ return OPC_EMULATED;
+
+ case 0x8F: /* addr32 pop r/m16 */
+ if ((prefix & ADDR32) == 0)
+ goto invalid;
+ if (!pop(regs, prefix, opc))
+ goto invalid;
+ return OPC_EMULATED;
+
+ case 0x90: /* nop */
+ TRACE((regs, regs->eip - eip, "nop"));
+ return OPC_EMULATED;
+
+ case 0x9C: /* pushf */
+ TRACE((regs, regs->eip - eip, "pushf"));
+ if (prefix & DATA32)
+ push32(regs, regs->eflags & ~EFLAGS_VM);
+ else
+ push16(regs, regs->eflags & ~EFLAGS_VM);
+ return OPC_EMULATED;
+
+ case 0x9D: /* popf */
+ TRACE((regs, regs->eip - eip, "popf"));
+ if (prefix & DATA32)
+ regs->eflags = pop32(regs);
+ else
+ regs->eflags = (regs->eflags & 0xFFFF0000L) |
+ pop16(regs);
+ regs->eflags |= EFLAGS_VM;
+ return OPC_EMULATED;
+
+ case 0xA1: /* mov ax, r/m16 */
+ {
+ int addr, data;
+ int seg = segment(prefix, regs, regs->vds);
+ int offset = prefix & ADDR32? fetch32(regs) : fetch16(regs);
+
+ if (prefix & DATA32) {
+ addr = address(regs, seg, offset);
+ data = read32(addr);
+ setreg32(regs, 0, data);
+ } else {
+ addr = address(regs, seg, offset);
+ data = read16(addr);
+ setreg16(regs, 0, data);
+ }
+ TRACE((regs, regs->eip - eip, "mov *0x%x, %%ax", addr));
+ }
+ return OPC_EMULATED;
+
+ case 0xBB: /* mov bx, imm16 */
+ {
+ int data;
+ if (prefix & DATA32) {
+ data = fetch32(regs);
+ setreg32(regs, 3, data);
+ } else {
+ data = fetch16(regs);
+ setreg16(regs, 3, data);
+ }
+ TRACE((regs, regs->eip - eip, "mov $0x%x, %%bx", data));
+ }
+ return OPC_EMULATED;
+
+ case 0xC6: /* addr32 movb $imm, r/m8 */
+ if ((prefix & ADDR32) == 0)
+ goto invalid;
+ if (!movr(regs, prefix, opc))
+ goto invalid;
+ return OPC_EMULATED;
+
+ case 0xCB: /* retl */
+ if ((mode == VM86_REAL_TO_PROTECTED) ||
+ (mode == VM86_PROTECTED_TO_REAL)) {
+ retl(regs, prefix);
+ return OPC_INVALID;
+ }
+ goto invalid;
+
+ case 0xCD: /* int $n */
+ TRACE((regs, regs->eip - eip, "int"));
+ interrupt(regs, fetch8(regs));
+ return OPC_EMULATED;
+
+ case 0xCF: /* iret */
+ if (prefix & DATA32) {
+ TRACE((regs, regs->eip - eip, "data32 iretd"));
+ regs->eip = pop32(regs);
+ regs->cs = pop32(regs);
+ regs->eflags = pop32(regs);
+ } else {
+ TRACE((regs, regs->eip - eip, "iret"));
+ regs->eip = pop16(regs);
+ regs->cs = pop16(regs);
+ regs->eflags = (regs->eflags & 0xFFFF0000L) |
+ pop16(regs);
+ }
+ return OPC_EMULATED;
+
+ case 0xE4: /* inb al, port */
+ if (!inbyte(regs, prefix, opc))
+ goto invalid;
+ return OPC_EMULATED;
+
+ case 0xE6: /* outb port, al */
+ if (!outbyte(regs, prefix, opc))
+ goto invalid;
+ return OPC_EMULATED;
+
+ case 0xEA: /* jmpl */
+ if ((mode == VM86_REAL_TO_PROTECTED) ||
+ (mode == VM86_PROTECTED_TO_REAL)) {
+ jmpl(regs, prefix);
+ return OPC_INVALID;
+ }
+ goto invalid;
+
+ case 0xFF: /* jmpl (indirect) */
+ {
+ unsigned modrm = fetch8(regs);
+ switch((modrm >> 3) & 7) {
+ case 5: /* jmpl (indirect) */
+ if ((mode == VM86_REAL_TO_PROTECTED) ||
+ (mode == VM86_PROTECTED_TO_REAL)) {
+ jmpl_indirect(regs, prefix, modrm);
+ return OPC_INVALID;
+ }
+ goto invalid;
+
+ case 6: /* push r/m16 */
+ pushrm(regs, prefix, modrm);
+ return OPC_EMULATED;
+
+ default:
+ goto invalid;
+ }
+ }
+
+ case 0xEB: /* short jump */
+ if ((mode == VM86_REAL_TO_PROTECTED) ||
+ (mode == VM86_PROTECTED_TO_REAL)) {
+ disp = (char) fetch8(regs);
+ TRACE((regs, 2, "jmp 0x%x", regs->eip + disp));
+ regs->eip += disp;
+ return OPC_EMULATED;
+ }
+ goto invalid;
+
+ case 0xEC: /* inb al, (%dx) */
+ if (!inbyte(regs, prefix, opc))
+ goto invalid;
+ return OPC_EMULATED;
+
+ case 0xEE: /* outb (%dx), al */
+ if (!outbyte(regs, prefix, opc))
+ goto invalid;
+ return OPC_EMULATED;
+
+ case 0xF0: /* lock */
+ TRACE((regs, regs->eip - eip, "lock"));
+ continue;
+
+ case 0xF6: /* addr32 testb $imm, r/m8 */
+ if ((prefix & ADDR32) == 0)
+ goto invalid;
+ if (!test(regs, prefix, opc))
+ goto invalid;
+ return OPC_EMULATED;
+
+ case 0xFA: /* cli */
+ TRACE((regs, regs->eip - eip, "cli"));
+ regs->eflags &= ~EFLAGS_IF;
+ return OPC_EMULATED;
+
+ case 0xFB: /* sti */
+ TRACE((regs, regs->eip - eip, "sti"));
+ regs->eflags |= EFLAGS_IF;
+ return OPC_EMULATED;
+
+ default:
+ goto invalid;
+ }
+ }
+
+invalid:
+ regs->eip = eip;
+ TRACE((regs, regs->eip - eip, "opc 0x%x", opc));
+ return OPC_INVALID;
+}
+
+void
+emulate(struct regs *regs)
+{
+ unsigned flteip;
+ int nemul = 0;
+
+ /* emulate as many instructions as possible */
+ while (opcode(regs) != OPC_INVALID)
+ nemul++;
+
+ /* detect the case where we are not making progress */
+ if (nemul == 0 && prev_eip == regs->eip) {
+ flteip = address(regs, MASK16(regs->cs), regs->eip);
+ panic("Unknown opcode at %04x:%04x=0x%x",
+ MASK16(regs->cs), regs->eip, flteip);
+ } else
+ prev_eip = regs->eip;
+}
+
+void
+trap(int trapno, int errno, struct regs *regs)
+{
+ /* emulate device interrupts */
+ if (trapno >= NR_EXCEPTION_HANDLER) {
+ int irq = trapno - NR_EXCEPTION_HANDLER;
+ if (irq < 8)
+ interrupt(regs, irq + 8);
+ else
+ interrupt(regs, 0x70 + (irq - 8));
+ return;
+ }
+
+ switch (trapno) {
+ case 1: /* Debug */
+ if (regs->eflags & EFLAGS_VM) {
+ /* emulate any 8086 instructions */
+ if (mode != VM86_REAL_TO_PROTECTED)
+ panic("not in real-to-protected mode");
+ emulate(regs);
+ return;
+ }
+ goto invalid;
+
+ case 13: /* GPF */
+ if (regs->eflags & EFLAGS_VM) {
+ /* emulate any 8086 instructions */
+ if (mode == VM86_PROTECTED)
+ panic("unexpected protected mode");
+ emulate(regs);
+ return;
+ }
+ goto invalid;
+
+ default:
+ invalid:
+ printf("Trap (0x%x) while in %s mode\n",
+ trapno, regs->eflags & EFLAGS_VM ? "real" : "protected");
+ if (trapno == 14)
+ printf("Page fault address 0x%x\n", get_cr2());
+ dump_regs(regs);
+ halt();
+ }
+}