/* * 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 uint64_t guest_linear_to_phys(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 &= 0xffffff000ULL; if (l2_mfn & 0xf00000000ULL) { printf("l2 page above 4G\n"); cpuid_addr_value(l2_mfn + 8 * ((base >> 21) & 0x1ff), &l1_mfn); } else 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 & 0xfffe00000ULL; return l0_mfn + (base & 0x1fffff); } l1_mfn &= 0xffffff000ULL; if (l1_mfn & 0xf00000000ULL) { printf("l1 page above 4G\n"); cpuid_addr_value(l1_mfn + 8 * ((base >> 12) & 0x1ff), &l0_mfn); } else l0_mfn = ((uint64_t *)(long)l1_mfn)[(base >> 12) & 0x1ff]; if (!(l0_mfn & PT_ENTRY_PRESENT)) panic("l1 entry not present\n"); l0_mfn &= 0xffffff000ULL; return l0_mfn + (base & 0xfff); } } static unsigned address(struct regs *regs, unsigned seg, unsigned off) { uint64_t gdt_phys_base; 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; gdt_phys_base = guest_linear_to_phys(oldctx.gdtr_base); if (gdt_phys_base != (uint32_t)gdt_phys_base) { printf("gdt base address above 4G\n"); cpuid_addr_value(gdt_phys_base + 8 * (seg >> 3), &entry); } else entry = ((unsigned long long *)(long)gdt_phys_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
/*
 * lib/error.c		Error Handling
 *
 *	This library is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU Lesser General Public
 *	License as published by the Free Software Foundation version 2.1
 *	of the License.
 *
 * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
 */

#include <netlink-local.h>
#include <netlink/netlink.h>

static const char *errmsg[NLE_MAX+1] = {
[NLE_SUCCESS]		= "Success",
[NLE_FAILURE]		= "Unspecific failure",
[NLE_INTR]		= "Interrupted system call",
[NLE_BAD_SOCK]		= "Bad socket",
[NLE_AGAIN]		= "Try again",
[NLE_NOMEM]		= "Out of memory",
[NLE_EXIST]		= "Object exists",
[NLE_INVAL]		= "Invalid input data or parameter",
[NLE_RANGE]		= "Input data out of range",
[NLE_MSGSIZE]		= "Message size not sufficient",
[NLE_OPNOTSUPP]		= "Operation not supported",
[NLE_AF_NOSUPPORT]	= "Address family not supported",
[NLE_OBJ_NOTFOUND]	= "Object not found",
[NLE_NOATTR]		= "Attribute not available",
[NLE_MISSING_ATTR]	= "Missing attribute",
[NLE_AF_MISMATCH]	= "Address family mismatch",
[NLE_SEQ_MISMATCH]	= "Message sequence number mismatch",
[NLE_MSG_OVERFLOW]	= "Kernel reported message overflow",
[NLE_MSG_TRUNC]		= "Kernel reported truncated message",
[NLE_NOADDR]		= "Invalid address for specified address family",
[NLE_SRCRT_NOSUPPORT]	= "Source based routing not supported",
[NLE_MSG_TOOSHORT]	= "Netlink message is too short",
[NLE_MSGTYPE_NOSUPPORT]	= "Netlink message type is not supported",
[NLE_OBJ_MISMATCH]	= "Object type does not match cache",
[NLE_NOCACHE]		= "Unknown or invalid cache type",
[NLE_BUSY]		= "Object busy",
[NLE_PROTO_MISMATCH]	= "Protocol mismatch",
[NLE_NOACCESS]		= "No Access",
[NLE_PERM]		= "Operation not permitted",
[NLE_PKTLOC_FILE]	= "Unable to open packet location file",
[NLE_PARSE_ERR]		= "Unable to parse object",
[NLE_NODEV]		= "No such device",
[NLE_IMMUTABLE]		= "Immutable attribute",
[NLE_DUMP_INTR]		= "Dump inconsistency detected, interrupted",
};

/**
 * Return error message for an error code
 * @return error message
 */
const char *nl_geterror(int error)
{
	error = abs(error);

	if (error > NLE_MAX)
		error = NLE_FAILURE;

	return errmsg[error];
}

/**
 * Print a libnl error message
 * @arg s		error message prefix
 *
 * Prints the error message of the call that failed last.
 *
 * If s is not NULL and *s is not a null byte the argument
 * string is printed, followed by a colon and a blank. Then
 * the error message and a new-line.
 */
void nl_perror(int error, const char *s)
{
	if (s && *s)
		fprintf(stderr, "%s: %s\n", s, nl_geterror(error));
	else
		fprintf(stderr, "%s\n", nl_geterror(error));
}

int nl_syserr2nlerr(int error)
{
	error = abs(error);

	switch (error) {
	case EBADF:		return NLE_BAD_SOCK;
	case EADDRINUSE:	return NLE_EXIST;
	case EEXIST:		return NLE_EXIST;
	case EADDRNOTAVAIL:	return NLE_NOADDR;
	case ESRCH:		/* fall through */
	case ENOENT:		return NLE_OBJ_NOTFOUND;
	case EINTR:		return NLE_INTR;
	case EAGAIN:		return NLE_AGAIN;
	case ENOTSOCK:		return NLE_BAD_SOCK;
	case ENOPROTOOPT:	return NLE_INVAL;
	case EFAULT:		return NLE_INVAL;
	case EACCES:		return NLE_NOACCESS;
	case EINVAL:		return NLE_INVAL;
	case ENOBUFS:		return NLE_NOMEM;
	case ENOMEM:		return NLE_NOMEM;
	case EAFNOSUPPORT:	return NLE_AF_NOSUPPORT;
	case EPROTONOSUPPORT:	return NLE_PROTO_MISMATCH;
	case EOPNOTSUPP:	return NLE_OPNOTSUPP;
	case EPERM:		return NLE_PERM;
	case EBUSY:		return NLE_BUSY;
	case ERANGE:		return NLE_RANGE;
	case ENODEV:		return NLE_NODEV;
	default:		return NLE_FAILURE;
	}
}

/** @} */
); } 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: /* pop %es */ regs->ves = (prefix & DATA32) ? pop32(regs) : pop16(regs); TRACE((regs, regs->eip - eip, "pop %%es")); if (mode == VM86_REAL_TO_PROTECTED) { saved_rm_regs.ves = 0; oldctx.es_sel = regs->ves; } 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 0x1F: /* pop %ds */ regs->vds = (prefix & DATA32) ? pop32(regs) : pop16(regs); TRACE((regs, regs->eip - eip, "pop %%ds")); if (mode == VM86_REAL_TO_PROTECTED) { saved_rm_regs.vds = 0; oldctx.ds_sel = regs->vds; } return OPC_EMULATED; 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 0x8E: /* mov r16, sreg */ if (!mov_to_seg(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(); } }