Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Ported palacios to Kbuild
[palacios.git] / bios / vmxassist / vm86.c
diff --git a/bios/vmxassist/vm86.c b/bios/vmxassist/vm86.c
new file mode 100644 (file)
index 0000000..8c620a4
--- /dev/null
@@ -0,0 +1,1650 @@
+/*
+ * 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();
+       }
+}