X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?p=palacios.git;a=blobdiff_plain;f=bios%2Fvmxassist%2Fvm86.c;fp=bios%2Fvmxassist%2Fvm86.c;h=8c620a4d5ce88b4afb2b6c46235296ff38c8bf52;hp=0000000000000000000000000000000000000000;hb=d34450b1e6fe3c2e1295c268c1722c669ba8d545;hpb=1a24d4e8fb3918a42b124777db5b41940150023e diff --git a/bios/vmxassist/vm86.c b/bios/vmxassist/vm86.c new file mode 100644 index 0000000..8c620a4 --- /dev/null +++ b/bios/vmxassist/vm86.c @@ -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[] = { + "", + "", + "", + "" +}; + +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("\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(); + } +}