Commit 5fae36af authored by Sebastien Badia's avatar Sebastien Badia

Imported Upstream version 1.4

parent 4e4314f9
changelog
VERSION
License information
-------------------
The x86emu library is under a BSD style license, comaptible
with the XFree86 and X licenses used by XFree86. The
original x86emu libraries were under the GNU General Public
License. Due to license incompatibilities between the GPL
and the XFree86 license, the original authors of the code
decided to allow a license change. If you have submitted
code to the original x86emu project, and you don't agree
with the license change, please contact us and let you
know. Your code will be removed to comply with your wishes.
If you have any questions about this, please send email to
x86emu@linuxlabs.com or KendallB@scitechsoft.com for
clarification.
ARCH := $(shell uname -m)
ifneq ($(filter i386 i486 i586 i686, $(ARCH)),)
ARCH := i386
endif
GIT2LOG := $(shell if [ -x ./git2log ] ; then echo ./git2log --update ; else echo true ; fi)
GITDEPS := $(shell [ -d .git ] && echo .git/HEAD .git/refs/heads .git/refs/tags)
CC = gcc
CFLAGS = -g -O2 -fPIC -fomit-frame-pointer -Wall
ifneq ($(filter x86_64, $(ARCH)),)
LIBDIR = /usr/lib64
else
LIBDIR = /usr/lib
endif
LIBX86 = libx86emu
VERSION := $(shell $(GIT2LOG) --version VERSION ; cat VERSION)
MAJOR_VERSION := $(shell $(GIT2LOG) --version VERSION ; cut -d . -f 1 VERSION)
CFILES = $(wildcard *.c)
OBJS = $(CFILES:.c=.o)
LIB_NAME = $(LIBX86).so.$(VERSION)
LIB_SONAME = $(LIBX86).so.$(MAJOR_VERSION)
.PHONY: all shared install test clean
%.o: %.c
$(CC) -c $(CFLAGS) $<
all: changelog shared
changelog: $(GITDEPS)
$(GIT2LOG) --changelog changelog
shared: $(LIB_NAME)
install: shared
install -D $(LIB_NAME) $(DESTDIR)$(LIBDIR)/$(LIB_NAME)
ln -snf $(LIB_NAME) $(DESTDIR)$(LIBDIR)/$(LIB_SONAME)
ln -snf $(LIB_SONAME) $(DESTDIR)$(LIBDIR)/$(LIBX86).so
install -m 644 -D include/x86emu.h $(DESTDIR)/usr/include/x86emu.h
$(LIB_NAME): .depend $(OBJS)
$(CC) -shared -Wl,-soname,$(LIB_SONAME) $(OBJS) -o $(LIB_NAME)
test:
make -C test
clean:
make -C test clean
rm -f *.o *~ include/*~ *.so.* .depend
ifneq "$(MAKECMDGOALS)" "clean"
.depend: $(CFILES)
@$(CC) -MG -MM $(CFLAGS) $(CFILES) >$@
-include .depend
endif
x86 emulation library
=====================
API functions
-------------
- create new emulation object
x86emu_t *x86emu_new(unsigned def_mem_perm, unsigned def_io_perm);
def_mem_perm are the default permissions for memory accesses, def_io_perm for io. See
x86emu_set_perm(), x86emu_set_io_perm().
Free object later with x86emu_done().
- delete emulation object
x86emu_t *x86emu_done(x86emu_t *emu);
Frees all memory; returns NULL;
- clone emulation object
x86emu_t *x86emu_clone(x86emu_t *emu);
Creates a copy of emu. Free the copy later with x86emu_done().
- reset cpu state
void x86emu_reset(x86emu_t *emu);
Does a normal cpu reset (clear registers, set cs:eip).
- start emulation
unsigned x86emu_run(x86emu_t *emu, unsigned flags);
- flags:
X86EMU_RUN_TIMEOUT
X86EMU_RUN_MAX_INSTR
X86EMU_RUN_NO_EXEC
X86EMU_RUN_NO_CODE
X86EMU_RUN_LOOP
X86EMU_RUN_TIMEOUT: set emu->timeout to max. seconds to run.
X86EMU_RUN_MAX_INSTR: set emu->max_instr to max. instructions to emulate.
Return value indicates why x86emu_run() stopped (see flags).
Note: copies emu to global variable x86emu while running!
- stop emulation
void x86emu_stop(x86emu_t *emu);
Use this function in callbacks (e.g. interrupt handler) to tell the emulator
to stop. The emulator returns from x86emu_run() when the current instruction
has been finished.
- set log buffer
void x86emu_set_log(x86emu_t *emu, char *buffer, unsigned buffer_size, x86emu_flush_func_t flush);
typedef void (* x86emu_flush_func_t)(x86emu_t *emu, char *buf, unsigned size);
If the log buffer is full, flush() is called (if not NULL). The buffer is freed in x86emu_done().
- write to log
void x86emu_log(x86emu_t *emu, const char *format, ...) __attribute__ ((format (printf, 1, 2)));
- clear log
void x86emu_clear_log(x86emu_t *emu, int flush);
Clear log buffer. If flush != 0, write current log via flush() function (see x86emu_set_log()).
- dump emulator state
void x86emu_dump(x86emu_t *emu, int flags);
- flags:
X86EMU_DUMP_REGS
X86EMU_DUMP_MEM
X86EMU_DUMP_ACC_MEM
X86EMU_DUMP_INV_MEM
X86EMU_DUMP_ATTR
X86EMU_DUMP_ASCII
X86EMU_DUMP_IO
X86EMU_DUMP_INTS
X86EMU_DUMP_TIME
Writes emulator state to log.
- memory permissions
void x86emu_set_perm(x86emu_t *emu, unsigned start, unsigned end, unsigned perm);
- perm: bitmask of
X86EMU_PERM_R
X86EMU_PERM_W
X86EMU_PERM_X
X86EMU_PERM_VALID
X86EMU_ACC_R
X86EMU_ACC_W
X86EMU_ACC_X
X86EMU_ACC_INVALID
X86EMU_PERM_{R,W,X}: memory is readable, writable, executable
X86EMU_PERM_VALID: memory has been initialized (say, been written to)
X86EMU_ACC_{R,W,X}: memory has been read, written, executed
X86EMU_ACC_INVALID: there was an invalid access (e.g. tried to read but not readable)
- direct memory access
void x86emu_set_page(x86emu_t *emu, unsigned offset, void *address);
Map memory area of X86EMU_PAGE_SIZE size at address into emulator at offset. offset
must be X86EMU_PAGE_SIZE aligned, address needs not.
Memory permissions still apply (via x86emu_set_perm()).
If address is NULL, switch back to emulated memory.
- io permissions
void x86emu_set_io_perm(x86emu_t *emu, unsigned start, unsigned end, unsigned perm);
- perm: see x86emu_set_perm()
- reset memory access statistics
void x86emu_reset_access_stats(x86emu_t *emu);
Resets the X86EMU_ACC_* bits for the whole memory (see x86emu_set_perm()).
- execution hook
x86emu_code_handler_t x86emu_set_code_handler(x86emu_t *emu, x86emu_code_handler_t handler);
typedef int (* x86emu_code_handler_t)(x86emu_t *emu);
If defined, the function is called before a new instruction is decoded and
emulated. If logging is enabled the current cpu state has already been
logged. If the function returns a value != 0, the emulation is stopped.
- set interrupt handler
x86emu_intr_handler_t x86emu_set_intr_handler(x86emu_t *emu, x86emu_intr_handler_t handler);
typedef int (* x86emu_intr_handler_t)(x86emu_t *emu, u8 num, unsigned type);
- type:
INTR_TYPE_SOFT
INTR_TYPE_FAULT
+ bitmask of
INTR_MODE_RESTART
INTR_MODE_ERRCODE
If defined, the interrupt handler is called at the start of the interrupt
handling procedure. The handler should return 1 to indicate the interrupt
handling is complete and the emulator can skip its own interrupt processing
or 0 to indicate the emulator should continue with normal interrupt processing.
- set alternative callback function that handles memory and io accesses
x86emu_memio_handler_t x86emu_set_memio_handler(x86emu_t *emu, x86emu_memio_handler_t handler);
typedef unsigned (* x86emu_memio_handler_t)(x86emu_t *emu, u32 addr, u32 *val, unsigned type);
- type: one of
X86EMU_MEMIO_8
X86EMU_MEMIO_16
X86EMU_MEMIO_32
X86EMU_MEMIO_8_NOPERM
+ one of
X86EMU_MEMIO_R
X86EMU_MEMIO_W
X86EMU_MEMIO_X
X86EMU_MEMIO_I
X86EMU_MEMIO_O
Returns old function.
- raise an interrupt
void x86emu_intr_raise(x86emu_t *emu, u8 intr_nr, unsigned type, unsigned err);
The interrupt is handled before the next instruction. For type see
x86emu_set_intr_func(); if INTR_MODE_ERRCODE is set, err is the error
code pushed to the stack.
- memory access functions
unsigned x86emu_read_byte(x86emu_t *emu, unsigned addr);
unsigned x86emu_read_byte_noperm(x86emu_t *emu, unsigned addr);
unsigned x86emu_read_word(x86emu_t *emu, unsigned addr);
unsigned x86emu_read_dword(x86emu_t *emu, unsigned addr);
void x86emu_write_byte(x86emu_t *emu, unsigned addr, unsigned val);
void x86emu_write_byte_noperm(x86emu_t *emu, unsigned addr, unsigned val);
void x86emu_write_word(x86emu_t *emu, unsigned addr, unsigned val);
void x86emu_write_dword(x86emu_t *emu, unsigned addr, unsigned val);
Convenience functions to access emulator memory. Memory access restrictions
(see x86emu_set_perm()) apply except for x86emu_*_noperm() which do not
check permissions.
- set segment register
void x86emu_set_seg_register(x86emu_t *emu, sel_t *seg, u16 val);
R_CS_SEL, R_DS_SEL, R_ES_SEL, R_FS_SEL, R_GS_SEL, R_SS_SEL
Example:
x86emu_set_seg_register(emu, emu->x86.R_CS_SEL, 0x7c0);
debug instruction
-----------------
If the X86EMU_TRACE_DEBUG flags is set, the emulator interprets a special debug intruction:
db 0x67, 0xeb, LEN, DATA...
which is basically a short jump with address size prefix (an instruction normally not
used). LEN is the size of DATA.
DATA can be:
- db 0x01
db STRING
Print STRING to log. STRING is not 0-zerminated.
- db 0x02
dd flags
Set trace flags.
- db 0x03
dd flags
Clear trace flags.
- db 0x04
dd flags
Dump emulator state. For flags, see x86emu_dump().
- db 0x05
Reset memory access stats. See x86emu_reset_access_stats().
#include "include/x86emu_int.h"
#define LINE_LEN 16
x86emu_t *x86emu_new(unsigned def_mem_perm, unsigned def_io_perm)
{
x86emu_t *emu = calloc(1, sizeof *emu);
emu->mem = emu_mem_new(def_mem_perm);
emu->io.map = calloc(X86EMU_IO_PORTS, sizeof *emu->io.map);
emu->io.stats_i = calloc(X86EMU_IO_PORTS, sizeof *emu->io.stats_i);
emu->io.stats_o = calloc(X86EMU_IO_PORTS, sizeof *emu->io.stats_o);
if(def_io_perm) x86emu_set_io_perm(emu, 0, X86EMU_IO_PORTS - 1, def_io_perm);
x86emu_set_memio_handler(emu, vm_memio);
x86emu_reset(emu);
return emu;
}
x86emu_t *x86emu_done(x86emu_t *emu)
{
if(emu) {
emu_mem_free(emu->mem);
free(emu->log.buf);
free(emu->io.map);
free(emu->io.stats_i);
free(emu->io.stats_o);
free(emu->x86.msr);
free(emu->x86.msr_perm);
free(emu);
}
return NULL;
}
x86emu_t *x86emu_clone(x86emu_t *emu)
{
x86emu_t *new_emu = NULL;
if(!emu) return new_emu;
new_emu = mem_dup(emu, sizeof *emu);
new_emu->mem = emu_mem_clone(emu->mem);
if(emu->log.buf && emu->log.ptr) {
new_emu->log.buf = malloc(emu->log.size);
// copy only used log space
if(emu->log.ptr <= emu->log.buf + emu->log.size) {
new_emu->log.ptr = new_emu->log.buf + (emu->log.ptr - emu->log.buf);
memcpy(new_emu->log.buf, emu->log.buf, emu->log.ptr - emu->log.buf);
}
}
new_emu->io.map = mem_dup(emu->io.map, X86EMU_IO_PORTS * sizeof *emu->io.map);
new_emu->io.stats_i = mem_dup(emu->io.stats_i, X86EMU_IO_PORTS * sizeof *emu->io.stats_i);
new_emu->io.stats_o = mem_dup(emu->io.stats_o, X86EMU_IO_PORTS * sizeof *emu->io.stats_o);
new_emu->x86.msr = mem_dup(emu->x86.msr, X86EMU_MSRS * sizeof *emu->x86.msr);
new_emu->x86.msr_perm = mem_dup(emu->x86.msr_perm, X86EMU_MSRS * sizeof *emu->x86.msr_perm);
return new_emu;
}
void x86emu_reset(x86emu_t *emu)
{
x86emu_regs_t *x86 = &emu->x86;
free(x86->msr);
free(x86->msr_perm);
memset(x86, 0, sizeof *x86);
x86->R_EFLG = 2;
x86->R_CS_LIMIT = x86->R_DS_LIMIT = x86->R_SS_LIMIT = x86->R_ES_LIMIT =
x86->R_FS_LIMIT = x86->R_GS_LIMIT = 0xffff;
// resp. 0x4093/9b for 4GB
x86->R_CS_ACC = 0x9b;
x86->R_SS_ACC = x86->R_DS_ACC = x86->R_ES_ACC = x86->R_FS_ACC = x86->R_GS_ACC = 0x93;
x86->R_CS = 0xf000;
x86->R_CS_BASE = 0xf0000;
x86->R_EIP = 0xfff0;
x86->R_GDT_LIMIT = 0xffff;
x86->R_IDT_LIMIT = 0xffff;
x86->msr = calloc(X86EMU_MSRS, sizeof *x86->msr);
x86->msr_perm = calloc(X86EMU_MSRS, sizeof *x86->msr_perm);
x86->msr_perm[0x10] = X86EMU_ACC_X; // tsc
x86->msr_perm[0x11] = X86EMU_ACC_X; // last real tsc
x86->msr_perm[0x12] = X86EMU_ACC_X; // real tsc
}
x86emu_memio_handler_t x86emu_set_memio_handler(x86emu_t *emu, x86emu_memio_handler_t handler)
{
x86emu_memio_handler_t old = NULL;
if(emu) {
old = emu->memio;
emu->memio = handler;
}
return old;
}
x86emu_intr_handler_t x86emu_set_intr_handler(x86emu_t *emu, x86emu_intr_handler_t handler)
{
x86emu_intr_handler_t old = NULL;
if(emu) {
old = emu->intr;
emu->intr = handler;
}
return old;
}
x86emu_code_handler_t x86emu_set_code_handler(x86emu_t *emu, x86emu_code_handler_t handler)
{
x86emu_code_handler_t old = NULL;
if(emu) {
old = emu->code_check;
emu->code_check = handler;
}
return old;
}
unsigned x86emu_read_byte(x86emu_t *emu, unsigned addr)
{
u32 val = 0xff;
if(emu) emu->memio(emu, addr, &val, X86EMU_MEMIO_R | X86EMU_MEMIO_8);
return val;
}
unsigned x86emu_read_byte_noperm(x86emu_t *emu, unsigned addr)
{
u32 val = 0xff;
if(emu) emu->memio(emu, addr, &val, X86EMU_MEMIO_R | X86EMU_MEMIO_8_NOPERM);
return val;
}
unsigned x86emu_read_word(x86emu_t *emu, unsigned addr)
{
u32 val = 0xffff;
if(emu) emu->memio(emu, addr, &val, X86EMU_MEMIO_R | X86EMU_MEMIO_16);
return val;
}
unsigned x86emu_read_dword(x86emu_t *emu, unsigned addr)
{
u32 val = 0xffffffff;
if(emu) emu->memio(emu, addr, &val, X86EMU_MEMIO_R | X86EMU_MEMIO_32);
return val;
}
void x86emu_write_byte(x86emu_t *emu, unsigned addr, unsigned val)
{
u32 val32 = val;
if(emu) emu->memio(emu, addr, &val32, X86EMU_MEMIO_W | X86EMU_MEMIO_8);
}
void x86emu_write_byte_noperm(x86emu_t *emu, unsigned addr, unsigned val)
{
u32 val32 = val;
if(emu) emu->memio(emu, addr, &val32, X86EMU_MEMIO_W | X86EMU_MEMIO_8_NOPERM);
}
void x86emu_write_word(x86emu_t *emu, unsigned addr, unsigned val)
{
u32 val32 = val;
if(emu) emu->memio(emu, addr, &val32, X86EMU_MEMIO_W | X86EMU_MEMIO_16);
}
void x86emu_write_dword(x86emu_t *emu, unsigned addr, unsigned val)
{
u32 val32 = val;
if(emu) emu->memio(emu, addr, &val32, X86EMU_MEMIO_W | X86EMU_MEMIO_32);
}
void x86emu_set_log(x86emu_t *emu, unsigned buffer_size, x86emu_flush_func_t flush)
{
if(emu) {
if(emu->log.buf) free(emu->log.buf);
emu->log.size = buffer_size;
emu->log.buf = buffer_size ? calloc(1, buffer_size) : NULL;
emu->log.ptr = emu->log.buf;
emu->log.flush = flush;
}
}
unsigned x86emu_clear_log(x86emu_t *emu, int flush)
{
if(!emu) emu = &M;
if(flush && emu->log.flush) {
if(emu->log.ptr && emu->log.ptr != emu->log.buf) {
emu->log.flush(emu, emu->log.buf, emu->log.ptr - emu->log.buf);
}
}
if((emu->log.ptr = emu->log.buf)) *emu->log.ptr = 0;
return emu->log.ptr ? LOG_FREE(emu) : 0;
}
void x86emu_log(x86emu_t *emu, const char *format, ...)
{
va_list args;
int size;
if(!emu || !emu->log.ptr) return;
size = emu->log.size - (emu->log.ptr - emu->log.buf);
va_start(args, format);
if(size > 0) {
size = vsnprintf(emu->log.ptr, size, format, args);
if(size > 0) {
emu->log.ptr += size;
}
else {
*emu->log.ptr = 0;
}
}
va_end(args);
}
/*
* flags:
* bits 0-7:
* 0: show all initialized memory
* 1: show only accessed memory
* 2: show only invalidly accessed memory
*
* bit 8: show ascii, too
*/
static void dump_data(unsigned char *data, unsigned char *attr, char *str_data, char *str_attr, int flags)
{
unsigned u, u1, flag_ascii;
char c;
int ok = 0;
char *sd = str_data, *sa = str_attr;
char *ascii = str_data + 4 * LINE_LEN + 2;
flag_ascii = flags & 0x100;
flags &= 0xff;
for(u = 0; u < LINE_LEN; u++) {
*str_data++ = (attr[u] & X86EMU_ACC_INVALID) ? '*' : ' ';
if(
(flags == 0 && (attr[u] & X86EMU_PERM_VALID)) ||
(flags == 1 && (attr[u] & (X86EMU_ACC_R | X86EMU_ACC_W | X86EMU_ACC_X | X86EMU_ACC_INVALID))) ||
(flags == 2 && (attr[u] & X86EMU_ACC_INVALID))
) {
ok = 1;
decode_hex2(&str_data, u1 = data[u]);
c = (attr[u] & X86EMU_PERM_R) ? (attr[u] & X86EMU_ACC_R) ? 'R' : 'r' : ' ';
*str_attr++ = c;
c = (attr[u] & X86EMU_PERM_W) ? (attr[u] & X86EMU_ACC_W) ? 'W' : 'w' : ' ';
*str_attr++ = c;
c = (attr[u] & X86EMU_PERM_X) ? (attr[u] & X86EMU_ACC_X) ? 'X' : 'x' : ' ';
*str_attr++ = c;
if(u1 < 0x20 || u1 >= 0x7f) u1 = '.';
ascii[u] = u1;
}
else {
*str_data++ = ' ';
*str_data++ = ' ';
*str_attr++ = ' ';
*str_attr++ = ' ';
*str_attr++ = ' ';
ascii[u] = ' ';
}
*str_data++ = ' ';
*str_attr++ = ' ';
}
if(ok) {
if(flag_ascii) {
str_data[0] = ' ';
str_data[1] = ' ';
str_data += 2 + LINE_LEN;
}
}
else {
str_data = sd;
str_attr = sa;
}
*str_data = *str_attr = 0;
while(str_data > sd && str_data[-1] == ' ') *--str_data = 0;
while(str_attr > sa && str_attr[-1] == ' ') *--str_attr = 0;
}
void x86emu_dump(x86emu_t *emu, int flags)
{
x86emu_mem_t *mem = emu->mem;
mem2_pdir_t *pdir;
mem2_ptable_t *ptable;
mem2_page_t page;
unsigned pdir_idx, u, u1, u2, addr;
char str_data[LINE_LEN * 8], str_attr[LINE_LEN * 8], fbuf[64];
unsigned char def_data[LINE_LEN], def_attr[LINE_LEN];
int dump_flags;
if(
mem &&
mem->pdir &&
(flags & (X86EMU_DUMP_MEM | X86EMU_DUMP_ACC_MEM | X86EMU_DUMP_INV_MEM | X86EMU_DUMP_ATTR))
) {
x86emu_log(emu, "; - - memory\n");
x86emu_log(emu, "; ");
for(u1 = 0; u1 < 16; u1++) x86emu_log(emu, "%4x", u1);
x86emu_log(emu, "\n");
dump_flags = 0;
if(flags & X86EMU_DUMP_INV_MEM) dump_flags = 2;
if(flags & X86EMU_DUMP_ACC_MEM) dump_flags = 1;
if(flags & X86EMU_DUMP_MEM) dump_flags = 0;
if(flags & X86EMU_DUMP_ASCII) dump_flags |= 0x100;
pdir = mem->pdir;
for(pdir_idx = 0; pdir_idx < (1 << X86EMU_PDIR_BITS); pdir_idx++) {