Compare commits
37 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
1eb20527c8 | ||
|
e3e86d56c4 | ||
|
1df912cf9e | ||
|
4351832355 | ||
|
59faf6d6a6 | ||
|
725af7d460 | ||
|
a363e34cc5 | ||
|
ea041c0e33 | ||
|
83479e770d | ||
|
e2f2289897 | ||
|
844c72eccc | ||
|
6b1534cc67 | ||
|
e8cd23de30 | ||
|
7c2d6a781c | ||
|
f1510b2cc3 | ||
|
357a94326c | ||
|
0824d6fc67 | ||
|
6c0372d30b | ||
|
92ccca6aa8 | ||
|
dd3587f38e | ||
|
7d83131cc5 | ||
|
66e85a21c7 | ||
|
90a9fdae1f | ||
|
3fb2ded1d5 | ||
|
f76af4b3f3 | ||
|
717fc2ad8d | ||
|
c05bab779e | ||
|
a52c757c9f | ||
|
970a87a6bb | ||
|
d8bc1fd0ae | ||
|
7501267e22 | ||
|
13b55754af | ||
|
972ddf7840 | ||
|
322d0c6657 | ||
|
2054396a04 | ||
|
039de852ec | ||
|
144c345daf |
18
Changelog
18
Changelog
@@ -1,19 +1,27 @@
|
||||
version 0.4:
|
||||
|
||||
- initial support for ring 0 x86 processor emulation
|
||||
- fixed signal handling for correct dosemu DPMI emulation
|
||||
- fast x86 MMU emulation with mmap()
|
||||
- fixed popl (%esp) case
|
||||
- Linux kernel can be executed by QEMU with the 'vl' command.
|
||||
|
||||
version 0.3:
|
||||
|
||||
- initial support for ARM emulation
|
||||
- added fnsave, frstor, fnstenv, fldenv FPU instructions
|
||||
- added FPU register save in signal emulation
|
||||
- ARM port
|
||||
- initial ARM port
|
||||
- Sparc and Alpha ports work on the regression test
|
||||
- generic ioctl number conversion
|
||||
- fixed ioctl type conversion
|
||||
|
||||
version 0.2:
|
||||
|
||||
- PowerPC disassembly and ELF symbols output (Rusty Russel)
|
||||
- flock support (Rusty Russel)
|
||||
- ugetrlimit support (Rusty Russel)
|
||||
- fstat64 fix (Rusty Russel)
|
||||
- PowerPC disassembly and ELF symbols output (Rusty Russell)
|
||||
- flock support (Rusty Russell)
|
||||
- ugetrlimit support (Rusty Russell)
|
||||
- fstat64 fix (Rusty Russell)
|
||||
- initial Alpha port (Falk Hueffner)
|
||||
- initial IA64 port (Matt Wilson)
|
||||
- initial Sparc and Sparc64 port (David S. Miller)
|
||||
|
23
Makefile
23
Makefile
@@ -5,6 +5,7 @@ LDFLAGS=-g
|
||||
LIBS=
|
||||
DEFINES=-DHAVE_BYTESWAP_H
|
||||
HELPER_CFLAGS=$(CFLAGS)
|
||||
PROGS=qemu
|
||||
|
||||
ifdef CONFIG_STATIC
|
||||
LDFLAGS+=-static
|
||||
@@ -13,7 +14,7 @@ endif
|
||||
ifeq ($(ARCH),i386)
|
||||
CFLAGS+=-fomit-frame-pointer
|
||||
OP_CFLAGS=$(CFLAGS) -mpreferred-stack-boundary=2
|
||||
ifeq ($(GCC_MAJOR),3)
|
||||
ifeq ($(HAVE_GCC3_OPTIONS),yes)
|
||||
OP_CFLAGS+= -falign-functions=0
|
||||
else
|
||||
OP_CFLAGS+= -malign-functions=0
|
||||
@@ -26,6 +27,9 @@ else
|
||||
# is the simplest way to make it self virtualizable!
|
||||
LDFLAGS+=-Wl,-shared
|
||||
endif
|
||||
ifeq ($(TARGET_ARCH), i386)
|
||||
PROGS+=vl
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ppc)
|
||||
@@ -70,7 +74,7 @@ OP_CFLAGS=$(CFLAGS) -mno-sched-prolog
|
||||
LDFLAGS+=-Wl,-T,arm.ld
|
||||
endif
|
||||
|
||||
ifeq ($(GCC_MAJOR),3)
|
||||
ifeq ($(HAVE_GCC3_OPTIONS),yes)
|
||||
# very important to generate a return at the end of every operation
|
||||
OP_CFLAGS+=-fno-reorder-blocks -fno-optimize-sibling-calls
|
||||
endif
|
||||
@@ -125,7 +129,7 @@ ifeq ($(ARCH),ia64)
|
||||
OBJS += ia64-syscall.o
|
||||
endif
|
||||
|
||||
all: qemu qemu-doc.html
|
||||
all: $(PROGS) qemu-doc.html
|
||||
|
||||
qemu: $(OBJS)
|
||||
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
@@ -135,6 +139,10 @@ ifeq ($(ARCH),alpha)
|
||||
echo -ne '\001\000\000\000' | dd of=qemu bs=1 seek=48 count=4 conv=notrunc
|
||||
endif
|
||||
|
||||
# must use static linking to avoid leaving stuff in virtual address space
|
||||
vl: vl.o libqemu.a
|
||||
$(CC) -static -Wl,-T,i386-vl.ld -o $@ $^ $(LIBS)
|
||||
|
||||
depend: $(SRCS)
|
||||
$(CC) -MM $(CFLAGS) $^ 1>.depend
|
||||
|
||||
@@ -180,8 +188,8 @@ clean:
|
||||
distclean: clean
|
||||
rm -f config.mak config.h
|
||||
|
||||
install: qemu
|
||||
install -m 755 -s qemu $(prefix)/bin
|
||||
install: $(PROGS)
|
||||
install -m 755 -s $(PROGS) $(prefix)/bin
|
||||
|
||||
# various test targets
|
||||
test speed: qemu
|
||||
@@ -200,7 +208,8 @@ configure \
|
||||
dyngen.c dyngen.h dyngen-exec.h ioctls.h syscall_types.h \
|
||||
Makefile elf.h elfload.c main.c signal.c qemu.h \
|
||||
syscall.c syscall_defs.h vm86.c path.c mmap.c \
|
||||
ppc.ld alpha.ld s390.ld sparc.ld arm.ld\
|
||||
i386.ld ppc.ld alpha.ld s390.ld sparc.ld arm.ld\
|
||||
vl.c i386-vl.ld\
|
||||
thunk.c cpu-exec.c translate.c cpu-all.h thunk.h exec.h\
|
||||
exec.c cpu-exec.c\
|
||||
cpu-i386.h op-i386.c helper-i386.c syscall-i386.h translate-i386.c \
|
||||
@@ -211,7 +220,7 @@ arm-dis.c \
|
||||
tests/Makefile \
|
||||
tests/test-i386.c tests/test-i386-shift.h tests/test-i386.h \
|
||||
tests/test-i386-muldiv.h tests/test-i386-code16.S tests/test-i386-vm86.S \
|
||||
tests/hello.c tests/hello \
|
||||
tests/hello-i386.c tests/hello-i386 \
|
||||
tests/hello-arm.c tests/hello-arm \
|
||||
tests/sha1.c \
|
||||
tests/testsig.c tests/testclone.c tests/testthread.c \
|
||||
|
17
README
17
README
@@ -45,22 +45,25 @@ that QEMU works if you do not use a tested gcc version. Look at
|
||||
'configure' and 'Makefile' if you want to make a different gcc
|
||||
version work.
|
||||
|
||||
host gcc binutils glibc linux
|
||||
-------------------------------------------------------
|
||||
x86 2.95.2 2.13.2 2.1.3 2.4.18
|
||||
host gcc binutils glibc linux distribution
|
||||
----------------------------------------------------------------------
|
||||
x86 2.95.2 2.13.2 2.1.3 2.4.18
|
||||
3.2 2.13.2 2.1.3 2.4.18
|
||||
2.96 2.11.93.0.2 2.2.5 2.4.18 Red Hat 7.3
|
||||
|
||||
PowerPC 2.95.4 2.12.90.0.1 2.2.5 2.4.20-pre2
|
||||
PowerPC 2.95.4 2.12.90.0.1 2.2.5 2.4.20-pre2 Debian 3.0
|
||||
|
||||
Alpha 3.3 [1] 2.14.90.0.4 2.2.5 2.2.20 [2]
|
||||
Alpha 3.3 [1] 2.14.90.0.4 2.2.5 2.2.20 [2] Debian 3.0
|
||||
|
||||
Sparc32 2.95.4 2.12.90.0.1 2.2.5 2.4.18
|
||||
Sparc32 2.95.4 2.12.90.0.1 2.2.5 2.4.18 Debian 3.0
|
||||
|
||||
ARM 2.95.4 2.12.90.0.1 2.2.5 2.4.9-ac10-rmk2-np1-cerf2
|
||||
ARM 2.95.4 2.12.90.0.1 2.2.5 2.4.9 [3] Debian 3.0
|
||||
|
||||
[1] On Alpha, QEMU needs the gcc 'visibility' attribute only available
|
||||
for gcc version >= 3.3.
|
||||
[2] Linux >= 2.4.20 is necessary for precise exception support
|
||||
(untested).
|
||||
[3] 2.4.9-ac10-rmk2-np1-cerf2
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
2
TODO
2
TODO
@@ -1,4 +1,5 @@
|
||||
|
||||
- finish segment ops (call far, ret far, load_seg suppressed)
|
||||
- fix arm fpu rounding (at least for float->integer conversions)
|
||||
- fix CCOP optimisation
|
||||
- optimize FPU operations (evaluate x87 stack pointer statically)
|
||||
@@ -7,7 +8,6 @@
|
||||
state, find a solution for tb_flush()).
|
||||
- add gcc 2.96 test configure (some gcc3 flags are needed)
|
||||
- add IPC syscalls
|
||||
- submit a patch to fix DOSEMU coopthreads
|
||||
|
||||
lower priority:
|
||||
--------------
|
||||
|
17
configure
vendored
17
configure
vendored
@@ -153,20 +153,15 @@ fi
|
||||
|
||||
fi
|
||||
|
||||
# check gcc version
|
||||
# check gcc options support
|
||||
cat > $TMPC <<EOF
|
||||
int main(void) {
|
||||
#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
|
||||
return 0;
|
||||
#else
|
||||
#error gcc < 3.2
|
||||
#endif
|
||||
}
|
||||
EOF
|
||||
|
||||
gcc_major="2"
|
||||
if $cc -o $TMPO $TMPC 2> /dev/null ; then
|
||||
gcc_major="3"
|
||||
have_gcc3_options="no"
|
||||
if $cc -fno-reorder-blocks -fno-optimize-sibling-calls -o $TMPO $TMPC 2> /dev/null ; then
|
||||
have_gcc3_options="yes"
|
||||
fi
|
||||
|
||||
if test "$target_bigendian" = "default" ; then
|
||||
@@ -224,7 +219,9 @@ echo "prefix=$prefix" >> config.mak
|
||||
echo "#define CONFIG_QEMU_PREFIX \"$interp_prefix\"" >> $TMPH
|
||||
echo "MAKE=$make" >> config.mak
|
||||
echo "CC=$cc" >> config.mak
|
||||
echo "GCC_MAJOR=$gcc_major" >> config.mak
|
||||
if test "$have_gcc3_options" = "yes" ; then
|
||||
echo "HAVE_GCC3_OPTIONS=yes" >> config.mak
|
||||
fi
|
||||
echo "HOST_CC=$host_cc" >> config.mak
|
||||
echo "AR=$ar" >> config.mak
|
||||
echo "STRIP=$strip -s -R .comment -R .note" >> config.mak
|
||||
|
@@ -302,6 +302,9 @@ void page_unprotect_range(uint8_t *data, unsigned long data_size);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif /* SINGLE_CPU_DEFINES */
|
||||
|
||||
void cpu_abort(CPUState *env, const char *fmt, ...);
|
||||
extern CPUState *cpu_single_env;
|
||||
|
||||
#endif /* CPU_ALL_H */
|
||||
|
@@ -40,6 +40,8 @@ typedef struct CPUARMState {
|
||||
jmp_buf jmp_env;
|
||||
int exception_index;
|
||||
int interrupt_request;
|
||||
struct TranslationBlock *current_tb;
|
||||
int user_mode_only;
|
||||
|
||||
/* user data */
|
||||
void *opaque;
|
||||
|
343
cpu-exec.c
343
cpu-exec.c
@@ -140,146 +140,197 @@ int cpu_exec(CPUState *env1)
|
||||
#error unsupported target CPU
|
||||
#endif
|
||||
env->interrupt_request = 0;
|
||||
env->exception_index = -1;
|
||||
|
||||
/* prepare setjmp context for exception handling */
|
||||
if (setjmp(env->jmp_env) == 0) {
|
||||
T0 = 0; /* force lookup of first TB */
|
||||
for(;;) {
|
||||
#ifdef __sparc__
|
||||
/* g1 can be modified by some libc? functions */
|
||||
tmp_T0 = T0;
|
||||
#endif
|
||||
if (env->interrupt_request) {
|
||||
env->exception_index = EXCP_INTERRUPT;
|
||||
cpu_loop_exit();
|
||||
}
|
||||
#ifdef DEBUG_EXEC
|
||||
if (loglevel) {
|
||||
for(;;) {
|
||||
if (setjmp(env->jmp_env) == 0) {
|
||||
/* if an exception is pending, we execute it here */
|
||||
if (env->exception_index >= 0) {
|
||||
if (env->exception_index >= EXCP_INTERRUPT) {
|
||||
/* exit request from the cpu execution loop */
|
||||
ret = env->exception_index;
|
||||
break;
|
||||
} else if (env->user_mode_only) {
|
||||
/* if user mode only, we simulate a fake exception
|
||||
which will be hanlded outside the cpu execution
|
||||
loop */
|
||||
#if defined(TARGET_I386)
|
||||
/* restore flags in standard format */
|
||||
env->regs[R_EAX] = EAX;
|
||||
env->regs[R_EBX] = EBX;
|
||||
env->regs[R_ECX] = ECX;
|
||||
env->regs[R_EDX] = EDX;
|
||||
env->regs[R_ESI] = ESI;
|
||||
env->regs[R_EDI] = EDI;
|
||||
env->regs[R_EBP] = EBP;
|
||||
env->regs[R_ESP] = ESP;
|
||||
env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK);
|
||||
cpu_x86_dump_state(env, logfile, 0);
|
||||
env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
|
||||
do_interrupt_user(env->exception_index,
|
||||
env->exception_is_int,
|
||||
env->error_code,
|
||||
env->exception_next_eip);
|
||||
#endif
|
||||
ret = env->exception_index;
|
||||
break;
|
||||
} else {
|
||||
#if defined(TARGET_I386)
|
||||
/* simulate a real cpu exception. On i386, it can
|
||||
trigger new exceptions, but we do not handle
|
||||
double or triple faults yet. */
|
||||
do_interrupt(env->exception_index,
|
||||
env->exception_is_int,
|
||||
env->error_code,
|
||||
env->exception_next_eip);
|
||||
#endif
|
||||
}
|
||||
env->exception_index = -1;
|
||||
}
|
||||
#if defined(TARGET_I386)
|
||||
/* if hardware interrupt pending, we execute it */
|
||||
if (env->hard_interrupt_request &&
|
||||
(env->eflags & IF_MASK)) {
|
||||
int intno;
|
||||
intno = cpu_x86_get_pic_interrupt(env);
|
||||
if (loglevel) {
|
||||
fprintf(logfile, "Servicing hardware INT=0x%02x\n", intno);
|
||||
}
|
||||
do_interrupt(intno, 0, 0, 0);
|
||||
env->hard_interrupt_request = 0;
|
||||
}
|
||||
#endif
|
||||
T0 = 0; /* force lookup of first TB */
|
||||
for(;;) {
|
||||
#ifdef __sparc__
|
||||
/* g1 can be modified by some libc? functions */
|
||||
tmp_T0 = T0;
|
||||
#endif
|
||||
if (env->interrupt_request) {
|
||||
env->exception_index = EXCP_INTERRUPT;
|
||||
cpu_loop_exit();
|
||||
}
|
||||
#ifdef DEBUG_EXEC
|
||||
if (loglevel) {
|
||||
#if defined(TARGET_I386)
|
||||
/* restore flags in standard format */
|
||||
env->regs[R_EAX] = EAX;
|
||||
env->regs[R_EBX] = EBX;
|
||||
env->regs[R_ECX] = ECX;
|
||||
env->regs[R_EDX] = EDX;
|
||||
env->regs[R_ESI] = ESI;
|
||||
env->regs[R_EDI] = EDI;
|
||||
env->regs[R_EBP] = EBP;
|
||||
env->regs[R_ESP] = ESP;
|
||||
env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK);
|
||||
cpu_x86_dump_state(env, logfile, 0);
|
||||
env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
|
||||
#elif defined(TARGET_ARM)
|
||||
cpu_arm_dump_state(env, logfile, 0);
|
||||
cpu_arm_dump_state(env, logfile, 0);
|
||||
#else
|
||||
#error unsupported target CPU
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* we compute the CPU state. We assume it will not
|
||||
change during the whole generated block. */
|
||||
/* we compute the CPU state. We assume it will not
|
||||
change during the whole generated block. */
|
||||
#if defined(TARGET_I386)
|
||||
flags = env->seg_cache[R_CS].seg_32bit << GEN_FLAG_CODE32_SHIFT;
|
||||
flags |= env->seg_cache[R_SS].seg_32bit << GEN_FLAG_SS32_SHIFT;
|
||||
flags |= (((unsigned long)env->seg_cache[R_DS].base |
|
||||
(unsigned long)env->seg_cache[R_ES].base |
|
||||
(unsigned long)env->seg_cache[R_SS].base) != 0) <<
|
||||
GEN_FLAG_ADDSEG_SHIFT;
|
||||
if (!(env->eflags & VM_MASK)) {
|
||||
flags |= (env->segs[R_CS] & 3) << GEN_FLAG_CPL_SHIFT;
|
||||
} else {
|
||||
/* NOTE: a dummy CPL is kept */
|
||||
flags |= (1 << GEN_FLAG_VM_SHIFT);
|
||||
flags |= (3 << GEN_FLAG_CPL_SHIFT);
|
||||
}
|
||||
flags |= (env->eflags & (IOPL_MASK | TF_MASK));
|
||||
cs_base = env->seg_cache[R_CS].base;
|
||||
pc = cs_base + env->eip;
|
||||
flags = (env->segs[R_CS].flags & DESC_B_MASK)
|
||||
>> (DESC_B_SHIFT - GEN_FLAG_CODE32_SHIFT);
|
||||
flags |= (env->segs[R_SS].flags & DESC_B_MASK)
|
||||
>> (DESC_B_SHIFT - GEN_FLAG_SS32_SHIFT);
|
||||
flags |= (((unsigned long)env->segs[R_DS].base |
|
||||
(unsigned long)env->segs[R_ES].base |
|
||||
(unsigned long)env->segs[R_SS].base) != 0) <<
|
||||
GEN_FLAG_ADDSEG_SHIFT;
|
||||
if (!(env->eflags & VM_MASK)) {
|
||||
flags |= (env->segs[R_CS].selector & 3) << GEN_FLAG_CPL_SHIFT;
|
||||
} else {
|
||||
/* NOTE: a dummy CPL is kept */
|
||||
flags |= (1 << GEN_FLAG_VM_SHIFT);
|
||||
flags |= (3 << GEN_FLAG_CPL_SHIFT);
|
||||
}
|
||||
flags |= (env->eflags & (IOPL_MASK | TF_MASK));
|
||||
cs_base = env->segs[R_CS].base;
|
||||
pc = cs_base + env->eip;
|
||||
#elif defined(TARGET_ARM)
|
||||
flags = 0;
|
||||
cs_base = 0;
|
||||
pc = (uint8_t *)env->regs[15];
|
||||
flags = 0;
|
||||
cs_base = 0;
|
||||
pc = (uint8_t *)env->regs[15];
|
||||
#else
|
||||
#error unsupported CPU
|
||||
#endif
|
||||
tb = tb_find(&ptb, (unsigned long)pc, (unsigned long)cs_base,
|
||||
flags);
|
||||
if (!tb) {
|
||||
spin_lock(&tb_lock);
|
||||
/* if no translated code available, then translate it now */
|
||||
tb = tb_alloc((unsigned long)pc);
|
||||
tb = tb_find(&ptb, (unsigned long)pc, (unsigned long)cs_base,
|
||||
flags);
|
||||
if (!tb) {
|
||||
/* flush must be done */
|
||||
tb_flush();
|
||||
/* cannot fail at this point */
|
||||
spin_lock(&tb_lock);
|
||||
/* if no translated code available, then translate it now */
|
||||
tb = tb_alloc((unsigned long)pc);
|
||||
/* don't forget to invalidate previous TB info */
|
||||
ptb = &tb_hash[tb_hash_func((unsigned long)pc)];
|
||||
T0 = 0;
|
||||
}
|
||||
tc_ptr = code_gen_ptr;
|
||||
tb->tc_ptr = tc_ptr;
|
||||
tb->cs_base = (unsigned long)cs_base;
|
||||
tb->flags = flags;
|
||||
ret = cpu_gen_code(tb, CODE_GEN_MAX_SIZE, &code_gen_size);
|
||||
if (!tb) {
|
||||
/* flush must be done */
|
||||
tb_flush();
|
||||
/* cannot fail at this point */
|
||||
tb = tb_alloc((unsigned long)pc);
|
||||
/* don't forget to invalidate previous TB info */
|
||||
ptb = &tb_hash[tb_hash_func((unsigned long)pc)];
|
||||
T0 = 0;
|
||||
}
|
||||
tc_ptr = code_gen_ptr;
|
||||
tb->tc_ptr = tc_ptr;
|
||||
tb->cs_base = (unsigned long)cs_base;
|
||||
tb->flags = flags;
|
||||
ret = cpu_gen_code(tb, CODE_GEN_MAX_SIZE, &code_gen_size);
|
||||
#if defined(TARGET_I386)
|
||||
/* XXX: suppress that, this is incorrect */
|
||||
/* if invalid instruction, signal it */
|
||||
if (ret != 0) {
|
||||
/* NOTE: the tb is allocated but not linked, so we
|
||||
can leave it */
|
||||
spin_unlock(&tb_lock);
|
||||
raise_exception(EXCP06_ILLOP);
|
||||
}
|
||||
/* XXX: suppress that, this is incorrect */
|
||||
/* if invalid instruction, signal it */
|
||||
if (ret != 0) {
|
||||
/* NOTE: the tb is allocated but not linked, so we
|
||||
can leave it */
|
||||
spin_unlock(&tb_lock);
|
||||
raise_exception(EXCP06_ILLOP);
|
||||
}
|
||||
#endif
|
||||
*ptb = tb;
|
||||
tb->hash_next = NULL;
|
||||
tb_link(tb);
|
||||
code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
|
||||
spin_unlock(&tb_lock);
|
||||
}
|
||||
*ptb = tb;
|
||||
tb->hash_next = NULL;
|
||||
tb_link(tb);
|
||||
code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
|
||||
spin_unlock(&tb_lock);
|
||||
}
|
||||
#ifdef DEBUG_EXEC
|
||||
if (loglevel) {
|
||||
fprintf(logfile, "Trace 0x%08lx [0x%08lx] %s\n",
|
||||
(long)tb->tc_ptr, (long)tb->pc,
|
||||
lookup_symbol((void *)tb->pc));
|
||||
}
|
||||
if (loglevel) {
|
||||
fprintf(logfile, "Trace 0x%08lx [0x%08lx] %s\n",
|
||||
(long)tb->tc_ptr, (long)tb->pc,
|
||||
lookup_symbol((void *)tb->pc));
|
||||
}
|
||||
#endif
|
||||
#ifdef __sparc__
|
||||
T0 = tmp_T0;
|
||||
T0 = tmp_T0;
|
||||
#endif
|
||||
/* see if we can patch the calling TB. XXX: remove TF test */
|
||||
if (T0 != 0
|
||||
/* see if we can patch the calling TB. XXX: remove TF test */
|
||||
if (T0 != 0
|
||||
#if defined(TARGET_I386)
|
||||
&& !(env->eflags & TF_MASK)
|
||||
&& !(env->eflags & TF_MASK)
|
||||
#endif
|
||||
) {
|
||||
spin_lock(&tb_lock);
|
||||
tb_add_jump((TranslationBlock *)(T0 & ~3), T0 & 3, tb);
|
||||
spin_unlock(&tb_lock);
|
||||
}
|
||||
tc_ptr = tb->tc_ptr;
|
||||
|
||||
/* execute the generated code */
|
||||
gen_func = (void *)tc_ptr;
|
||||
) {
|
||||
spin_lock(&tb_lock);
|
||||
tb_add_jump((TranslationBlock *)(T0 & ~3), T0 & 3, tb);
|
||||
spin_unlock(&tb_lock);
|
||||
}
|
||||
tc_ptr = tb->tc_ptr;
|
||||
env->current_tb = tb;
|
||||
/* execute the generated code */
|
||||
gen_func = (void *)tc_ptr;
|
||||
#if defined(__sparc__)
|
||||
__asm__ __volatile__("call %0\n\t"
|
||||
"mov %%o7,%%i0"
|
||||
: /* no outputs */
|
||||
: "r" (gen_func)
|
||||
: "i0", "i1", "i2", "i3", "i4", "i5");
|
||||
__asm__ __volatile__("call %0\n\t"
|
||||
"mov %%o7,%%i0"
|
||||
: /* no outputs */
|
||||
: "r" (gen_func)
|
||||
: "i0", "i1", "i2", "i3", "i4", "i5");
|
||||
#elif defined(__arm__)
|
||||
asm volatile ("mov pc, %0\n\t"
|
||||
".global exec_loop\n\t"
|
||||
"exec_loop:\n\t"
|
||||
: /* no outputs */
|
||||
: "r" (gen_func)
|
||||
: "r1", "r2", "r3", "r8", "r9", "r10", "r12", "r14");
|
||||
asm volatile ("mov pc, %0\n\t"
|
||||
".global exec_loop\n\t"
|
||||
"exec_loop:\n\t"
|
||||
: /* no outputs */
|
||||
: "r" (gen_func)
|
||||
: "r1", "r2", "r3", "r8", "r9", "r10", "r12", "r14");
|
||||
#else
|
||||
gen_func();
|
||||
gen_func();
|
||||
#endif
|
||||
env->current_tb = NULL;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
ret = env->exception_index;
|
||||
} /* for(;;) */
|
||||
|
||||
|
||||
#if defined(TARGET_I386)
|
||||
/* restore flags in standard format */
|
||||
@@ -330,12 +381,6 @@ int cpu_exec(CPUState *env1)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cpu_interrupt(CPUState *s)
|
||||
{
|
||||
s->interrupt_request = 1;
|
||||
}
|
||||
|
||||
|
||||
#if defined(TARGET_I386)
|
||||
|
||||
void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
|
||||
@@ -347,13 +392,13 @@ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
|
||||
if (env->eflags & VM_MASK) {
|
||||
SegmentCache *sc;
|
||||
selector &= 0xffff;
|
||||
sc = &env->seg_cache[seg_reg];
|
||||
/* NOTE: in VM86 mode, limit and seg_32bit are never reloaded,
|
||||
sc = &env->segs[seg_reg];
|
||||
/* NOTE: in VM86 mode, limit and flags are never reloaded,
|
||||
so we must load them here */
|
||||
sc->base = (void *)(selector << 4);
|
||||
sc->limit = 0xffff;
|
||||
sc->seg_32bit = 0;
|
||||
env->segs[seg_reg] = selector;
|
||||
sc->flags = 0;
|
||||
sc->selector = selector;
|
||||
} else {
|
||||
load_seg(seg_reg, selector, 0);
|
||||
}
|
||||
@@ -398,6 +443,8 @@ void cpu_x86_frstor(CPUX86State *s, uint8_t *ptr, int data32)
|
||||
#include <signal.h>
|
||||
#include <sys/ucontext.h>
|
||||
|
||||
#if defined(TARGET_I386)
|
||||
|
||||
/* 'pc' is the host PC at which the exception was raised. 'address' is
|
||||
the effective address of the memory exception. 'is_write' is 1 if a
|
||||
write caused the exception and otherwise 0'. 'old_set' is the
|
||||
@@ -407,42 +454,52 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
int ret;
|
||||
uint32_t found_pc;
|
||||
|
||||
if (cpu_single_env)
|
||||
env = cpu_single_env; /* XXX: find a correct solution for multithread */
|
||||
#if defined(DEBUG_SIGNAL)
|
||||
printf("qemu: SIGSEGV pc=0x%08lx address=%08lx wr=%d oldset=0x%08lx\n",
|
||||
printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
|
||||
pc, address, is_write, *(unsigned long *)old_set);
|
||||
#endif
|
||||
/* XXX: locking issue */
|
||||
if (is_write && page_unprotect(address)) {
|
||||
return 1;
|
||||
}
|
||||
/* see if it is an MMU fault */
|
||||
ret = cpu_x86_handle_mmu_fault(env, address, is_write);
|
||||
if (ret < 0)
|
||||
return 0; /* not an MMU fault */
|
||||
if (ret == 0)
|
||||
return 1; /* the MMU fault was handled without causing real CPU fault */
|
||||
/* now we have a real cpu fault */
|
||||
tb = tb_find_pc(pc);
|
||||
if (tb) {
|
||||
/* the PC is inside the translated code. It means that we have
|
||||
a virtual CPU fault */
|
||||
ret = cpu_search_pc(tb, &found_pc, pc);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
#if defined(TARGET_I386)
|
||||
env->eip = found_pc - tb->cs_base;
|
||||
env->cr2 = address;
|
||||
/* we restore the process signal mask as the sigreturn should
|
||||
do it (XXX: use sigsetjmp) */
|
||||
sigprocmask(SIG_SETMASK, old_set, NULL);
|
||||
raise_exception_err(EXCP0E_PAGE, 4 | (is_write << 1));
|
||||
cpu_restore_state(tb, env, pc);
|
||||
}
|
||||
#if 0
|
||||
printf("PF exception: EIP=0x%08x CR2=0x%08x error=0x%x\n",
|
||||
env->eip, env->cr[2], env->error_code);
|
||||
#endif
|
||||
/* we restore the process signal mask as the sigreturn should
|
||||
do it (XXX: use sigsetjmp) */
|
||||
sigprocmask(SIG_SETMASK, old_set, NULL);
|
||||
raise_exception_err(EXCP0E_PAGE, env->error_code);
|
||||
/* never comes here */
|
||||
return 1;
|
||||
}
|
||||
|
||||
#elif defined(TARGET_ARM)
|
||||
env->regs[15] = found_pc;
|
||||
/* XXX: do more */
|
||||
static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
|
||||
int is_write, sigset_t *old_set)
|
||||
{
|
||||
/* XXX: do more */
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#error unsupported target CPU
|
||||
#endif
|
||||
/* never comes here */
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
@@ -570,6 +627,6 @@ int cpu_signal_handler(int host_signum, struct siginfo *info,
|
||||
|
||||
#else
|
||||
|
||||
#error CPU specific signal handler needed
|
||||
#error host CPU specific signal handler needed
|
||||
|
||||
#endif
|
||||
|
87
cpu-i386.h
87
cpu-i386.h
@@ -50,7 +50,8 @@
|
||||
|
||||
/* segment descriptor fields */
|
||||
#define DESC_G_MASK (1 << 23)
|
||||
#define DESC_B_MASK (1 << 22)
|
||||
#define DESC_B_SHIFT 22
|
||||
#define DESC_B_MASK (1 << DESC_B_SHIFT)
|
||||
#define DESC_AVL_MASK (1 << 20)
|
||||
#define DESC_P_MASK (1 << 15)
|
||||
#define DESC_DPL_SHIFT 13
|
||||
@@ -85,6 +86,45 @@
|
||||
#define VIP_MASK 0x00100000
|
||||
#define ID_MASK 0x00200000
|
||||
|
||||
#define CR0_PE_MASK (1 << 0)
|
||||
#define CR0_TS_MASK (1 << 3)
|
||||
#define CR0_WP_MASK (1 << 16)
|
||||
#define CR0_AM_MASK (1 << 18)
|
||||
#define CR0_PG_MASK (1 << 31)
|
||||
|
||||
#define CR4_VME_MASK (1 << 0)
|
||||
#define CR4_PVI_MASK (1 << 1)
|
||||
#define CR4_TSD_MASK (1 << 2)
|
||||
#define CR4_DE_MASK (1 << 3)
|
||||
#define CR4_PSE_MASK (1 << 4)
|
||||
|
||||
#define PG_PRESENT_BIT 0
|
||||
#define PG_RW_BIT 1
|
||||
#define PG_USER_BIT 2
|
||||
#define PG_PWT_BIT 3
|
||||
#define PG_PCD_BIT 4
|
||||
#define PG_ACCESSED_BIT 5
|
||||
#define PG_DIRTY_BIT 6
|
||||
#define PG_PSE_BIT 7
|
||||
#define PG_GLOBAL_BIT 8
|
||||
|
||||
#define PG_PRESENT_MASK (1 << PG_PRESENT_BIT)
|
||||
#define PG_RW_MASK (1 << PG_RW_BIT)
|
||||
#define PG_USER_MASK (1 << PG_USER_BIT)
|
||||
#define PG_PWT_MASK (1 << PG_PWT_BIT)
|
||||
#define PG_PCD_MASK (1 << PG_PCD_BIT)
|
||||
#define PG_ACCESSED_MASK (1 << PG_ACCESSED_BIT)
|
||||
#define PG_DIRTY_MASK (1 << PG_DIRTY_BIT)
|
||||
#define PG_PSE_MASK (1 << PG_PSE_BIT)
|
||||
#define PG_GLOBAL_MASK (1 << PG_GLOBAL_BIT)
|
||||
|
||||
#define PG_ERROR_W_BIT 1
|
||||
|
||||
#define PG_ERROR_P_MASK 0x01
|
||||
#define PG_ERROR_W_MASK (1 << PG_ERROR_W_BIT)
|
||||
#define PG_ERROR_U_MASK 0x04
|
||||
#define PG_ERROR_RSVD_MASK 0x08
|
||||
|
||||
#define EXCP00_DIVZ 0
|
||||
#define EXCP01_SSTP 1
|
||||
#define EXCP02_NMI 2
|
||||
@@ -105,6 +145,7 @@
|
||||
#define EXCP12_MCHK 18
|
||||
|
||||
#define EXCP_INTERRUPT 256 /* async interruption */
|
||||
#define EXCP_HLT 257 /* hlt instruction reached */
|
||||
|
||||
enum {
|
||||
CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */
|
||||
@@ -161,19 +202,12 @@ typedef double CPU86_LDouble;
|
||||
#endif
|
||||
|
||||
typedef struct SegmentCache {
|
||||
uint32_t selector;
|
||||
uint8_t *base;
|
||||
unsigned long limit;
|
||||
uint8_t seg_32bit;
|
||||
uint32_t limit;
|
||||
uint32_t flags;
|
||||
} SegmentCache;
|
||||
|
||||
typedef struct SegmentDescriptorTable {
|
||||
uint8_t *base;
|
||||
unsigned long limit;
|
||||
/* this is the returned base when reading the register, just to
|
||||
avoid that the emulated program modifies it */
|
||||
unsigned long emu_base;
|
||||
} SegmentDescriptorTable;
|
||||
|
||||
typedef struct CPUX86State {
|
||||
/* standard registers */
|
||||
uint32_t regs[8];
|
||||
@@ -205,19 +239,27 @@ typedef struct CPUX86State {
|
||||
} fp_convert;
|
||||
|
||||
/* segments */
|
||||
uint32_t segs[6]; /* selector values */
|
||||
SegmentCache seg_cache[6]; /* info taken from LDT/GDT */
|
||||
SegmentDescriptorTable gdt;
|
||||
SegmentDescriptorTable ldt;
|
||||
SegmentDescriptorTable idt;
|
||||
SegmentCache segs[6]; /* selector values */
|
||||
SegmentCache ldt;
|
||||
SegmentCache tr;
|
||||
SegmentCache gdt; /* only base and limit are used */
|
||||
SegmentCache idt; /* only base and limit are used */
|
||||
|
||||
/* exception/interrupt handling */
|
||||
jmp_buf jmp_env;
|
||||
int exception_index;
|
||||
int error_code;
|
||||
uint32_t cr2;
|
||||
int interrupt_request;
|
||||
|
||||
int exception_is_int;
|
||||
int exception_next_eip;
|
||||
struct TranslationBlock *current_tb; /* currently executing TB */
|
||||
uint32_t cr[5]; /* NOTE: cr1 is unused */
|
||||
uint32_t dr[8]; /* debug registers */
|
||||
int interrupt_request; /* if true, will exit from cpu_exec() ASAP */
|
||||
/* if true, will call cpu_x86_get_pic_interrupt() ASAP to get the
|
||||
request interrupt number */
|
||||
int hard_interrupt_request;
|
||||
int user_mode_only; /* user mode only simulation */
|
||||
|
||||
/* user data */
|
||||
void *opaque;
|
||||
} CPUX86State;
|
||||
@@ -235,6 +277,7 @@ CPUX86State *cpu_x86_init(void);
|
||||
int cpu_x86_exec(CPUX86State *s);
|
||||
void cpu_x86_interrupt(CPUX86State *s);
|
||||
void cpu_x86_close(CPUX86State *s);
|
||||
int cpu_x86_get_pic_interrupt(CPUX86State *s);
|
||||
|
||||
/* needed to load some predefinied segment registers */
|
||||
void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector);
|
||||
@@ -250,6 +293,12 @@ struct siginfo;
|
||||
int cpu_x86_signal_handler(int host_signum, struct siginfo *info,
|
||||
void *puc);
|
||||
|
||||
/* MMU defines */
|
||||
void cpu_x86_init_mmu(CPUX86State *env);
|
||||
extern int phys_ram_size;
|
||||
extern int phys_ram_fd;
|
||||
extern uint8_t *phys_ram_base;
|
||||
|
||||
/* used to debug */
|
||||
#define X86_DUMP_FPU 0x0001 /* dump FPU state too */
|
||||
#define X86_DUMP_CCOP 0x0002 /* dump qemu flag cache */
|
||||
|
7
dyngen.c
7
dyngen.c
@@ -654,7 +654,6 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
sym_name = strtab + sym->st_name;
|
||||
if (strstart(sym_name, "__op_label", &p)) {
|
||||
uint8_t *ptr;
|
||||
int addend;
|
||||
unsigned long offset;
|
||||
|
||||
/* test if the variable refers to a label inside
|
||||
@@ -663,7 +662,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
if (!ptr)
|
||||
error("__op_labelN in invalid section");
|
||||
offset = sym->st_value;
|
||||
addend = 0;
|
||||
val = *(target_ulong *)(ptr + offset);
|
||||
#ifdef ELF_USES_RELOCA
|
||||
{
|
||||
int reloc_shndx, nb_relocs1, j;
|
||||
@@ -676,7 +675,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
rel = (ELF_RELOC *)sdata[reloc_shndx];
|
||||
for(j = 0; j < nb_relocs1; j++) {
|
||||
if (rel->r_offset == offset) {
|
||||
addend = rel->r_addend;
|
||||
val = rel->r_addend;
|
||||
break;
|
||||
}
|
||||
rel++;
|
||||
@@ -684,8 +683,6 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
|
||||
}
|
||||
}
|
||||
#endif
|
||||
val = *(target_ulong *)(ptr + offset);
|
||||
val += addend;
|
||||
|
||||
if (val >= start_offset && val < start_offset + copy_size) {
|
||||
n = strtol(p, NULL, 10);
|
||||
|
34
exec-i386.h
34
exec-i386.h
@@ -123,8 +123,23 @@ typedef struct CCTable {
|
||||
extern CCTable cc_table[];
|
||||
|
||||
void load_seg(int seg_reg, int selector, unsigned cur_eip);
|
||||
void jmp_seg(int selector, unsigned int new_eip);
|
||||
void helper_iret_protected(int shift);
|
||||
void helper_lldt_T0(void);
|
||||
void helper_ltr_T0(void);
|
||||
void helper_movl_crN_T0(int reg);
|
||||
void helper_movl_drN_T0(int reg);
|
||||
void helper_invlpg(unsigned int addr);
|
||||
void cpu_x86_update_cr0(CPUX86State *env);
|
||||
void cpu_x86_update_cr3(CPUX86State *env);
|
||||
void cpu_x86_flush_tlb(CPUX86State *env, uint32_t addr);
|
||||
int cpu_x86_handle_mmu_fault(CPUX86State *env, uint32_t addr, int is_write);
|
||||
void __hidden cpu_lock(void);
|
||||
void __hidden cpu_unlock(void);
|
||||
void do_interrupt(int intno, int is_int, int error_code,
|
||||
unsigned int next_eip);
|
||||
void do_interrupt_user(int intno, int is_int, int error_code,
|
||||
unsigned int next_eip);
|
||||
void raise_interrupt(int intno, int is_int, int error_code,
|
||||
unsigned int next_eip);
|
||||
void raise_exception_err(int exception_index, int error_code);
|
||||
@@ -324,3 +339,22 @@ void helper_frstor(uint8_t *ptr, int data32);
|
||||
const uint8_t parity_table[256];
|
||||
const uint8_t rclw_table[32];
|
||||
const uint8_t rclb_table[32];
|
||||
|
||||
static inline uint32_t compute_eflags(void)
|
||||
{
|
||||
return env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK);
|
||||
}
|
||||
|
||||
#define FL_UPDATE_MASK32 (TF_MASK | AC_MASK | ID_MASK)
|
||||
|
||||
#define FL_UPDATE_CPL0_MASK (TF_MASK | IF_MASK | IOPL_MASK | NT_MASK | \
|
||||
RF_MASK | AC_MASK | ID_MASK)
|
||||
|
||||
/* NOTE: CC_OP must be modified manually to CC_OP_EFLAGS */
|
||||
static inline void load_eflags(int eflags, int update_mask)
|
||||
{
|
||||
CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
|
||||
DF = 1 - (2 * ((eflags >> 10) & 1));
|
||||
env->eflags = (env->eflags & ~update_mask) |
|
||||
(eflags & update_mask);
|
||||
}
|
||||
|
125
exec.c
125
exec.c
@@ -26,11 +26,17 @@
|
||||
#include <inttypes.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "config.h"
|
||||
#ifdef TARGET_I386
|
||||
#include "cpu-i386.h"
|
||||
#endif
|
||||
#ifdef TARGET_ARM
|
||||
#include "cpu-arm.h"
|
||||
#endif
|
||||
#include "exec.h"
|
||||
|
||||
//#define DEBUG_TB_INVALIDATE
|
||||
#define DEBUG_FLUSH
|
||||
//#define DEBUG_FLUSH
|
||||
|
||||
/* make various TB consistency checks */
|
||||
//#define DEBUG_TB_CHECK
|
||||
@@ -563,3 +569,120 @@ TranslationBlock *tb_find_pc(unsigned long tc_ptr)
|
||||
}
|
||||
return &tbs[m_max];
|
||||
}
|
||||
|
||||
static void tb_reset_jump_recursive(TranslationBlock *tb);
|
||||
|
||||
static inline void tb_reset_jump_recursive2(TranslationBlock *tb, int n)
|
||||
{
|
||||
TranslationBlock *tb1, *tb_next, **ptb;
|
||||
unsigned int n1;
|
||||
|
||||
tb1 = tb->jmp_next[n];
|
||||
if (tb1 != NULL) {
|
||||
/* find head of list */
|
||||
for(;;) {
|
||||
n1 = (long)tb1 & 3;
|
||||
tb1 = (TranslationBlock *)((long)tb1 & ~3);
|
||||
if (n1 == 2)
|
||||
break;
|
||||
tb1 = tb1->jmp_next[n1];
|
||||
}
|
||||
/* we are now sure now that tb jumps to tb1 */
|
||||
tb_next = tb1;
|
||||
|
||||
/* remove tb from the jmp_first list */
|
||||
ptb = &tb_next->jmp_first;
|
||||
for(;;) {
|
||||
tb1 = *ptb;
|
||||
n1 = (long)tb1 & 3;
|
||||
tb1 = (TranslationBlock *)((long)tb1 & ~3);
|
||||
if (n1 == n && tb1 == tb)
|
||||
break;
|
||||
ptb = &tb1->jmp_next[n1];
|
||||
}
|
||||
*ptb = tb->jmp_next[n];
|
||||
tb->jmp_next[n] = NULL;
|
||||
|
||||
/* suppress the jump to next tb in generated code */
|
||||
tb_reset_jump(tb, n);
|
||||
|
||||
/* suppress jumps in the tb on which we could have jump */
|
||||
tb_reset_jump_recursive(tb_next);
|
||||
}
|
||||
}
|
||||
|
||||
static void tb_reset_jump_recursive(TranslationBlock *tb)
|
||||
{
|
||||
tb_reset_jump_recursive2(tb, 0);
|
||||
tb_reset_jump_recursive2(tb, 1);
|
||||
}
|
||||
|
||||
void cpu_interrupt(CPUState *env)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
|
||||
env->interrupt_request = 1;
|
||||
/* if the cpu is currently executing code, we must unlink it and
|
||||
all the potentially executing TB */
|
||||
tb = env->current_tb;
|
||||
if (tb) {
|
||||
tb_reset_jump_recursive(tb);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void cpu_abort(CPUState *env, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "qemu: fatal: ");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
#ifdef TARGET_I386
|
||||
cpu_x86_dump_state(env, stderr, X86_DUMP_FPU | X86_DUMP_CCOP);
|
||||
#endif
|
||||
va_end(ap);
|
||||
abort();
|
||||
}
|
||||
|
||||
#ifdef TARGET_I386
|
||||
/* unmap all maped pages and flush all associated code */
|
||||
void page_unmap(void)
|
||||
{
|
||||
PageDesc *p, *pmap;
|
||||
unsigned long addr;
|
||||
int i, j, ret, j1;
|
||||
|
||||
for(i = 0; i < L1_SIZE; i++) {
|
||||
pmap = l1_map[i];
|
||||
if (pmap) {
|
||||
p = pmap;
|
||||
for(j = 0;j < L2_SIZE;) {
|
||||
if (p->flags & PAGE_VALID) {
|
||||
addr = (i << (32 - L1_BITS)) | (j << TARGET_PAGE_BITS);
|
||||
/* we try to find a range to make less syscalls */
|
||||
j1 = j;
|
||||
p++;
|
||||
j++;
|
||||
while (j < L2_SIZE && (p->flags & PAGE_VALID)) {
|
||||
p++;
|
||||
j++;
|
||||
}
|
||||
ret = munmap((void *)addr, (j - j1) << TARGET_PAGE_BITS);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Could not unmap page 0x%08lx\n", addr);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
p++;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
free(pmap);
|
||||
l1_map[i] = NULL;
|
||||
}
|
||||
}
|
||||
tb_flush();
|
||||
}
|
||||
#endif
|
||||
|
9
exec.h
9
exec.h
@@ -39,6 +39,7 @@ struct TranslationBlock;
|
||||
extern uint16_t gen_opc_buf[OPC_BUF_SIZE];
|
||||
extern uint32_t gen_opparam_buf[OPPARAM_BUF_SIZE];
|
||||
extern uint32_t gen_opc_pc[OPC_BUF_SIZE];
|
||||
extern uint8_t gen_opc_cc_op[OPC_BUF_SIZE];
|
||||
extern uint8_t gen_opc_instr_start[OPC_BUF_SIZE];
|
||||
|
||||
#if defined(TARGET_I386)
|
||||
@@ -57,14 +58,16 @@ extern uint8_t gen_opc_instr_start[OPC_BUF_SIZE];
|
||||
extern FILE *logfile;
|
||||
extern int loglevel;
|
||||
|
||||
int gen_intermediate_code(struct TranslationBlock *tb, int search_pc);
|
||||
int gen_intermediate_code(struct TranslationBlock *tb);
|
||||
int gen_intermediate_code_pc(struct TranslationBlock *tb);
|
||||
void dump_ops(const uint16_t *opc_buf, const uint32_t *opparam_buf);
|
||||
int cpu_gen_code(struct TranslationBlock *tb,
|
||||
int max_code_size, int *gen_code_size_ptr);
|
||||
int cpu_search_pc(struct TranslationBlock *tb,
|
||||
uint32_t *found_pc, unsigned long searched_pc);
|
||||
int cpu_restore_state(struct TranslationBlock *tb,
|
||||
CPUState *env, unsigned long searched_pc);
|
||||
void cpu_exec_init(void);
|
||||
int page_unprotect(unsigned long address);
|
||||
void page_unmap(void);
|
||||
|
||||
#define CODE_GEN_MAX_SIZE 65536
|
||||
#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */
|
||||
|
658
helper-i386.c
658
helper-i386.c
@@ -126,17 +126,74 @@ void cpu_loop_exit(void)
|
||||
longjmp(env->jmp_env, 1);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* full interrupt support (only useful for real CPU emulation, not
|
||||
finished) - I won't do it any time soon, finish it if you want ! */
|
||||
void raise_interrupt(int intno, int is_int, int error_code,
|
||||
unsigned int next_eip)
|
||||
static inline void get_ss_esp_from_tss(uint32_t *ss_ptr,
|
||||
uint32_t *esp_ptr, int dpl)
|
||||
{
|
||||
SegmentDescriptorTable *dt;
|
||||
uint8_t *ptr;
|
||||
int type, dpl, cpl;
|
||||
uint32_t e1, e2;
|
||||
int type, index, shift;
|
||||
|
||||
#if 0
|
||||
{
|
||||
int i;
|
||||
printf("TR: base=%p limit=%x\n", env->tr.base, env->tr.limit);
|
||||
for(i=0;i<env->tr.limit;i++) {
|
||||
printf("%02x ", env->tr.base[i]);
|
||||
if ((i & 7) == 7) printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!(env->tr.flags & DESC_P_MASK))
|
||||
cpu_abort(env, "invalid tss");
|
||||
type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
|
||||
if ((type & 7) != 1)
|
||||
cpu_abort(env, "invalid tss type");
|
||||
shift = type >> 3;
|
||||
index = (dpl * 4 + 2) << shift;
|
||||
if (index + (4 << shift) - 1 > env->tr.limit)
|
||||
raise_exception_err(EXCP0A_TSS, env->tr.selector & 0xfffc);
|
||||
if (shift == 0) {
|
||||
*esp_ptr = lduw(env->tr.base + index);
|
||||
*ss_ptr = lduw(env->tr.base + index + 2);
|
||||
} else {
|
||||
*esp_ptr = ldl(env->tr.base + index);
|
||||
*ss_ptr = lduw(env->tr.base + index + 4);
|
||||
}
|
||||
}
|
||||
|
||||
/* return non zero if error */
|
||||
static inline int load_segment(uint32_t *e1_ptr, uint32_t *e2_ptr,
|
||||
int selector)
|
||||
{
|
||||
SegmentCache *dt;
|
||||
int index;
|
||||
uint8_t *ptr;
|
||||
|
||||
if (selector & 0x4)
|
||||
dt = &env->ldt;
|
||||
else
|
||||
dt = &env->gdt;
|
||||
index = selector & ~7;
|
||||
if ((index + 7) > dt->limit)
|
||||
return -1;
|
||||
ptr = dt->base + index;
|
||||
*e1_ptr = ldl(ptr);
|
||||
*e2_ptr = ldl(ptr + 4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* protected mode interrupt */
|
||||
static void do_interrupt_protected(int intno, int is_int, int error_code,
|
||||
unsigned int next_eip)
|
||||
{
|
||||
SegmentCache *dt;
|
||||
uint8_t *ptr, *ssp;
|
||||
int type, dpl, cpl, selector, ss_dpl;
|
||||
int has_error_code, new_stack, shift;
|
||||
uint32_t e1, e2, offset, ss, esp, ss_e1, ss_e2, push_size;
|
||||
uint32_t old_cs, old_ss, old_esp, old_eip;
|
||||
|
||||
dt = &env->idt;
|
||||
if (intno * 8 + 7 > dt->limit)
|
||||
raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
|
||||
@@ -147,6 +204,8 @@ void raise_interrupt(int intno, int is_int, int error_code,
|
||||
type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
|
||||
switch(type) {
|
||||
case 5: /* task gate */
|
||||
cpu_abort(env, "task gate not supported");
|
||||
break;
|
||||
case 6: /* 286 interrupt gate */
|
||||
case 7: /* 286 trap gate */
|
||||
case 14: /* 386 interrupt gate */
|
||||
@@ -157,26 +216,196 @@ void raise_interrupt(int intno, int is_int, int error_code,
|
||||
break;
|
||||
}
|
||||
dpl = (e2 >> DESC_DPL_SHIFT) & 3;
|
||||
cpl = env->segs[R_CS] & 3;
|
||||
if (env->eflags & VM_MASK)
|
||||
cpl = 3;
|
||||
else
|
||||
cpl = env->segs[R_CS].selector & 3;
|
||||
/* check privledge if software int */
|
||||
if (is_int && dpl < cpl)
|
||||
raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
|
||||
/* check valid bit */
|
||||
if (!(e2 & DESC_P_MASK))
|
||||
raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2);
|
||||
selector = e1 >> 16;
|
||||
offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff);
|
||||
if ((selector & 0xfffc) == 0)
|
||||
raise_exception_err(EXCP0D_GPF, 0);
|
||||
|
||||
if (load_segment(&e1, &e2, selector) != 0)
|
||||
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
|
||||
if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
|
||||
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
|
||||
dpl = (e2 >> DESC_DPL_SHIFT) & 3;
|
||||
if (dpl > cpl)
|
||||
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
|
||||
if (!(e2 & DESC_P_MASK))
|
||||
raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
|
||||
if (!(e2 & DESC_C_MASK) && dpl < cpl) {
|
||||
/* to inner priviledge */
|
||||
get_ss_esp_from_tss(&ss, &esp, dpl);
|
||||
if ((ss & 0xfffc) == 0)
|
||||
raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
|
||||
if ((ss & 3) != dpl)
|
||||
raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
|
||||
if (load_segment(&ss_e1, &ss_e2, ss) != 0)
|
||||
raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
|
||||
ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
|
||||
if (ss_dpl != dpl)
|
||||
raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
|
||||
if (!(ss_e2 & DESC_S_MASK) ||
|
||||
(ss_e2 & DESC_CS_MASK) ||
|
||||
!(ss_e2 & DESC_W_MASK))
|
||||
raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
|
||||
if (!(ss_e2 & DESC_P_MASK))
|
||||
raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
|
||||
new_stack = 1;
|
||||
} else if ((e2 & DESC_C_MASK) || dpl == cpl) {
|
||||
/* to same priviledge */
|
||||
new_stack = 0;
|
||||
} else {
|
||||
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
|
||||
new_stack = 0; /* avoid warning */
|
||||
}
|
||||
|
||||
shift = type >> 3;
|
||||
has_error_code = 0;
|
||||
if (!is_int) {
|
||||
switch(intno) {
|
||||
case 8:
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
case 17:
|
||||
has_error_code = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
push_size = 6 + (new_stack << 2) + (has_error_code << 1);
|
||||
if (env->eflags & VM_MASK)
|
||||
push_size += 8;
|
||||
push_size <<= shift;
|
||||
|
||||
/* XXX: check that enough room is available */
|
||||
if (new_stack) {
|
||||
old_esp = env->regs[R_ESP];
|
||||
old_ss = env->segs[R_SS].selector;
|
||||
load_seg(R_SS, ss, env->eip);
|
||||
} else {
|
||||
old_esp = 0;
|
||||
old_ss = 0;
|
||||
esp = env->regs[R_ESP];
|
||||
}
|
||||
if (is_int)
|
||||
old_eip = next_eip;
|
||||
else
|
||||
old_eip = env->eip;
|
||||
old_cs = env->segs[R_CS].selector;
|
||||
load_seg(R_CS, selector, env->eip);
|
||||
env->eip = offset;
|
||||
env->regs[R_ESP] = esp - push_size;
|
||||
ssp = env->segs[R_SS].base + esp;
|
||||
if (shift == 1) {
|
||||
int old_eflags;
|
||||
if (env->eflags & VM_MASK) {
|
||||
ssp -= 4;
|
||||
stl(ssp, env->segs[R_GS].selector);
|
||||
ssp -= 4;
|
||||
stl(ssp, env->segs[R_FS].selector);
|
||||
ssp -= 4;
|
||||
stl(ssp, env->segs[R_DS].selector);
|
||||
ssp -= 4;
|
||||
stl(ssp, env->segs[R_ES].selector);
|
||||
}
|
||||
if (new_stack) {
|
||||
ssp -= 4;
|
||||
stl(ssp, old_ss);
|
||||
ssp -= 4;
|
||||
stl(ssp, old_esp);
|
||||
}
|
||||
ssp -= 4;
|
||||
old_eflags = compute_eflags();
|
||||
stl(ssp, old_eflags);
|
||||
ssp -= 4;
|
||||
stl(ssp, old_cs);
|
||||
ssp -= 4;
|
||||
stl(ssp, old_eip);
|
||||
if (has_error_code) {
|
||||
ssp -= 4;
|
||||
stl(ssp, error_code);
|
||||
}
|
||||
} else {
|
||||
if (new_stack) {
|
||||
ssp -= 2;
|
||||
stw(ssp, old_ss);
|
||||
ssp -= 2;
|
||||
stw(ssp, old_esp);
|
||||
}
|
||||
ssp -= 2;
|
||||
stw(ssp, compute_eflags());
|
||||
ssp -= 2;
|
||||
stw(ssp, old_cs);
|
||||
ssp -= 2;
|
||||
stw(ssp, old_eip);
|
||||
if (has_error_code) {
|
||||
ssp -= 2;
|
||||
stw(ssp, error_code);
|
||||
}
|
||||
}
|
||||
|
||||
/* interrupt gate clear IF mask */
|
||||
if ((type & 1) == 0) {
|
||||
env->eflags &= ~IF_MASK;
|
||||
}
|
||||
env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* is_int is TRUE if coming from the int instruction. next_eip is the
|
||||
* EIP value AFTER the interrupt instruction. It is only relevant if
|
||||
* is_int is TRUE.
|
||||
*/
|
||||
void raise_interrupt(int intno, int is_int, int error_code,
|
||||
unsigned int next_eip)
|
||||
/* real mode interrupt */
|
||||
static void do_interrupt_real(int intno, int is_int, int error_code,
|
||||
unsigned int next_eip)
|
||||
{
|
||||
SegmentDescriptorTable *dt;
|
||||
SegmentCache *dt;
|
||||
uint8_t *ptr, *ssp;
|
||||
int selector;
|
||||
uint32_t offset, esp;
|
||||
uint32_t old_cs, old_eip;
|
||||
|
||||
/* real mode (simpler !) */
|
||||
dt = &env->idt;
|
||||
if (intno * 4 + 3 > dt->limit)
|
||||
raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
|
||||
ptr = dt->base + intno * 4;
|
||||
offset = lduw(ptr);
|
||||
selector = lduw(ptr + 2);
|
||||
esp = env->regs[R_ESP] & 0xffff;
|
||||
ssp = env->segs[R_SS].base + esp;
|
||||
if (is_int)
|
||||
old_eip = next_eip;
|
||||
else
|
||||
old_eip = env->eip;
|
||||
old_cs = env->segs[R_CS].selector;
|
||||
ssp -= 2;
|
||||
stw(ssp, compute_eflags());
|
||||
ssp -= 2;
|
||||
stw(ssp, old_cs);
|
||||
ssp -= 2;
|
||||
stw(ssp, old_eip);
|
||||
esp -= 6;
|
||||
|
||||
/* update processor state */
|
||||
env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) | (esp & 0xffff);
|
||||
env->eip = offset;
|
||||
env->segs[R_CS].selector = selector;
|
||||
env->segs[R_CS].base = (uint8_t *)(selector << 4);
|
||||
env->eflags &= ~(IF_MASK | TF_MASK | AC_MASK | RF_MASK);
|
||||
}
|
||||
|
||||
/* fake user mode interrupt */
|
||||
void do_interrupt_user(int intno, int is_int, int error_code,
|
||||
unsigned int next_eip)
|
||||
{
|
||||
SegmentCache *dt;
|
||||
uint8_t *ptr;
|
||||
int dpl, cpl;
|
||||
uint32_t e2;
|
||||
@@ -196,13 +425,38 @@ void raise_interrupt(int intno, int is_int, int error_code,
|
||||
code */
|
||||
if (is_int)
|
||||
EIP = next_eip;
|
||||
env->exception_index = intno;
|
||||
env->error_code = error_code;
|
||||
|
||||
cpu_loop_exit();
|
||||
}
|
||||
|
||||
#endif
|
||||
/*
|
||||
* Begin excution of an interruption. is_int is TRUE if coming from
|
||||
* the int instruction. next_eip is the EIP value AFTER the interrupt
|
||||
* instruction. It is only relevant if is_int is TRUE.
|
||||
*/
|
||||
void do_interrupt(int intno, int is_int, int error_code,
|
||||
unsigned int next_eip)
|
||||
{
|
||||
if (env->cr[0] & CR0_PE_MASK) {
|
||||
do_interrupt_protected(intno, is_int, error_code, next_eip);
|
||||
} else {
|
||||
do_interrupt_real(intno, is_int, error_code, next_eip);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Signal an interruption. It is executed in the main CPU loop.
|
||||
* is_int is TRUE if coming from the int instruction. next_eip is the
|
||||
* EIP value AFTER the interrupt instruction. It is only relevant if
|
||||
* is_int is TRUE.
|
||||
*/
|
||||
void raise_interrupt(int intno, int is_int, int error_code,
|
||||
unsigned int next_eip)
|
||||
{
|
||||
env->exception_index = intno;
|
||||
env->error_code = error_code;
|
||||
env->exception_is_int = is_int;
|
||||
env->exception_next_eip = next_eip;
|
||||
cpu_loop_exit();
|
||||
}
|
||||
|
||||
/* shortcuts to generate exceptions */
|
||||
void raise_exception_err(int exception_index, int error_code)
|
||||
@@ -321,50 +575,129 @@ void helper_cpuid(void)
|
||||
ECX = 0x6c65746e;
|
||||
EDX = 0x49656e69;
|
||||
} else if (EAX == 1) {
|
||||
int family, model, stepping;
|
||||
/* EAX = 1 info */
|
||||
EAX = 0x52b;
|
||||
#if 0
|
||||
/* pentium 75-200 */
|
||||
family = 5;
|
||||
model = 2;
|
||||
stepping = 11;
|
||||
#else
|
||||
/* pentium pro */
|
||||
family = 6;
|
||||
model = 1;
|
||||
stepping = 3;
|
||||
#endif
|
||||
EAX = (family << 8) | (model << 4) | stepping;
|
||||
EBX = 0;
|
||||
ECX = 0;
|
||||
EDX = CPUID_FP87 | CPUID_DE | CPUID_PSE |
|
||||
CPUID_TSC | CPUID_MSR | CPUID_MCE |
|
||||
CPUID_CX8;
|
||||
CPUID_CX8 | CPUID_PGE | CPUID_CMOV;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void load_seg_cache(SegmentCache *sc, uint32_t e1, uint32_t e2)
|
||||
{
|
||||
sc->base = (void *)((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000));
|
||||
sc->limit = (e1 & 0xffff) | (e2 & 0x000f0000);
|
||||
if (e2 & DESC_G_MASK)
|
||||
sc->limit = (sc->limit << 12) | 0xfff;
|
||||
sc->flags = e2;
|
||||
}
|
||||
|
||||
void helper_lldt_T0(void)
|
||||
{
|
||||
int selector;
|
||||
SegmentCache *dt;
|
||||
uint32_t e1, e2;
|
||||
int index;
|
||||
uint8_t *ptr;
|
||||
|
||||
selector = T0 & 0xffff;
|
||||
if ((selector & 0xfffc) == 0) {
|
||||
/* XXX: NULL selector case: invalid LDT */
|
||||
env->ldt.base = NULL;
|
||||
env->ldt.limit = 0;
|
||||
} else {
|
||||
if (selector & 0x4)
|
||||
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
|
||||
dt = &env->gdt;
|
||||
index = selector & ~7;
|
||||
if ((index + 7) > dt->limit)
|
||||
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
|
||||
ptr = dt->base + index;
|
||||
e1 = ldl(ptr);
|
||||
e2 = ldl(ptr + 4);
|
||||
if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2)
|
||||
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
|
||||
if (!(e2 & DESC_P_MASK))
|
||||
raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
|
||||
load_seg_cache(&env->ldt, e1, e2);
|
||||
}
|
||||
env->ldt.selector = selector;
|
||||
}
|
||||
|
||||
void helper_ltr_T0(void)
|
||||
{
|
||||
int selector;
|
||||
SegmentCache *dt;
|
||||
uint32_t e1, e2;
|
||||
int index, type;
|
||||
uint8_t *ptr;
|
||||
|
||||
selector = T0 & 0xffff;
|
||||
if ((selector & 0xfffc) == 0) {
|
||||
/* NULL selector case: invalid LDT */
|
||||
env->tr.base = NULL;
|
||||
env->tr.limit = 0;
|
||||
env->tr.flags = 0;
|
||||
} else {
|
||||
if (selector & 0x4)
|
||||
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
|
||||
dt = &env->gdt;
|
||||
index = selector & ~7;
|
||||
if ((index + 7) > dt->limit)
|
||||
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
|
||||
ptr = dt->base + index;
|
||||
e1 = ldl(ptr);
|
||||
e2 = ldl(ptr + 4);
|
||||
type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
|
||||
if ((e2 & DESC_S_MASK) ||
|
||||
(type != 2 && type != 9))
|
||||
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
|
||||
if (!(e2 & DESC_P_MASK))
|
||||
raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
|
||||
load_seg_cache(&env->tr, e1, e2);
|
||||
e2 |= 0x00000200; /* set the busy bit */
|
||||
stl(ptr + 4, e2);
|
||||
}
|
||||
env->tr.selector = selector;
|
||||
}
|
||||
|
||||
/* only works if protected mode and not VM86 */
|
||||
void load_seg(int seg_reg, int selector, unsigned cur_eip)
|
||||
void load_seg(int seg_reg, int selector, unsigned int cur_eip)
|
||||
{
|
||||
SegmentCache *sc;
|
||||
SegmentDescriptorTable *dt;
|
||||
int index;
|
||||
uint32_t e1, e2;
|
||||
uint8_t *ptr;
|
||||
|
||||
sc = &env->seg_cache[seg_reg];
|
||||
|
||||
sc = &env->segs[seg_reg];
|
||||
if ((selector & 0xfffc) == 0) {
|
||||
/* null selector case */
|
||||
if (seg_reg == R_SS) {
|
||||
EIP = cur_eip;
|
||||
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
|
||||
raise_exception_err(EXCP0D_GPF, 0);
|
||||
} else {
|
||||
/* XXX: each access should trigger an exception */
|
||||
sc->base = NULL;
|
||||
sc->limit = 0;
|
||||
sc->seg_32bit = 1;
|
||||
sc->flags = 0;
|
||||
}
|
||||
} else {
|
||||
if (selector & 0x4)
|
||||
dt = &env->ldt;
|
||||
else
|
||||
dt = &env->gdt;
|
||||
index = selector & ~7;
|
||||
if ((index + 7) > dt->limit) {
|
||||
if (load_segment(&e1, &e2, selector) != 0) {
|
||||
EIP = cur_eip;
|
||||
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
|
||||
}
|
||||
ptr = dt->base + index;
|
||||
e1 = ldl(ptr);
|
||||
e2 = ldl(ptr + 4);
|
||||
if (!(e2 & DESC_S_MASK) ||
|
||||
(e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK) {
|
||||
EIP = cur_eip;
|
||||
@@ -390,18 +723,210 @@ void load_seg(int seg_reg, int selector, unsigned cur_eip)
|
||||
else
|
||||
raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
|
||||
}
|
||||
|
||||
sc->base = (void *)((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000));
|
||||
sc->limit = (e1 & 0xffff) | (e2 & 0x000f0000);
|
||||
if (e2 & (1 << 23))
|
||||
sc->limit = (sc->limit << 12) | 0xfff;
|
||||
sc->seg_32bit = (e2 >> 22) & 1;
|
||||
load_seg_cache(sc, e1, e2);
|
||||
#if 0
|
||||
fprintf(logfile, "load_seg: sel=0x%04x base=0x%08lx limit=0x%08lx seg_32bit=%d\n",
|
||||
selector, (unsigned long)sc->base, sc->limit, sc->seg_32bit);
|
||||
fprintf(logfile, "load_seg: sel=0x%04x base=0x%08lx limit=0x%08lx flags=%08x\n",
|
||||
selector, (unsigned long)sc->base, sc->limit, sc->flags);
|
||||
#endif
|
||||
}
|
||||
env->segs[seg_reg] = selector;
|
||||
sc->selector = selector;
|
||||
}
|
||||
|
||||
/* protected mode jump */
|
||||
void jmp_seg(int selector, unsigned int new_eip)
|
||||
{
|
||||
SegmentCache sc1;
|
||||
uint32_t e1, e2, cpl, dpl, rpl;
|
||||
|
||||
if ((selector & 0xfffc) == 0) {
|
||||
raise_exception_err(EXCP0D_GPF, 0);
|
||||
}
|
||||
|
||||
if (load_segment(&e1, &e2, selector) != 0)
|
||||
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
|
||||
cpl = env->segs[R_CS].selector & 3;
|
||||
if (e2 & DESC_S_MASK) {
|
||||
if (!(e2 & DESC_CS_MASK))
|
||||
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
|
||||
dpl = (e2 >> DESC_DPL_SHIFT) & 3;
|
||||
if (e2 & DESC_CS_MASK) {
|
||||
/* conforming code segment */
|
||||
if (dpl > cpl)
|
||||
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
|
||||
} else {
|
||||
/* non conforming code segment */
|
||||
rpl = selector & 3;
|
||||
if (rpl > cpl)
|
||||
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
|
||||
if (dpl != cpl)
|
||||
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
|
||||
}
|
||||
if (!(e2 & DESC_P_MASK))
|
||||
raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
|
||||
load_seg_cache(&sc1, e1, e2);
|
||||
if (new_eip > sc1.limit)
|
||||
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
|
||||
env->segs[R_CS].base = sc1.base;
|
||||
env->segs[R_CS].limit = sc1.limit;
|
||||
env->segs[R_CS].flags = sc1.flags;
|
||||
env->segs[R_CS].selector = (selector & 0xfffc) | cpl;
|
||||
EIP = new_eip;
|
||||
} else {
|
||||
cpu_abort(env, "jmp to call/task gate not supported 0x%04x:0x%08x",
|
||||
selector, new_eip);
|
||||
}
|
||||
}
|
||||
|
||||
/* init the segment cache in vm86 mode */
|
||||
static inline void load_seg_vm(int seg, int selector)
|
||||
{
|
||||
SegmentCache *sc = &env->segs[seg];
|
||||
selector &= 0xffff;
|
||||
sc->base = (uint8_t *)(selector << 4);
|
||||
sc->selector = selector;
|
||||
sc->flags = 0;
|
||||
sc->limit = 0xffff;
|
||||
}
|
||||
|
||||
/* protected mode iret */
|
||||
void helper_iret_protected(int shift)
|
||||
{
|
||||
uint32_t sp, new_cs, new_eip, new_eflags, new_esp, new_ss;
|
||||
uint32_t new_es, new_ds, new_fs, new_gs;
|
||||
uint32_t e1, e2;
|
||||
int cpl, dpl, rpl, eflags_mask;
|
||||
uint8_t *ssp;
|
||||
|
||||
sp = env->regs[R_ESP];
|
||||
if (!(env->segs[R_SS].flags & DESC_B_MASK))
|
||||
sp &= 0xffff;
|
||||
ssp = env->segs[R_SS].base + sp;
|
||||
if (shift == 1) {
|
||||
/* 32 bits */
|
||||
new_eflags = ldl(ssp + 8);
|
||||
new_cs = ldl(ssp + 4) & 0xffff;
|
||||
new_eip = ldl(ssp);
|
||||
if (new_eflags & VM_MASK)
|
||||
goto return_to_vm86;
|
||||
} else {
|
||||
/* 16 bits */
|
||||
new_eflags = lduw(ssp + 4);
|
||||
new_cs = lduw(ssp + 2);
|
||||
new_eip = lduw(ssp);
|
||||
}
|
||||
if ((new_cs & 0xfffc) == 0)
|
||||
raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
|
||||
if (load_segment(&e1, &e2, new_cs) != 0)
|
||||
raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
|
||||
if (!(e2 & DESC_S_MASK) ||
|
||||
!(e2 & DESC_CS_MASK))
|
||||
raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
|
||||
cpl = env->segs[R_CS].selector & 3;
|
||||
rpl = new_cs & 3;
|
||||
if (rpl < cpl)
|
||||
raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
|
||||
dpl = (e2 >> DESC_DPL_SHIFT) & 3;
|
||||
if (e2 & DESC_CS_MASK) {
|
||||
if (dpl > rpl)
|
||||
raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
|
||||
} else {
|
||||
if (dpl != rpl)
|
||||
raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
|
||||
}
|
||||
if (!(e2 & DESC_P_MASK))
|
||||
raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
|
||||
|
||||
if (rpl == cpl) {
|
||||
/* return to same priledge level */
|
||||
load_seg(R_CS, new_cs, env->eip);
|
||||
new_esp = sp + (6 << shift);
|
||||
} else {
|
||||
/* return to differentr priviledge level */
|
||||
if (shift == 1) {
|
||||
/* 32 bits */
|
||||
new_esp = ldl(ssp + 12);
|
||||
new_ss = ldl(ssp + 16) & 0xffff;
|
||||
} else {
|
||||
/* 16 bits */
|
||||
new_esp = lduw(ssp + 6);
|
||||
new_ss = lduw(ssp + 8);
|
||||
}
|
||||
|
||||
if ((new_ss & 3) != rpl)
|
||||
raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
|
||||
if (load_segment(&e1, &e2, new_ss) != 0)
|
||||
raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
|
||||
if (!(e2 & DESC_S_MASK) ||
|
||||
(e2 & DESC_CS_MASK) ||
|
||||
!(e2 & DESC_W_MASK))
|
||||
raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
|
||||
dpl = (e2 >> DESC_DPL_SHIFT) & 3;
|
||||
if (dpl != rpl)
|
||||
raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
|
||||
if (!(e2 & DESC_P_MASK))
|
||||
raise_exception_err(EXCP0B_NOSEG, new_ss & 0xfffc);
|
||||
|
||||
load_seg(R_CS, new_cs, env->eip);
|
||||
load_seg(R_SS, new_ss, env->eip);
|
||||
}
|
||||
if (env->segs[R_SS].flags & DESC_B_MASK)
|
||||
env->regs[R_ESP] = new_esp;
|
||||
else
|
||||
env->regs[R_ESP] = (env->regs[R_ESP] & 0xffff0000) |
|
||||
(new_esp & 0xffff);
|
||||
env->eip = new_eip;
|
||||
if (cpl == 0)
|
||||
eflags_mask = FL_UPDATE_CPL0_MASK;
|
||||
else
|
||||
eflags_mask = FL_UPDATE_MASK32;
|
||||
if (shift == 0)
|
||||
eflags_mask &= 0xffff;
|
||||
load_eflags(new_eflags, eflags_mask);
|
||||
return;
|
||||
|
||||
return_to_vm86:
|
||||
new_esp = ldl(ssp + 12);
|
||||
new_ss = ldl(ssp + 16);
|
||||
new_es = ldl(ssp + 20);
|
||||
new_ds = ldl(ssp + 24);
|
||||
new_fs = ldl(ssp + 28);
|
||||
new_gs = ldl(ssp + 32);
|
||||
|
||||
/* modify processor state */
|
||||
load_eflags(new_eflags, FL_UPDATE_CPL0_MASK | VM_MASK | VIF_MASK | VIP_MASK);
|
||||
load_seg_vm(R_CS, new_cs);
|
||||
load_seg_vm(R_SS, new_ss);
|
||||
load_seg_vm(R_ES, new_es);
|
||||
load_seg_vm(R_DS, new_ds);
|
||||
load_seg_vm(R_FS, new_fs);
|
||||
load_seg_vm(R_GS, new_gs);
|
||||
|
||||
env->eip = new_eip;
|
||||
env->regs[R_ESP] = new_esp;
|
||||
}
|
||||
|
||||
void helper_movl_crN_T0(int reg)
|
||||
{
|
||||
env->cr[reg] = T0;
|
||||
switch(reg) {
|
||||
case 0:
|
||||
cpu_x86_update_cr0(env);
|
||||
break;
|
||||
case 3:
|
||||
cpu_x86_update_cr3(env);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX: do more */
|
||||
void helper_movl_drN_T0(int reg)
|
||||
{
|
||||
env->dr[reg] = T0;
|
||||
}
|
||||
|
||||
void helper_invlpg(unsigned int addr)
|
||||
{
|
||||
cpu_x86_flush_tlb(env, addr);
|
||||
}
|
||||
|
||||
/* rdtsc */
|
||||
@@ -425,23 +950,12 @@ void helper_rdtsc(void)
|
||||
void helper_lsl(void)
|
||||
{
|
||||
unsigned int selector, limit;
|
||||
SegmentDescriptorTable *dt;
|
||||
int index;
|
||||
uint32_t e1, e2;
|
||||
uint8_t *ptr;
|
||||
|
||||
CC_SRC = cc_table[CC_OP].compute_all() & ~CC_Z;
|
||||
selector = T0 & 0xffff;
|
||||
if (selector & 0x4)
|
||||
dt = &env->ldt;
|
||||
else
|
||||
dt = &env->gdt;
|
||||
index = selector & ~7;
|
||||
if ((index + 7) > dt->limit)
|
||||
if (load_segment(&e1, &e2, selector) != 0)
|
||||
return;
|
||||
ptr = dt->base + index;
|
||||
e1 = ldl(ptr);
|
||||
e2 = ldl(ptr + 4);
|
||||
limit = (e1 & 0xffff) | (e2 & 0x000f0000);
|
||||
if (e2 & (1 << 23))
|
||||
limit = (limit << 12) | 0xfff;
|
||||
@@ -452,22 +966,12 @@ void helper_lsl(void)
|
||||
void helper_lar(void)
|
||||
{
|
||||
unsigned int selector;
|
||||
SegmentDescriptorTable *dt;
|
||||
int index;
|
||||
uint32_t e2;
|
||||
uint8_t *ptr;
|
||||
uint32_t e1, e2;
|
||||
|
||||
CC_SRC = cc_table[CC_OP].compute_all() & ~CC_Z;
|
||||
selector = T0 & 0xffff;
|
||||
if (selector & 0x4)
|
||||
dt = &env->ldt;
|
||||
else
|
||||
dt = &env->gdt;
|
||||
index = selector & ~7;
|
||||
if ((index + 7) > dt->limit)
|
||||
if (load_segment(&e1, &e2, selector) != 0)
|
||||
return;
|
||||
ptr = dt->base + index;
|
||||
e2 = ldl(ptr + 4);
|
||||
T1 = e2 & 0x00f0ff00;
|
||||
CC_SRC |= CC_Z;
|
||||
}
|
||||
|
140
i386-vl.ld
Normal file
140
i386-vl.ld
Normal file
@@ -0,0 +1,140 @@
|
||||
/* ld script to make i386 Linux kernel
|
||||
* Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
|
||||
*/
|
||||
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
|
||||
OUTPUT_ARCH(i386)
|
||||
SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib);
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
/* Read-only sections, merged into text segment: */
|
||||
. = 0xa0000000 + SIZEOF_HEADERS;
|
||||
.interp : { *(.interp) }
|
||||
.hash : { *(.hash) }
|
||||
.dynsym : { *(.dynsym) }
|
||||
.dynstr : { *(.dynstr) }
|
||||
.gnu.version : { *(.gnu.version) }
|
||||
.gnu.version_d : { *(.gnu.version_d) }
|
||||
.gnu.version_r : { *(.gnu.version_r) }
|
||||
.rel.text :
|
||||
{ *(.rel.text) *(.rel.gnu.linkonce.t*) }
|
||||
.rela.text :
|
||||
{ *(.rela.text) *(.rela.gnu.linkonce.t*) }
|
||||
.rel.data :
|
||||
{ *(.rel.data) *(.rel.gnu.linkonce.d*) }
|
||||
.rela.data :
|
||||
{ *(.rela.data) *(.rela.gnu.linkonce.d*) }
|
||||
.rel.rodata :
|
||||
{ *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
|
||||
.rela.rodata :
|
||||
{ *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
|
||||
.rel.got : { *(.rel.got) }
|
||||
.rela.got : { *(.rela.got) }
|
||||
.rel.ctors : { *(.rel.ctors) }
|
||||
.rela.ctors : { *(.rela.ctors) }
|
||||
.rel.dtors : { *(.rel.dtors) }
|
||||
.rela.dtors : { *(.rela.dtors) }
|
||||
.rel.init : { *(.rel.init) }
|
||||
.rela.init : { *(.rela.init) }
|
||||
.rel.fini : { *(.rel.fini) }
|
||||
.rela.fini : { *(.rela.fini) }
|
||||
.rel.bss : { *(.rel.bss) }
|
||||
.rela.bss : { *(.rela.bss) }
|
||||
.rel.plt : { *(.rel.plt) }
|
||||
.rela.plt : { *(.rela.plt) }
|
||||
.init : { *(.init) } =0x47ff041f
|
||||
.text :
|
||||
{
|
||||
*(.text)
|
||||
/* .gnu.warning sections are handled specially by elf32.em. */
|
||||
*(.gnu.warning)
|
||||
*(.gnu.linkonce.t*)
|
||||
} =0x47ff041f
|
||||
_etext = .;
|
||||
PROVIDE (etext = .);
|
||||
.fini : { *(.fini) } =0x47ff041f
|
||||
.rodata : { *(.rodata) *(.gnu.linkonce.r*) }
|
||||
.rodata1 : { *(.rodata1) }
|
||||
.reginfo : { *(.reginfo) }
|
||||
__preinit_array_start = .;
|
||||
.preinit_array : { *(.preinit_array) }
|
||||
__preinit_array_end = .;
|
||||
__init_array_start = .;
|
||||
.init_array : { *(.init_array) }
|
||||
__init_array_end = .;
|
||||
__fini_array_start = .;
|
||||
.fini_array : { *(.fini_array) }
|
||||
__fini_array_end = .;
|
||||
|
||||
/* Adjust the address for the data segment. We want to adjust up to
|
||||
the same address within the page on the next page up. */
|
||||
. = ALIGN(0x100000) + (. & (0x100000 - 1));
|
||||
.data :
|
||||
{
|
||||
*(.data)
|
||||
*(.gnu.linkonce.d*)
|
||||
CONSTRUCTORS
|
||||
}
|
||||
.data1 : { *(.data1) }
|
||||
.ctors :
|
||||
{
|
||||
*(.ctors)
|
||||
}
|
||||
.dtors :
|
||||
{
|
||||
*(.dtors)
|
||||
}
|
||||
.plt : { *(.plt) }
|
||||
.got : { *(.got.plt) *(.got) }
|
||||
.dynamic : { *(.dynamic) }
|
||||
/* We want the small data sections together, so single-instruction offsets
|
||||
can access them all, and initialized data all before uninitialized, so
|
||||
we can shorten the on-disk segment size. */
|
||||
.sdata : { *(.sdata) }
|
||||
_edata = .;
|
||||
PROVIDE (edata = .);
|
||||
__bss_start = .;
|
||||
.sbss : { *(.sbss) *(.scommon) }
|
||||
.bss :
|
||||
{
|
||||
*(.dynbss)
|
||||
*(.bss)
|
||||
*(COMMON)
|
||||
}
|
||||
_end = . ;
|
||||
PROVIDE (end = .);
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
/* DWARF debug sections.
|
||||
Symbols in the DWARF debugging sections are relative to the beginning
|
||||
of the section so we begin them at 0. */
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
/* SGI/MIPS DWARF 2 extensions */
|
||||
.debug_weaknames 0 : { *(.debug_weaknames) }
|
||||
.debug_funcnames 0 : { *(.debug_funcnames) }
|
||||
.debug_typenames 0 : { *(.debug_typenames) }
|
||||
.debug_varnames 0 : { *(.debug_varnames) }
|
||||
/* These must appear regardless of . */
|
||||
}
|
@@ -26,8 +26,6 @@
|
||||
|
||||
#include "qemu.h"
|
||||
|
||||
#include "cpu-i386.h"
|
||||
|
||||
#define DEBUG_LOGFILE "/tmp/qemu.log"
|
||||
|
||||
FILE *logfile = NULL;
|
||||
@@ -100,6 +98,11 @@ int cpu_x86_inl(CPUX86State *env, int addr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpu_x86_get_pic_interrupt(CPUX86State *env)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void write_dt(void *ptr, unsigned long addr, unsigned long limit,
|
||||
int flags)
|
||||
{
|
||||
@@ -176,7 +179,7 @@ void cpu_loop(CPUX86State *env)
|
||||
info.si_code = TARGET_SEGV_MAPERR;
|
||||
else
|
||||
info.si_code = TARGET_SEGV_ACCERR;
|
||||
info._sifields._sigfault._addr = env->cr2;
|
||||
info._sifields._sigfault._addr = env->cr[2];
|
||||
queue_signal(info.si_signo, &info);
|
||||
break;
|
||||
case EXCP00_DIVZ:
|
||||
@@ -231,7 +234,7 @@ void cpu_loop(CPUX86State *env)
|
||||
/* just indicate that signals should be handled asap */
|
||||
break;
|
||||
default:
|
||||
pc = env->seg_cache[R_CS].base + env->eip;
|
||||
pc = env->segs[R_CS].base + env->eip;
|
||||
fprintf(stderr, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n",
|
||||
(long)pc, trapnr);
|
||||
abort();
|
||||
@@ -317,6 +320,9 @@ void usage(void)
|
||||
|
||||
/* XXX: currently only used for async signals (see signal.c) */
|
||||
CPUState *global_env;
|
||||
/* used only if single thread */
|
||||
CPUState *cpu_single_env = NULL;
|
||||
|
||||
/* used to free thread contexts */
|
||||
TaskState *first_task_state;
|
||||
|
||||
@@ -395,7 +401,7 @@ int main(int argc, char **argv)
|
||||
/* NOTE: we need to init the CPU at this stage to get the
|
||||
host_page_size */
|
||||
env = cpu_init();
|
||||
|
||||
|
||||
if (elf_exec(filename, argv+optind, environ, regs, info) != 0) {
|
||||
printf("Error loading %s\n", filename);
|
||||
_exit(1);
|
||||
@@ -423,6 +429,7 @@ int main(int argc, char **argv)
|
||||
memset(ts, 0, sizeof(TaskState));
|
||||
env->opaque = ts;
|
||||
ts->used = 1;
|
||||
env->user_mode_only = 1;
|
||||
|
||||
#if defined(TARGET_I386)
|
||||
/* linux register setup */
|
||||
@@ -476,6 +483,7 @@ int main(int argc, char **argv)
|
||||
cpu_x86_load_seg(env, R_SS, __USER_DS);
|
||||
cpu_x86_load_seg(env, R_FS, __USER_DS);
|
||||
cpu_x86_load_seg(env, R_GS, __USER_DS);
|
||||
|
||||
#elif defined(TARGET_ARM)
|
||||
{
|
||||
int i;
|
||||
|
@@ -6,8 +6,15 @@
|
||||
#include <signal.h>
|
||||
#include "syscall_defs.h"
|
||||
|
||||
#include "cpu-" TARGET_ARCH ".h"
|
||||
#include "syscall-" TARGET_ARCH ".h"
|
||||
#if defined(TARGET_I386)
|
||||
#include "cpu-i386.h"
|
||||
#include "syscall-i386.h"
|
||||
#elif defined(TARGET_ARM)
|
||||
#include "cpu-arm.h"
|
||||
#include "syscall-arm.h"
|
||||
#else
|
||||
#error unsupported target CPU
|
||||
#endif
|
||||
|
||||
/* This struct is used to hold certain information about the image.
|
||||
* Basically, it replicates in user space what would be certain
|
||||
|
@@ -517,10 +517,10 @@ setup_sigcontext(struct target_sigcontext *sc, struct target_fpstate *fpstate,
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
err |= __put_user(env->segs[R_GS], (unsigned int *)&sc->gs);
|
||||
err |= __put_user(env->segs[R_FS], (unsigned int *)&sc->fs);
|
||||
err |= __put_user(env->segs[R_ES], (unsigned int *)&sc->es);
|
||||
err |= __put_user(env->segs[R_DS], (unsigned int *)&sc->ds);
|
||||
err |= __put_user(env->segs[R_GS].selector, (unsigned int *)&sc->gs);
|
||||
err |= __put_user(env->segs[R_FS].selector, (unsigned int *)&sc->fs);
|
||||
err |= __put_user(env->segs[R_ES].selector, (unsigned int *)&sc->es);
|
||||
err |= __put_user(env->segs[R_DS].selector, (unsigned int *)&sc->ds);
|
||||
err |= __put_user(env->regs[R_EDI], &sc->edi);
|
||||
err |= __put_user(env->regs[R_ESI], &sc->esi);
|
||||
err |= __put_user(env->regs[R_EBP], &sc->ebp);
|
||||
@@ -532,10 +532,10 @@ setup_sigcontext(struct target_sigcontext *sc, struct target_fpstate *fpstate,
|
||||
err |= __put_user(env->exception_index, &sc->trapno);
|
||||
err |= __put_user(env->error_code, &sc->err);
|
||||
err |= __put_user(env->eip, &sc->eip);
|
||||
err |= __put_user(env->segs[R_CS], (unsigned int *)&sc->cs);
|
||||
err |= __put_user(env->segs[R_CS].selector, (unsigned int *)&sc->cs);
|
||||
err |= __put_user(env->eflags, &sc->eflags);
|
||||
err |= __put_user(env->regs[R_ESP], &sc->esp_at_signal);
|
||||
err |= __put_user(env->segs[R_SS], (unsigned int *)&sc->ss);
|
||||
err |= __put_user(env->segs[R_SS].selector, (unsigned int *)&sc->ss);
|
||||
|
||||
cpu_x86_fsave(env, (void *)fpstate, 1);
|
||||
fpstate->status = fpstate->sw;
|
||||
@@ -544,7 +544,7 @@ setup_sigcontext(struct target_sigcontext *sc, struct target_fpstate *fpstate,
|
||||
|
||||
/* non-iBCS2 extensions.. */
|
||||
err |= __put_user(mask, &sc->oldmask);
|
||||
err |= __put_user(env->cr2, &sc->cr2);
|
||||
err |= __put_user(env->cr[2], &sc->cr2);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -567,13 +567,14 @@ get_sigframe(struct emulated_sigaction *ka, CPUX86State *env, size_t frame_size)
|
||||
}
|
||||
|
||||
/* This is the legacy signal stack switching. */
|
||||
else if ((regs->xss & 0xffff) != __USER_DS &&
|
||||
!(ka->sa.sa_flags & SA_RESTORER) &&
|
||||
ka->sa.sa_restorer) {
|
||||
esp = (unsigned long) ka->sa.sa_restorer;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
return (void *)((esp - frame_size) & -8ul);
|
||||
if ((env->segs[R_SS].selector & 0xffff) != __USER_DS &&
|
||||
!(ka->sa.sa_flags & TARGET_SA_RESTORER) &&
|
||||
ka->sa.sa_restorer) {
|
||||
esp = (unsigned long) ka->sa.sa_restorer;
|
||||
}
|
||||
return (void *)((esp - frame_size) & -8ul);
|
||||
}
|
||||
|
||||
static void setup_frame(int sig, struct emulated_sigaction *ka,
|
||||
|
@@ -73,17 +73,17 @@ void save_v86_state(CPUX86State *env)
|
||||
ts->target_v86->regs.ebp = tswap32(env->regs[R_EBP]);
|
||||
ts->target_v86->regs.esp = tswap32(env->regs[R_ESP]);
|
||||
ts->target_v86->regs.eip = tswap32(env->eip);
|
||||
ts->target_v86->regs.cs = tswap16(env->segs[R_CS]);
|
||||
ts->target_v86->regs.ss = tswap16(env->segs[R_SS]);
|
||||
ts->target_v86->regs.ds = tswap16(env->segs[R_DS]);
|
||||
ts->target_v86->regs.es = tswap16(env->segs[R_ES]);
|
||||
ts->target_v86->regs.fs = tswap16(env->segs[R_FS]);
|
||||
ts->target_v86->regs.gs = tswap16(env->segs[R_GS]);
|
||||
ts->target_v86->regs.cs = tswap16(env->segs[R_CS].selector);
|
||||
ts->target_v86->regs.ss = tswap16(env->segs[R_SS].selector);
|
||||
ts->target_v86->regs.ds = tswap16(env->segs[R_DS].selector);
|
||||
ts->target_v86->regs.es = tswap16(env->segs[R_ES].selector);
|
||||
ts->target_v86->regs.fs = tswap16(env->segs[R_FS].selector);
|
||||
ts->target_v86->regs.gs = tswap16(env->segs[R_GS].selector);
|
||||
set_flags(env->eflags, ts->v86flags, VIF_MASK | ts->v86mask);
|
||||
ts->target_v86->regs.eflags = tswap32(env->eflags);
|
||||
#ifdef DEBUG_VM86
|
||||
fprintf(logfile, "save_v86_state: eflags=%08x cs:ip=%04x:%04x\n",
|
||||
env->eflags, env->segs[R_CS], env->eip);
|
||||
env->eflags, env->segs[R_CS].selector, env->eip);
|
||||
#endif
|
||||
|
||||
/* restore 32 bit registers */
|
||||
@@ -180,6 +180,7 @@ static inline unsigned int get_vflags(CPUX86State *env)
|
||||
flags = env->eflags & RETURN_MASK;
|
||||
if (ts->v86flags & VIF_MASK)
|
||||
flags |= IF_MASK;
|
||||
flags |= IOPL_MASK;
|
||||
return flags | (ts->v86flags & ts->v86mask);
|
||||
}
|
||||
|
||||
@@ -194,7 +195,7 @@ static void do_int(CPUX86State *env, int intno)
|
||||
uint8_t *ssp;
|
||||
unsigned int sp;
|
||||
|
||||
if (env->segs[R_CS] == TARGET_BIOSSEG)
|
||||
if (env->segs[R_CS].selector == TARGET_BIOSSEG)
|
||||
goto cannot_handle;
|
||||
if (is_revectored(intno, &ts->vm86plus.int_revectored))
|
||||
goto cannot_handle;
|
||||
@@ -210,10 +211,10 @@ static void do_int(CPUX86State *env, int intno)
|
||||
intno, segoffs >> 16, segoffs & 0xffff);
|
||||
#endif
|
||||
/* save old state */
|
||||
ssp = (uint8_t *)(env->segs[R_SS] << 4);
|
||||
ssp = (uint8_t *)(env->segs[R_SS].selector << 4);
|
||||
sp = env->regs[R_ESP] & 0xffff;
|
||||
vm_putw(ssp, sp - 2, get_vflags(env));
|
||||
vm_putw(ssp, sp - 4, env->segs[R_CS]);
|
||||
vm_putw(ssp, sp - 4, env->segs[R_CS].selector);
|
||||
vm_putw(ssp, sp - 6, env->eip);
|
||||
ADD16(env->regs[R_ESP], -6);
|
||||
/* goto interrupt handler */
|
||||
@@ -257,16 +258,16 @@ void handle_vm86_fault(CPUX86State *env)
|
||||
unsigned int ip, sp, newflags, newip, newcs, opcode, intno;
|
||||
int data32, pref_done;
|
||||
|
||||
csp = (uint8_t *)(env->segs[R_CS] << 4);
|
||||
csp = (uint8_t *)(env->segs[R_CS].selector << 4);
|
||||
ip = env->eip & 0xffff;
|
||||
pc = csp + ip;
|
||||
|
||||
ssp = (uint8_t *)(env->segs[R_SS] << 4);
|
||||
ssp = (uint8_t *)(env->segs[R_SS].selector << 4);
|
||||
sp = env->regs[R_ESP] & 0xffff;
|
||||
|
||||
#if defined(DEBUG_VM86)
|
||||
fprintf(logfile, "VM86 exception %04x:%08x %02x %02x\n",
|
||||
env->segs[R_CS], env->eip, pc[0], pc[1]);
|
||||
env->segs[R_CS].selector, env->eip, pc[0], pc[1]);
|
||||
#endif
|
||||
|
||||
data32 = 0;
|
||||
@@ -413,12 +414,12 @@ int do_vm86(CPUX86State *env, long subfunction,
|
||||
ts->vm86_saved_regs.esp = env->regs[R_ESP];
|
||||
ts->vm86_saved_regs.eflags = env->eflags;
|
||||
ts->vm86_saved_regs.eip = env->eip;
|
||||
ts->vm86_saved_regs.cs = env->segs[R_CS];
|
||||
ts->vm86_saved_regs.ss = env->segs[R_SS];
|
||||
ts->vm86_saved_regs.ds = env->segs[R_DS];
|
||||
ts->vm86_saved_regs.es = env->segs[R_ES];
|
||||
ts->vm86_saved_regs.fs = env->segs[R_FS];
|
||||
ts->vm86_saved_regs.gs = env->segs[R_GS];
|
||||
ts->vm86_saved_regs.cs = env->segs[R_CS].selector;
|
||||
ts->vm86_saved_regs.ss = env->segs[R_SS].selector;
|
||||
ts->vm86_saved_regs.ds = env->segs[R_DS].selector;
|
||||
ts->vm86_saved_regs.es = env->segs[R_ES].selector;
|
||||
ts->vm86_saved_regs.fs = env->segs[R_FS].selector;
|
||||
ts->vm86_saved_regs.gs = env->segs[R_GS].selector;
|
||||
|
||||
/* build vm86 CPU state */
|
||||
ts->v86flags = tswap32(target_v86->regs.eflags);
|
||||
@@ -466,7 +467,8 @@ int do_vm86(CPUX86State *env, long subfunction,
|
||||
target_v86->vm86plus.vm86dbg_intxxtab, 32);
|
||||
|
||||
#ifdef DEBUG_VM86
|
||||
fprintf(logfile, "do_vm86: cs:ip=%04x:%04x\n", env->segs[R_CS], env->eip);
|
||||
fprintf(logfile, "do_vm86: cs:ip=%04x:%04x\n",
|
||||
env->segs[R_CS].selector, env->eip);
|
||||
#endif
|
||||
/* now the virtual CPU is ready for vm86 execution ! */
|
||||
out:
|
||||
|
120
op-i386.c
120
op-i386.c
@@ -357,6 +357,11 @@ void OPPROTO op_andl_T0_ffff(void)
|
||||
T0 = T0 & 0xffff;
|
||||
}
|
||||
|
||||
void OPPROTO op_andl_T0_im(void)
|
||||
{
|
||||
T0 = T0 & PARAM1;
|
||||
}
|
||||
|
||||
void OPPROTO op_movl_T0_T1(void)
|
||||
{
|
||||
T0 = T1;
|
||||
@@ -488,6 +493,12 @@ void OPPROTO op_jmp_im(void)
|
||||
EIP = PARAM1;
|
||||
}
|
||||
|
||||
void OPPROTO op_hlt(void)
|
||||
{
|
||||
env->exception_index = EXCP_HLT;
|
||||
cpu_loop_exit();
|
||||
}
|
||||
|
||||
void OPPROTO op_raise_interrupt(void)
|
||||
{
|
||||
int intno;
|
||||
@@ -665,7 +676,7 @@ void op_pushl_ss32_T0(void)
|
||||
{
|
||||
uint32_t offset;
|
||||
offset = ESP - 4;
|
||||
stl(env->seg_cache[R_SS].base + offset, T0);
|
||||
stl(env->segs[R_SS].base + offset, T0);
|
||||
/* modify ESP after to handle exceptions correctly */
|
||||
ESP = offset;
|
||||
}
|
||||
@@ -674,7 +685,7 @@ void op_pushw_ss32_T0(void)
|
||||
{
|
||||
uint32_t offset;
|
||||
offset = ESP - 2;
|
||||
stw(env->seg_cache[R_SS].base + offset, T0);
|
||||
stw(env->segs[R_SS].base + offset, T0);
|
||||
/* modify ESP after to handle exceptions correctly */
|
||||
ESP = offset;
|
||||
}
|
||||
@@ -683,7 +694,7 @@ void op_pushl_ss16_T0(void)
|
||||
{
|
||||
uint32_t offset;
|
||||
offset = (ESP - 4) & 0xffff;
|
||||
stl(env->seg_cache[R_SS].base + offset, T0);
|
||||
stl(env->segs[R_SS].base + offset, T0);
|
||||
/* modify ESP after to handle exceptions correctly */
|
||||
ESP = (ESP & ~0xffff) | offset;
|
||||
}
|
||||
@@ -692,7 +703,7 @@ void op_pushw_ss16_T0(void)
|
||||
{
|
||||
uint32_t offset;
|
||||
offset = (ESP - 2) & 0xffff;
|
||||
stw(env->seg_cache[R_SS].base + offset, T0);
|
||||
stw(env->segs[R_SS].base + offset, T0);
|
||||
/* modify ESP after to handle exceptions correctly */
|
||||
ESP = (ESP & ~0xffff) | offset;
|
||||
}
|
||||
@@ -710,22 +721,22 @@ void op_popw_T0(void)
|
||||
|
||||
void op_popl_ss32_T0(void)
|
||||
{
|
||||
T0 = ldl(env->seg_cache[R_SS].base + ESP);
|
||||
T0 = ldl(env->segs[R_SS].base + ESP);
|
||||
}
|
||||
|
||||
void op_popw_ss32_T0(void)
|
||||
{
|
||||
T0 = lduw(env->seg_cache[R_SS].base + ESP);
|
||||
T0 = lduw(env->segs[R_SS].base + ESP);
|
||||
}
|
||||
|
||||
void op_popl_ss16_T0(void)
|
||||
{
|
||||
T0 = ldl(env->seg_cache[R_SS].base + (ESP & 0xffff));
|
||||
T0 = ldl(env->segs[R_SS].base + (ESP & 0xffff));
|
||||
}
|
||||
|
||||
void op_popw_ss16_T0(void)
|
||||
{
|
||||
T0 = lduw(env->seg_cache[R_SS].base + (ESP & 0xffff));
|
||||
T0 = lduw(env->segs[R_SS].base + (ESP & 0xffff));
|
||||
}
|
||||
|
||||
void op_addl_ESP_4(void)
|
||||
@@ -909,17 +920,18 @@ void OPPROTO op_movl_seg_T0(void)
|
||||
void OPPROTO op_movl_seg_T0_vm(void)
|
||||
{
|
||||
int selector;
|
||||
SegmentCache *sc;
|
||||
|
||||
selector = T0 & 0xffff;
|
||||
/* env->segs[] access */
|
||||
*(uint32_t *)((char *)env + PARAM1) = selector;
|
||||
/* env->seg_cache[] access */
|
||||
((SegmentCache *)((char *)env + PARAM2))->base = (void *)(selector << 4);
|
||||
sc = (SegmentCache *)((char *)env + PARAM1);
|
||||
sc->selector = selector;
|
||||
sc->base = (void *)(selector << 4);
|
||||
}
|
||||
|
||||
void OPPROTO op_movl_T0_seg(void)
|
||||
{
|
||||
T0 = env->segs[PARAM1];
|
||||
T0 = env->segs[PARAM1].selector;
|
||||
}
|
||||
|
||||
void OPPROTO op_movl_A0_seg(void)
|
||||
@@ -942,6 +954,71 @@ void OPPROTO op_lar(void)
|
||||
helper_lar();
|
||||
}
|
||||
|
||||
/* T0: segment, T1:eip */
|
||||
void OPPROTO op_ljmp_T0_T1(void)
|
||||
{
|
||||
jmp_seg(T0 & 0xffff, T1);
|
||||
}
|
||||
|
||||
void OPPROTO op_iret_protected(void)
|
||||
{
|
||||
helper_iret_protected(PARAM1);
|
||||
}
|
||||
|
||||
void OPPROTO op_lldt_T0(void)
|
||||
{
|
||||
helper_lldt_T0();
|
||||
}
|
||||
|
||||
void OPPROTO op_ltr_T0(void)
|
||||
{
|
||||
helper_ltr_T0();
|
||||
}
|
||||
|
||||
/* CR registers access */
|
||||
void OPPROTO op_movl_crN_T0(void)
|
||||
{
|
||||
helper_movl_crN_T0(PARAM1);
|
||||
}
|
||||
|
||||
/* DR registers access */
|
||||
void OPPROTO op_movl_drN_T0(void)
|
||||
{
|
||||
helper_movl_drN_T0(PARAM1);
|
||||
}
|
||||
|
||||
void OPPROTO op_lmsw_T0(void)
|
||||
{
|
||||
/* only 4 lower bits of CR0 are modified */
|
||||
T0 = (env->cr[0] & ~0xf) | (T0 & 0xf);
|
||||
helper_movl_crN_T0(0);
|
||||
}
|
||||
|
||||
void OPPROTO op_invlpg_A0(void)
|
||||
{
|
||||
helper_invlpg(A0);
|
||||
}
|
||||
|
||||
void OPPROTO op_movl_T0_env(void)
|
||||
{
|
||||
T0 = *(uint32_t *)((char *)env + PARAM1);
|
||||
}
|
||||
|
||||
void OPPROTO op_movl_env_T0(void)
|
||||
{
|
||||
*(uint32_t *)((char *)env + PARAM1) = T0;
|
||||
}
|
||||
|
||||
void OPPROTO op_movl_env_T1(void)
|
||||
{
|
||||
*(uint32_t *)((char *)env + PARAM1) = T1;
|
||||
}
|
||||
|
||||
void OPPROTO op_clts(void)
|
||||
{
|
||||
env->cr[0] &= ~CR0_TS_MASK;
|
||||
}
|
||||
|
||||
/* flags handling */
|
||||
|
||||
/* slow jumps cases : in order to avoid calling a function with a
|
||||
@@ -1021,8 +1098,7 @@ void OPPROTO op_set_cc_op(void)
|
||||
CC_OP = PARAM1;
|
||||
}
|
||||
|
||||
#define FL_UPDATE_MASK32 (TF_MASK | AC_MASK | ID_MASK)
|
||||
#define FL_UPDATE_MASK16 (TF_MASK)
|
||||
#define FL_UPDATE_MASK16 (FL_UPDATE_MASK32 & 0xffff)
|
||||
|
||||
void OPPROTO op_movl_eflags_T0(void)
|
||||
{
|
||||
@@ -1031,7 +1107,8 @@ void OPPROTO op_movl_eflags_T0(void)
|
||||
CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
|
||||
DF = 1 - (2 * ((eflags >> 10) & 1));
|
||||
/* we also update some system flags as in user mode */
|
||||
env->eflags = (env->eflags & ~FL_UPDATE_MASK32) | (eflags & FL_UPDATE_MASK32);
|
||||
env->eflags = (env->eflags & ~FL_UPDATE_MASK32) |
|
||||
(eflags & FL_UPDATE_MASK32);
|
||||
}
|
||||
|
||||
void OPPROTO op_movw_eflags_T0(void)
|
||||
@@ -1041,7 +1118,18 @@ void OPPROTO op_movw_eflags_T0(void)
|
||||
CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
|
||||
DF = 1 - (2 * ((eflags >> 10) & 1));
|
||||
/* we also update some system flags as in user mode */
|
||||
env->eflags = (env->eflags & ~FL_UPDATE_MASK16) | (eflags & FL_UPDATE_MASK16);
|
||||
env->eflags = (env->eflags & ~FL_UPDATE_MASK16) |
|
||||
(eflags & FL_UPDATE_MASK16);
|
||||
}
|
||||
|
||||
void OPPROTO op_movl_eflags_T0_cpl0(void)
|
||||
{
|
||||
load_eflags(T0, FL_UPDATE_CPL0_MASK);
|
||||
}
|
||||
|
||||
void OPPROTO op_movw_eflags_T0_cpl0(void)
|
||||
{
|
||||
load_eflags(T0, FL_UPDATE_CPL0_MASK & 0xffff);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@@ -828,7 +828,7 @@ void OPPROTO glue(glue(op_bsr, SUFFIX), _T0_cc)(void)
|
||||
|
||||
#define STRING_SUFFIX _a32
|
||||
#define SI_ADDR (uint8_t *)A0 + ESI
|
||||
#define DI_ADDR env->seg_cache[R_ES].base + EDI
|
||||
#define DI_ADDR env->segs[R_ES].base + EDI
|
||||
#define INC_SI() ESI += inc
|
||||
#define INC_DI() EDI += inc
|
||||
#define CX ECX
|
||||
@@ -837,7 +837,7 @@ void OPPROTO glue(glue(op_bsr, SUFFIX), _T0_cc)(void)
|
||||
|
||||
#define STRING_SUFFIX _a16
|
||||
#define SI_ADDR (uint8_t *)A0 + (ESI & 0xffff)
|
||||
#define DI_ADDR env->seg_cache[R_ES].base + (EDI & 0xffff)
|
||||
#define DI_ADDR env->segs[R_ES].base + (EDI & 0xffff)
|
||||
#define INC_SI() ESI = (ESI & ~0xffff) | ((ESI + inc) & 0xffff)
|
||||
#define INC_DI() EDI = (EDI & ~0xffff) | ((EDI + inc) & 0xffff)
|
||||
#define CX (ECX & 0xffff)
|
||||
|
337
qemu-doc.texi
337
qemu-doc.texi
@@ -1,51 +1,87 @@
|
||||
\input texinfo @c -*- texinfo -*-
|
||||
|
||||
@settitle QEMU x86 Emulator Reference Documentation
|
||||
@settitle QEMU CPU Emulator Reference Documentation
|
||||
@titlepage
|
||||
@sp 7
|
||||
@center @titlefont{QEMU x86 Emulator Reference Documentation}
|
||||
@center @titlefont{QEMU CPU Emulator Reference Documentation}
|
||||
@sp 3
|
||||
@end titlepage
|
||||
|
||||
@chapter Introduction
|
||||
|
||||
QEMU is an x86 processor emulator. Its purpose is to run x86 Linux
|
||||
processes on non-x86 Linux architectures such as PowerPC. By using
|
||||
dynamic translation it achieves a reasonnable speed while being easy to
|
||||
port on new host CPUs. Its main goal is to be able to launch the
|
||||
@code{Wine} Windows API emulator (@url{http://www.winehq.org}) or
|
||||
@code{DOSEMU} (@url{http://www.dosemu.org}) on non-x86 CPUs.
|
||||
@section Features
|
||||
|
||||
QEMU features:
|
||||
QEMU is a FAST! processor emulator. By using dynamic translation it
|
||||
achieves a reasonnable speed while being easy to port on new host
|
||||
CPUs.
|
||||
|
||||
QEMU has two operating modes:
|
||||
@itemize
|
||||
@item User mode emulation. In this mode, QEMU can launch Linux processes
|
||||
compiled for one CPU on another CPU. Linux system calls are converted
|
||||
because of endianness and 32/64 bit mismatches. The Wine Windows API
|
||||
emulator (@url{http://www.winehq.org}) and the DOSEMU DOS emulator
|
||||
(@url{www.dosemu.org}) are the main targets for QEMU.
|
||||
|
||||
@item Full system emulation. In this mode, QEMU emulates a full
|
||||
system, including a processor and various peripherials. Currently, it
|
||||
is only used to launch an x86 Linux kernel on an x86 Linux system. It
|
||||
enables easier testing and debugging of system code. It can also be
|
||||
used to provide virtual hosting of several virtual PCs on a single
|
||||
server.
|
||||
|
||||
@end itemize
|
||||
|
||||
As QEMU requires no host kernel patches to run, it is very safe and
|
||||
easy to use.
|
||||
|
||||
QEMU generic features:
|
||||
|
||||
@itemize
|
||||
|
||||
@item User space only x86 emulator.
|
||||
|
||||
@item Currently ported on i386, PowerPC. Work in progress for S390, Alpha and Sparc.
|
||||
@item User space only or full system emulation.
|
||||
|
||||
@item Using dynamic translation to native code for reasonnable speed.
|
||||
|
||||
@item The virtual x86 CPU supports 16 bit and 32 bit addressing with segmentation.
|
||||
User space LDT and GDT are emulated. VM86 mode is also supported.
|
||||
@item Working on x86 and PowerPC hosts. Being tested on ARM, Sparc32, Alpha and S390.
|
||||
|
||||
@item Self-modifying code support.
|
||||
|
||||
@item Precise exception support.
|
||||
|
||||
@item The virtual CPU is a library (@code{libqemu}) which can be used
|
||||
in other projects.
|
||||
|
||||
@end itemize
|
||||
|
||||
QEMU user mode emulation features:
|
||||
@itemize
|
||||
@item Generic Linux system call converter, including most ioctls.
|
||||
|
||||
@item clone() emulation using native CPU clone() to use Linux scheduler for threads.
|
||||
|
||||
@item Accurate signal handling by remapping host signals to virtual x86 signals.
|
||||
@item Accurate signal handling by remapping host signals to target signals.
|
||||
@end itemize
|
||||
@end itemize
|
||||
|
||||
@item Precise user space x86 exceptions.
|
||||
QEMU full system emulation features:
|
||||
@itemize
|
||||
@item Using mmap() system calls to simulate the MMU
|
||||
@end itemize
|
||||
|
||||
@item Self-modifying code support.
|
||||
@section x86 emulation
|
||||
|
||||
@item Support of host page sizes bigger than 4KB.
|
||||
QEMU x86 target features:
|
||||
|
||||
@itemize
|
||||
|
||||
@item The virtual x86 CPU supports 16 bit and 32 bit addressing with segmentation.
|
||||
LDT/GDT and IDT are emulated. VM86 mode is also supported to run DOSEMU.
|
||||
|
||||
@item Support of host page sizes bigger than 4KB in user mode emulation.
|
||||
|
||||
@item QEMU can emulate itself on x86.
|
||||
|
||||
@item The virtual x86 CPU is a library (@code{libqemu}) which can be used
|
||||
in other projects.
|
||||
|
||||
@item An extensive Linux x86 CPU test program is included @file{tests/test-i386}.
|
||||
It can be used to test other x86 virtual CPUs.
|
||||
|
||||
@@ -62,18 +98,43 @@ Current QEMU limitations:
|
||||
@item IPC syscalls are missing.
|
||||
|
||||
@item The x86 segment limits and access rights are not tested at every
|
||||
memory access (and will never be to have good performances).
|
||||
memory access.
|
||||
|
||||
@item On non x86 host CPUs, @code{double}s are used instead of the non standard
|
||||
10 byte @code{long double}s of x86 for floating point emulation to get
|
||||
maximum performances.
|
||||
|
||||
@item Full system emulation only works if no data are mapped above the virtual address
|
||||
0xc0000000 (yet).
|
||||
|
||||
@item Some priviledged instructions or behaviors are missing. Only the ones
|
||||
needed for proper Linux kernel operation are emulated.
|
||||
|
||||
@item No memory separation between the kernel and the user processes is done.
|
||||
It will be implemented very soon.
|
||||
|
||||
@end itemize
|
||||
|
||||
@chapter Invocation
|
||||
@section ARM emulation
|
||||
|
||||
@itemize
|
||||
|
||||
@item ARM emulation can currently launch small programs while using the
|
||||
generic dynamic code generation architecture of QEMU.
|
||||
|
||||
@item No FPU support (yet).
|
||||
|
||||
@item No automatic regression testing (yet).
|
||||
|
||||
@end itemize
|
||||
|
||||
@chapter QEMU User space emulation invocation
|
||||
|
||||
@section Quick Start
|
||||
|
||||
If you need to compile QEMU, please read the @file{README} which gives
|
||||
the related information.
|
||||
|
||||
In order to launch a Linux process, QEMU needs the process executable
|
||||
itself and all the target (x86) dynamic libraries used by it.
|
||||
|
||||
@@ -171,27 +232,188 @@ Activate log (logfile=/tmp/qemu.log)
|
||||
Act as if the host page size was 'pagesize' bytes
|
||||
@end table
|
||||
|
||||
@chapter QEMU System emulator invocation
|
||||
|
||||
@section Quick Start
|
||||
|
||||
This section explains how to launch a Linux kernel inside QEMU.
|
||||
|
||||
@enumerate
|
||||
@item
|
||||
Download the archive @file{vl-test-xxx.tar.gz} containing a Linux kernel
|
||||
and an initrd (initial Ram Disk). The archive also contains a
|
||||
precompiled version of @file{vl}, the QEMU System emulator.
|
||||
|
||||
@item Optional: If you want network support (for example to launch X11 examples), you
|
||||
must copy the script @file{vl-ifup} in @file{/etc} and configure
|
||||
properly @code{sudo} so that the command @code{ifconfig} contained in
|
||||
@file{vl-ifup} can be executed as root. You must verify that your host
|
||||
kernel supports the TUN/TAP network interfaces: the device
|
||||
@file{/dev/net/tun} must be present.
|
||||
|
||||
When network is enabled, there is a virtual network connection between
|
||||
the host kernel and the emulated kernel. The emulated kernel is seen
|
||||
from the host kernel at IP address 172.20.0.2 and the host kernel is
|
||||
seen from the emulated kernel at IP address 172.20.0.1.
|
||||
|
||||
@item Launch @code{vl.sh}. You should have the following output:
|
||||
|
||||
@example
|
||||
> ./vl.sh
|
||||
connected to host network interface: tun0
|
||||
Uncompressing Linux... Ok, booting the kernel.
|
||||
Linux version 2.4.20 (bellard@voyager) (gcc version 2.95.2 20000220 (Debian GNU/Linux)) #42 Wed Jun 25 14:16:12 CEST 2003
|
||||
BIOS-provided physical RAM map:
|
||||
BIOS-88: 0000000000000000 - 000000000009f000 (usable)
|
||||
BIOS-88: 0000000000100000 - 0000000002000000 (usable)
|
||||
32MB LOWMEM available.
|
||||
On node 0 totalpages: 8192
|
||||
zone(0): 4096 pages.
|
||||
zone(1): 4096 pages.
|
||||
zone(2): 0 pages.
|
||||
Kernel command line: root=/dev/ram ramdisk_size=6144
|
||||
Initializing CPU#0
|
||||
Detected 501.785 MHz processor.
|
||||
Calibrating delay loop... 973.20 BogoMIPS
|
||||
Memory: 24776k/32768k available (725k kernel code, 7604k reserved, 151k data, 48k init, 0k highmem)
|
||||
Dentry cache hash table entries: 4096 (order: 3, 32768 bytes)
|
||||
Inode cache hash table entries: 2048 (order: 2, 16384 bytes)
|
||||
Mount-cache hash table entries: 512 (order: 0, 4096 bytes)
|
||||
Buffer-cache hash table entries: 1024 (order: 0, 4096 bytes)
|
||||
Page-cache hash table entries: 8192 (order: 3, 32768 bytes)
|
||||
CPU: Intel Pentium Pro stepping 03
|
||||
Checking 'hlt' instruction... OK.
|
||||
POSIX conformance testing by UNIFIX
|
||||
Linux NET4.0 for Linux 2.4
|
||||
Based upon Swansea University Computer Society NET3.039
|
||||
Initializing RT netlink socket
|
||||
apm: BIOS not found.
|
||||
Starting kswapd
|
||||
pty: 256 Unix98 ptys configured
|
||||
Serial driver version 5.05c (2001-07-08) with no serial options enabled
|
||||
ttyS00 at 0x03f8 (irq = 4) is a 16450
|
||||
ne.c:v1.10 9/23/94 Donald Becker (becker@scyld.com)
|
||||
Last modified Nov 1, 2000 by Paul Gortmaker
|
||||
NE*000 ethercard probe at 0x300: 52 54 00 12 34 56
|
||||
eth0: NE2000 found at 0x300, using IRQ 9.
|
||||
RAMDISK driver initialized: 16 RAM disks of 6144K size 1024 blocksize
|
||||
NET4: Linux TCP/IP 1.0 for NET4.0
|
||||
IP Protocols: ICMP, UDP, TCP, IGMP
|
||||
IP: routing cache hash table of 512 buckets, 4Kbytes
|
||||
TCP: Hash tables configured (established 2048 bind 2048)
|
||||
NET4: Unix domain sockets 1.0/SMP for Linux NET4.0.
|
||||
RAMDISK: ext2 filesystem found at block 0
|
||||
RAMDISK: Loading 6144 blocks [1 disk] into ram disk... done.
|
||||
Freeing initrd memory: 6144k freed
|
||||
VFS: Mounted root (ext2 filesystem).
|
||||
Freeing unused kernel memory: 48k freed
|
||||
sh: can't access tty; job control turned off
|
||||
#
|
||||
@end example
|
||||
|
||||
@item
|
||||
Then you can play with the kernel inside the virtual serial console. You
|
||||
can launch @code{ls} for example. Type @key{Ctrl-a h} to have an help
|
||||
about the keys you can type inside the virtual serial console. In
|
||||
particular @key{Ctrl-a b} is the Magic SysRq key.
|
||||
|
||||
@item
|
||||
If the network is enabled, launch the script @file{/etc/linuxrc} in the
|
||||
emulator (don't forget the leading dot):
|
||||
@example
|
||||
. /etc/linuxrc
|
||||
@end example
|
||||
|
||||
Then enable X11 connections on your PC from the emulated Linux:
|
||||
@example
|
||||
xhost +172.20.0.2
|
||||
@end example
|
||||
|
||||
You can now launch @file{xterm} or @file{xlogo} and verify that you have
|
||||
a real Virtual Linux system !
|
||||
|
||||
@end enumerate
|
||||
|
||||
NOTE: the example initrd is a modified version of the one made by Kevin
|
||||
Lawton for the plex86 Project (@url{www.plex86.org}).
|
||||
|
||||
@section Kernel Compilation
|
||||
|
||||
You can use any Linux kernel within QEMU provided it is mapped at
|
||||
address 0x90000000 (the default is 0xc0000000). You must modify only two
|
||||
lines in the kernel source:
|
||||
|
||||
In asm/page.h, replace
|
||||
@example
|
||||
#define __PAGE_OFFSET (0xc0000000)
|
||||
@end example
|
||||
by
|
||||
@example
|
||||
#define __PAGE_OFFSET (0x90000000)
|
||||
@end example
|
||||
|
||||
And in arch/i386/vmlinux.lds, replace
|
||||
@example
|
||||
. = 0xc0000000 + 0x100000;
|
||||
@end example
|
||||
by
|
||||
@example
|
||||
. = 0x90000000 + 0x100000;
|
||||
@end example
|
||||
|
||||
The file config-2.4.20 gives the configuration of the example kernel.
|
||||
|
||||
Just type
|
||||
@example
|
||||
make bzImage
|
||||
@end example
|
||||
|
||||
As you would do to make a real kernel. Then you can use with QEMU
|
||||
exactly the same kernel as you would boot on your PC (in
|
||||
@file{arch/i386/boot/bzImage}).
|
||||
|
||||
@section PC Emulation
|
||||
|
||||
QEMU emulates the following PC peripherials:
|
||||
|
||||
@itemize
|
||||
@item
|
||||
PIC (interrupt controler)
|
||||
@item
|
||||
PIT (timers)
|
||||
@item
|
||||
CMOS memory
|
||||
@item
|
||||
Serial port (port=0x3f8, irq=4)
|
||||
@item
|
||||
NE2000 network adapter (port=0x300, irq=9)
|
||||
@item
|
||||
Dumb VGA (to print the @code{uncompressing Linux kernel} message)
|
||||
@end itemize
|
||||
|
||||
@chapter QEMU Internals
|
||||
|
||||
@section QEMU compared to other emulators
|
||||
|
||||
Unlike bochs [3], QEMU emulates only a user space x86 CPU. It means that
|
||||
you cannot launch an operating system with it. The benefit is that it is
|
||||
simpler and faster due to the fact that some of the low level CPU state
|
||||
can be ignored (in particular, no virtual memory needs to be emulated).
|
||||
Like bochs [3], QEMU emulates an x86 CPU. But QEMU is much faster than
|
||||
bochs as it uses dynamic compilation and because it uses the host MMU to
|
||||
simulate the x86 MMU. The downside is that currently the emulation is
|
||||
not as accurate as bochs (for example, you cannot currently run Windows
|
||||
inside QEMU).
|
||||
|
||||
Like Valgrind [2], QEMU does user space emulation and dynamic
|
||||
translation. Valgrind is mainly a memory debugger while QEMU has no
|
||||
support for it (QEMU could be used to detect out of bound memory accesses
|
||||
as Valgrind, but it has no support to track uninitialised data as
|
||||
Valgrind does). Valgrind dynamic translator generates better code than
|
||||
QEMU (in particular it does register allocation) but it is closely tied
|
||||
to an x86 host.
|
||||
support for it (QEMU could be used to detect out of bound memory
|
||||
accesses as Valgrind, but it has no support to track uninitialised data
|
||||
as Valgrind does). Valgrind dynamic translator generates better code
|
||||
than QEMU (in particular it does register allocation) but it is closely
|
||||
tied to an x86 host and target and has no support for precise exception
|
||||
and system emulation.
|
||||
|
||||
EM86 [4] is the closest project to QEMU (and QEMU still uses some of its
|
||||
code, in particular the ELF file loader). EM86 was limited to an alpha
|
||||
host and used a proprietary and slow interpreter (the interpreter part
|
||||
of the FX!32 Digital Win32 code translator [5]).
|
||||
EM86 [4] is the closest project to user space QEMU (and QEMU still uses
|
||||
some of its code, in particular the ELF file loader). EM86 was limited
|
||||
to an alpha host and used a proprietary and slow interpreter (the
|
||||
interpreter part of the FX!32 Digital Win32 code translator [5]).
|
||||
|
||||
TWIN [6] is a Windows API emulator like Wine. It is less accurate than
|
||||
Wine but includes a protected mode x86 interpreter to launch x86 Windows
|
||||
@@ -200,11 +422,25 @@ Windows API is executed natively but it is far more difficult to develop
|
||||
because all the data structures and function parameters exchanged
|
||||
between the API and the x86 code must be converted.
|
||||
|
||||
User mode Linux [7] was the only solution before QEMU to launch a Linux
|
||||
kernel as a process while not needing any host kernel patches. However,
|
||||
user mode Linux requires heavy kernel patches while QEMU accepts
|
||||
unpatched Linux kernels. It would be interesting to compare the
|
||||
performance of the two approaches.
|
||||
|
||||
The new Plex86 [8] PC virtualizer is done in the same spirit as the QEMU
|
||||
system emulator. It requires a patched Linux kernel to work (you cannot
|
||||
launch the same kernel on your PC), but the patches are really small. As
|
||||
it is a PC virtualizer (no emulation is done except for some priveledged
|
||||
instructions), it has the potential of being faster than QEMU. The
|
||||
downside is that a complicated (and potentially unsafe) kernel patch is
|
||||
needed.
|
||||
|
||||
@section Portable dynamic translation
|
||||
|
||||
QEMU is a dynamic translator. When it first encounters a piece of code,
|
||||
it converts it to the host instruction set. Usually dynamic translators
|
||||
are very complicated and highly CPU dependant. QEMU uses some tricks
|
||||
are very complicated and highly CPU dependent. QEMU uses some tricks
|
||||
which make it relatively easily portable and simple while achieving good
|
||||
performances.
|
||||
|
||||
@@ -382,6 +618,16 @@ space conflicts. QEMU solves this problem by being an executable ELF
|
||||
shared object as the ld-linux.so ELF interpreter. That way, it can be
|
||||
relocated at load time.
|
||||
|
||||
@section MMU emulation
|
||||
|
||||
For system emulation, QEMU uses the mmap() system call to emulate the
|
||||
target CPU MMU. It works as long the emulated OS does not use an area
|
||||
reserved by the host OS (such as the area above 0xc0000000 on x86
|
||||
Linux).
|
||||
|
||||
It is planned to add a slower but more precise MMU emulation
|
||||
with a software MMU.
|
||||
|
||||
@section Bibliography
|
||||
|
||||
@table @asis
|
||||
@@ -412,18 +658,31 @@ Chernoff and Ray Hookway.
|
||||
@url{http://www.willows.com/}, Windows API library emulation from
|
||||
Willows Software.
|
||||
|
||||
@item [7]
|
||||
@url{http://user-mode-linux.sourceforge.net/},
|
||||
The User-mode Linux Kernel.
|
||||
|
||||
@item [8]
|
||||
@url{http://www.plex86.org/},
|
||||
The new Plex86 project.
|
||||
|
||||
@end table
|
||||
|
||||
@chapter Regression Tests
|
||||
|
||||
In the directory @file{tests/}, various interesting x86 testing programs
|
||||
In the directory @file{tests/}, various interesting testing programs
|
||||
are available. There are used for regression testing.
|
||||
|
||||
@section @file{hello}
|
||||
@section @file{hello-i386}
|
||||
|
||||
Very simple statically linked x86 program, just to test QEMU during a
|
||||
port to a new host CPU.
|
||||
|
||||
@section @file{hello-arm}
|
||||
|
||||
Very simple statically linked ARM program, just to test QEMU during a
|
||||
port to a new host CPU.
|
||||
|
||||
@section @file{test-i386}
|
||||
|
||||
This program executes most of the 16 bit and 32 bit x86 instructions and
|
||||
|
@@ -1,6 +1,6 @@
|
||||
gmon.out
|
||||
testsig
|
||||
hello
|
||||
hello-i386
|
||||
hello-arm
|
||||
sha1.test.c
|
||||
sha1.c
|
||||
|
@@ -12,9 +12,9 @@ QEMU=../qemu
|
||||
|
||||
all: $(TESTS)
|
||||
|
||||
hello: hello.c
|
||||
hello-i386: hello-i386.c
|
||||
$(CC) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $<
|
||||
strip hello
|
||||
strip $@
|
||||
|
||||
testclone: testclone.c
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
|
||||
|
@@ -77,4 +77,21 @@ myfunc2:
|
||||
|
||||
|
||||
code16_end:
|
||||
|
||||
|
||||
/* other 32 bits tests */
|
||||
.code32
|
||||
|
||||
.globl func_lret32
|
||||
func_lret32:
|
||||
movl $0x87654321, %eax
|
||||
lret
|
||||
|
||||
.globl func_iret32
|
||||
func_iret32:
|
||||
movl $0xabcd4321, %eax
|
||||
iret
|
||||
|
||||
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <signal.h>
|
||||
@@ -591,7 +592,7 @@ void test_fbcd(double a)
|
||||
asm("fld1\n"\
|
||||
prefix "fnstenv %1\n"\
|
||||
prefix "fldenv %1\n"\
|
||||
: "=t" (res) : "m" (*(env)) : "st");\
|
||||
: "=t" (res) : "m" (*(env)));\
|
||||
printf("res=%f\n", res);\
|
||||
printf("fpuc=%04x fpus=%04x fptag=%04x\n",\
|
||||
(env)->fpuc,\
|
||||
@@ -601,7 +602,7 @@ void test_fbcd(double a)
|
||||
asm("fld1\n"\
|
||||
prefix "fnsave %1\n"\
|
||||
prefix "frstor %1\n"\
|
||||
: "=t" (res) : "m" (*(env)) : "st");\
|
||||
: "=t" (res) : "m" (*(env)));\
|
||||
printf("res=%f\n", res);\
|
||||
printf("fpuc=%04x fpus=%04x fptag=%04x\n",\
|
||||
(env)->fpuc,\
|
||||
@@ -937,6 +938,9 @@ void test_code16(void)
|
||||
printf("func3() = 0x%08x\n", res);
|
||||
}
|
||||
|
||||
extern char func_lret32;
|
||||
extern char func_iret32;
|
||||
|
||||
void test_misc(void)
|
||||
{
|
||||
char table[256];
|
||||
@@ -946,6 +950,21 @@ void test_misc(void)
|
||||
res = 0x12345678;
|
||||
asm ("xlat" : "=a" (res) : "b" (table), "0" (res));
|
||||
printf("xlat: EAX=%08x\n", res);
|
||||
|
||||
asm volatile ("pushl %%cs ; call %1"
|
||||
: "=a" (res)
|
||||
: "m" (func_lret32): "memory", "cc");
|
||||
printf("func_lret32=%x\n", res);
|
||||
|
||||
asm volatile ("pushfl ; pushl %%cs ; call %1"
|
||||
: "=a" (res)
|
||||
: "m" (func_iret32): "memory", "cc");
|
||||
printf("func_iret32=%x\n", res);
|
||||
|
||||
/* specific popl test */
|
||||
asm volatile ("pushl $12345432 ; pushl $0x9abcdef ; popl (%%esp) ; popl %0"
|
||||
: "=g" (res));
|
||||
printf("popl esp=%x\n", res);
|
||||
}
|
||||
|
||||
uint8_t str_buffer[4096];
|
||||
|
@@ -655,7 +655,7 @@ static void disas_arm_insn(DisasContext *s)
|
||||
/* generate intermediate code in gen_opc_buf and gen_opparam_buf for
|
||||
basic block 'tb'. If search_pc is TRUE, also generate PC
|
||||
information for each intermediate instruction. */
|
||||
int gen_intermediate_code(TranslationBlock *tb, int search_pc)
|
||||
static inline int gen_intermediate_code_internal(TranslationBlock *tb, int search_pc)
|
||||
{
|
||||
DisasContext dc1, *dc = &dc1;
|
||||
uint16_t *gen_opc_end;
|
||||
@@ -717,6 +717,16 @@ int gen_intermediate_code(TranslationBlock *tb, int search_pc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gen_intermediate_code(TranslationBlock *tb)
|
||||
{
|
||||
return gen_intermediate_code_internal(tb, 0);
|
||||
}
|
||||
|
||||
int gen_intermediate_code_pc(TranslationBlock *tb)
|
||||
{
|
||||
return gen_intermediate_code_internal(tb, 1);
|
||||
}
|
||||
|
||||
CPUARMState *cpu_arm_init(void)
|
||||
{
|
||||
CPUARMState *env;
|
||||
|
536
translate-i386.c
536
translate-i386.c
@@ -24,11 +24,14 @@
|
||||
#include <inttypes.h>
|
||||
#include <signal.h>
|
||||
#include <assert.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "cpu-i386.h"
|
||||
#include "exec.h"
|
||||
#include "disas.h"
|
||||
|
||||
//#define DEBUG_MMU
|
||||
|
||||
/* XXX: move that elsewhere */
|
||||
static uint16_t *gen_opc_ptr;
|
||||
static uint32_t *gen_opparam_ptr;
|
||||
@@ -59,6 +62,7 @@ typedef struct DisasContext {
|
||||
int iopl;
|
||||
int tf; /* TF cpu flag */
|
||||
struct TranslationBlock *tb;
|
||||
int popl_esp_hack; /* for correct popl with esp base handling */
|
||||
} DisasContext;
|
||||
|
||||
/* i386 arith/logic operations */
|
||||
@@ -575,7 +579,7 @@ static inline void gen_string_ds(DisasContext *s, int ot, GenOpFunc **func)
|
||||
if (s->addseg && override < 0)
|
||||
override = R_DS;
|
||||
if (override >= 0) {
|
||||
gen_op_movl_A0_seg(offsetof(CPUX86State,seg_cache[override].base));
|
||||
gen_op_movl_A0_seg(offsetof(CPUX86State,segs[override].base));
|
||||
index = 3 + ot;
|
||||
} else {
|
||||
index = ot;
|
||||
@@ -583,7 +587,7 @@ static inline void gen_string_ds(DisasContext *s, int ot, GenOpFunc **func)
|
||||
} else {
|
||||
if (override < 0)
|
||||
override = R_DS;
|
||||
gen_op_movl_A0_seg(offsetof(CPUX86State,seg_cache[override].base));
|
||||
gen_op_movl_A0_seg(offsetof(CPUX86State,segs[override].base));
|
||||
/* 16 address, always override */
|
||||
index = 6 + ot;
|
||||
}
|
||||
@@ -862,12 +866,16 @@ static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_
|
||||
}
|
||||
|
||||
if (base >= 0) {
|
||||
/* for correct popl handling with esp */
|
||||
if (base == 4 && s->popl_esp_hack)
|
||||
disp += 4;
|
||||
gen_op_movl_A0_reg[base]();
|
||||
if (disp != 0)
|
||||
gen_op_addl_A0_im(disp);
|
||||
} else {
|
||||
gen_op_movl_A0_im(disp);
|
||||
}
|
||||
/* XXX: index == 4 is always invalid */
|
||||
if (havesib && (index != 4 || scale != 0)) {
|
||||
gen_op_addl_A0_reg_sN[scale][index]();
|
||||
}
|
||||
@@ -878,7 +886,7 @@ static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_
|
||||
else
|
||||
override = R_DS;
|
||||
}
|
||||
gen_op_addl_A0_seg(offsetof(CPUX86State,seg_cache[override].base));
|
||||
gen_op_addl_A0_seg(offsetof(CPUX86State,segs[override].base));
|
||||
}
|
||||
} else {
|
||||
switch (mod) {
|
||||
@@ -944,7 +952,7 @@ static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_
|
||||
else
|
||||
override = R_DS;
|
||||
}
|
||||
gen_op_addl_A0_seg(offsetof(CPUX86State,seg_cache[override].base));
|
||||
gen_op_addl_A0_seg(offsetof(CPUX86State,segs[override].base));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1146,8 +1154,7 @@ static void gen_movl_seg_T0(DisasContext *s, int seg_reg, unsigned int cur_eip)
|
||||
if (!s->vm86)
|
||||
gen_op_movl_seg_T0(seg_reg, cur_eip);
|
||||
else
|
||||
gen_op_movl_seg_T0_vm(offsetof(CPUX86State,segs[seg_reg]),
|
||||
offsetof(CPUX86State,seg_cache[seg_reg].base));
|
||||
gen_op_movl_seg_T0_vm(offsetof(CPUX86State,segs[seg_reg]));
|
||||
if (!s->addseg && seg_reg < R_FS)
|
||||
s->is_jmp = 2; /* abort translation because the register may
|
||||
have a non zero base */
|
||||
@@ -1230,7 +1237,7 @@ static void gen_stack_A0(DisasContext *s)
|
||||
gen_op_andl_A0_ffff();
|
||||
gen_op_movl_T1_A0();
|
||||
if (s->addseg)
|
||||
gen_op_addl_A0_seg(offsetof(CPUX86State,seg_cache[R_SS].base));
|
||||
gen_op_addl_A0_seg(offsetof(CPUX86State,segs[R_SS].base));
|
||||
}
|
||||
|
||||
/* NOTE: wrap around in 16 bit not fully handled */
|
||||
@@ -1243,7 +1250,7 @@ static void gen_pusha(DisasContext *s)
|
||||
gen_op_andl_A0_ffff();
|
||||
gen_op_movl_T1_A0();
|
||||
if (s->addseg)
|
||||
gen_op_addl_A0_seg(offsetof(CPUX86State,seg_cache[R_SS].base));
|
||||
gen_op_addl_A0_seg(offsetof(CPUX86State,segs[R_SS].base));
|
||||
for(i = 0;i < 8; i++) {
|
||||
gen_op_mov_TN_reg[OT_LONG][0][7 - i]();
|
||||
gen_op_st_T0_A0[OT_WORD + s->dflag]();
|
||||
@@ -1262,7 +1269,7 @@ static void gen_popa(DisasContext *s)
|
||||
gen_op_movl_T1_A0();
|
||||
gen_op_addl_T1_im(16 << s->dflag);
|
||||
if (s->addseg)
|
||||
gen_op_addl_A0_seg(offsetof(CPUX86State,seg_cache[R_SS].base));
|
||||
gen_op_addl_A0_seg(offsetof(CPUX86State,segs[R_SS].base));
|
||||
for(i = 0;i < 8; i++) {
|
||||
/* ESP is not reloaded */
|
||||
if (i != 3) {
|
||||
@@ -1291,7 +1298,7 @@ static void gen_enter(DisasContext *s, int esp_addend, int level)
|
||||
gen_op_andl_A0_ffff();
|
||||
gen_op_movl_T1_A0();
|
||||
if (s->addseg)
|
||||
gen_op_addl_A0_seg(offsetof(CPUX86State,seg_cache[R_SS].base));
|
||||
gen_op_addl_A0_seg(offsetof(CPUX86State,segs[R_SS].base));
|
||||
/* push bp */
|
||||
gen_op_mov_TN_reg[OT_LONG][0][R_EBP]();
|
||||
gen_op_st_T0_A0[ot]();
|
||||
@@ -1714,9 +1721,15 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
||||
gen_op_ld_T1_A0[ot]();
|
||||
gen_op_addl_A0_im(1 << (ot - OT_WORD + 1));
|
||||
gen_op_lduw_T0_A0();
|
||||
gen_movl_seg_T0(s, R_CS, pc_start - s->cs_base);
|
||||
gen_op_movl_T0_T1();
|
||||
gen_op_jmp_T0();
|
||||
if (!s->vm86) {
|
||||
/* we compute EIP to handle the exception case */
|
||||
gen_op_jmp_im(pc_start - s->cs_base);
|
||||
gen_op_ljmp_T0_T1();
|
||||
} else {
|
||||
gen_op_movl_seg_T0_vm(offsetof(CPUX86State,segs[R_CS]));
|
||||
gen_op_movl_T0_T1();
|
||||
gen_op_jmp_T0();
|
||||
}
|
||||
s->is_jmp = 1;
|
||||
break;
|
||||
case 6: /* push Ev */
|
||||
@@ -1889,7 +1902,9 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
||||
ot = dflag ? OT_LONG : OT_WORD;
|
||||
modrm = ldub(s->pc++);
|
||||
gen_pop_T0(s);
|
||||
s->popl_esp_hack = 1;
|
||||
gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1);
|
||||
s->popl_esp_hack = 0;
|
||||
gen_pop_update(s);
|
||||
break;
|
||||
case 0xc8: /* enter */
|
||||
@@ -2085,7 +2100,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
||||
override = R_DS;
|
||||
}
|
||||
if (must_add_seg) {
|
||||
gen_op_addl_A0_seg(offsetof(CPUX86State,seg_cache[override].base));
|
||||
gen_op_addl_A0_seg(offsetof(CPUX86State,segs[override].base));
|
||||
}
|
||||
}
|
||||
if ((b & 2) == 0) {
|
||||
@@ -2113,7 +2128,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
||||
override = R_DS;
|
||||
}
|
||||
if (must_add_seg) {
|
||||
gen_op_addl_A0_seg(offsetof(CPUX86State,seg_cache[override].base));
|
||||
gen_op_addl_A0_seg(offsetof(CPUX86State,segs[override].base));
|
||||
}
|
||||
}
|
||||
gen_op_ldub_T0_A0();
|
||||
@@ -2619,12 +2634,18 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
||||
break;
|
||||
case 0x1c:
|
||||
switch(rm) {
|
||||
case 0: /* feni (287 only, just do nop here) */
|
||||
break;
|
||||
case 1: /* fdisi (287 only, just do nop here) */
|
||||
break;
|
||||
case 2: /* fclex */
|
||||
gen_op_fclex();
|
||||
break;
|
||||
case 3: /* fninit */
|
||||
gen_op_fninit();
|
||||
break;
|
||||
case 4: /* fsetpm (287 only, just do nop here) */
|
||||
break;
|
||||
default:
|
||||
goto illegal_op;
|
||||
}
|
||||
@@ -2929,29 +2950,10 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
||||
if (s->vm86 && s->iopl != 3) {
|
||||
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
|
||||
} else {
|
||||
/* XXX: not restartable */
|
||||
gen_stack_A0(s);
|
||||
/* pop offset */
|
||||
gen_op_ld_T0_A0[1 + s->dflag]();
|
||||
if (s->dflag == 0)
|
||||
gen_op_andl_T0_ffff();
|
||||
/* NOTE: keeping EIP updated is not a problem in case of
|
||||
exception */
|
||||
gen_op_jmp_T0();
|
||||
/* pop selector */
|
||||
gen_op_addl_A0_im(2 << s->dflag);
|
||||
gen_op_ld_T0_A0[1 + s->dflag]();
|
||||
/* pop eflags */
|
||||
gen_op_addl_A0_im(2 << s->dflag);
|
||||
gen_op_ld_T1_A0[1 + s->dflag]();
|
||||
gen_movl_seg_T0(s, R_CS, pc_start - s->cs_base);
|
||||
gen_op_movl_T0_T1();
|
||||
if (s->dflag) {
|
||||
gen_op_movl_eflags_T0();
|
||||
} else {
|
||||
gen_op_movw_eflags_T0();
|
||||
}
|
||||
gen_stack_update(s, (6 << s->dflag));
|
||||
if (s->cc_op != CC_OP_DYNAMIC)
|
||||
gen_op_set_cc_op(s->cc_op);
|
||||
gen_op_jmp_im(pc_start - s->cs_base);
|
||||
gen_op_iret_protected(s->dflag);
|
||||
s->cc_op = CC_OP_EFLAGS;
|
||||
}
|
||||
s->is_jmp = 1;
|
||||
@@ -3011,8 +3013,15 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
||||
|
||||
/* change cs and pc */
|
||||
gen_op_movl_T0_im(selector);
|
||||
gen_movl_seg_T0(s, R_CS, pc_start - s->cs_base);
|
||||
gen_op_jmp_im((unsigned long)offset);
|
||||
if (!s->vm86) {
|
||||
/* we compute EIP to handle the exception case */
|
||||
gen_op_jmp_im(pc_start - s->cs_base);
|
||||
gen_op_movl_T1_im(offset);
|
||||
gen_op_ljmp_T0_T1();
|
||||
} else {
|
||||
gen_op_movl_seg_T0_vm(offsetof(CPUX86State,segs[R_CS]));
|
||||
gen_op_jmp_im((unsigned long)offset);
|
||||
}
|
||||
s->is_jmp = 1;
|
||||
}
|
||||
break;
|
||||
@@ -3078,10 +3087,18 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
||||
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
|
||||
} else {
|
||||
gen_pop_T0(s);
|
||||
if (s->dflag) {
|
||||
gen_op_movl_eflags_T0();
|
||||
if (s->cpl == 0) {
|
||||
if (s->dflag) {
|
||||
gen_op_movl_eflags_T0_cpl0();
|
||||
} else {
|
||||
gen_op_movw_eflags_T0_cpl0();
|
||||
}
|
||||
} else {
|
||||
gen_op_movw_eflags_T0();
|
||||
if (s->dflag) {
|
||||
gen_op_movl_eflags_T0();
|
||||
} else {
|
||||
gen_op_movw_eflags_T0();
|
||||
}
|
||||
}
|
||||
gen_pop_update(s);
|
||||
s->cc_op = CC_OP_EFLAGS;
|
||||
@@ -3340,8 +3357,130 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
||||
gen_op_cpuid();
|
||||
break;
|
||||
case 0xf4: /* hlt */
|
||||
/* XXX: if cpl == 0, then should do something else */
|
||||
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
|
||||
if (s->cpl != 0) {
|
||||
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
|
||||
} else {
|
||||
if (s->cc_op != CC_OP_DYNAMIC)
|
||||
gen_op_set_cc_op(s->cc_op);
|
||||
gen_op_jmp_im(s->pc - s->cs_base);
|
||||
gen_op_hlt();
|
||||
s->is_jmp = 1;
|
||||
}
|
||||
break;
|
||||
case 0x100:
|
||||
modrm = ldub(s->pc++);
|
||||
mod = (modrm >> 6) & 3;
|
||||
op = (modrm >> 3) & 7;
|
||||
switch(op) {
|
||||
case 0: /* sldt */
|
||||
gen_op_movl_T0_env(offsetof(CPUX86State,ldt.selector));
|
||||
ot = OT_WORD;
|
||||
if (mod == 3)
|
||||
ot += s->dflag;
|
||||
gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1);
|
||||
break;
|
||||
case 2: /* lldt */
|
||||
if (s->cpl != 0) {
|
||||
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
|
||||
} else {
|
||||
gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0);
|
||||
gen_op_jmp_im(pc_start - s->cs_base);
|
||||
gen_op_lldt_T0();
|
||||
}
|
||||
break;
|
||||
case 1: /* str */
|
||||
gen_op_movl_T0_env(offsetof(CPUX86State,tr.selector));
|
||||
ot = OT_WORD;
|
||||
if (mod == 3)
|
||||
ot += s->dflag;
|
||||
gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1);
|
||||
break;
|
||||
case 3: /* ltr */
|
||||
if (s->cpl != 0) {
|
||||
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
|
||||
} else {
|
||||
gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0);
|
||||
gen_op_jmp_im(pc_start - s->cs_base);
|
||||
gen_op_ltr_T0();
|
||||
}
|
||||
break;
|
||||
case 4: /* verr */
|
||||
case 5: /* verw */
|
||||
default:
|
||||
goto illegal_op;
|
||||
}
|
||||
break;
|
||||
case 0x101:
|
||||
modrm = ldub(s->pc++);
|
||||
mod = (modrm >> 6) & 3;
|
||||
op = (modrm >> 3) & 7;
|
||||
switch(op) {
|
||||
case 0: /* sgdt */
|
||||
case 1: /* sidt */
|
||||
if (mod == 3)
|
||||
goto illegal_op;
|
||||
gen_lea_modrm(s, modrm, ®_addr, &offset_addr);
|
||||
if (op == 0)
|
||||
gen_op_movl_T0_env(offsetof(CPUX86State,gdt.limit));
|
||||
else
|
||||
gen_op_movl_T0_env(offsetof(CPUX86State,idt.limit));
|
||||
gen_op_stw_T0_A0();
|
||||
gen_op_addl_A0_im(2);
|
||||
if (op == 0)
|
||||
gen_op_movl_T0_env(offsetof(CPUX86State,gdt.base));
|
||||
else
|
||||
gen_op_movl_T0_env(offsetof(CPUX86State,idt.base));
|
||||
if (!s->dflag)
|
||||
gen_op_andl_T0_im(0xffffff);
|
||||
gen_op_stl_T0_A0();
|
||||
break;
|
||||
case 2: /* lgdt */
|
||||
case 3: /* lidt */
|
||||
if (mod == 3)
|
||||
goto illegal_op;
|
||||
if (s->cpl != 0) {
|
||||
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
|
||||
} else {
|
||||
gen_lea_modrm(s, modrm, ®_addr, &offset_addr);
|
||||
gen_op_lduw_T1_A0();
|
||||
gen_op_addl_A0_im(2);
|
||||
gen_op_ldl_T0_A0();
|
||||
if (!s->dflag)
|
||||
gen_op_andl_T0_im(0xffffff);
|
||||
if (op == 2) {
|
||||
gen_op_movl_env_T0(offsetof(CPUX86State,gdt.base));
|
||||
gen_op_movl_env_T1(offsetof(CPUX86State,gdt.limit));
|
||||
} else {
|
||||
gen_op_movl_env_T0(offsetof(CPUX86State,idt.base));
|
||||
gen_op_movl_env_T1(offsetof(CPUX86State,idt.limit));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 4: /* smsw */
|
||||
gen_op_movl_T0_env(offsetof(CPUX86State,cr[0]));
|
||||
gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 1);
|
||||
break;
|
||||
case 6: /* lmsw */
|
||||
if (s->cpl != 0) {
|
||||
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
|
||||
} else {
|
||||
gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0);
|
||||
gen_op_lmsw_T0();
|
||||
}
|
||||
break;
|
||||
case 7: /* invlpg */
|
||||
if (s->cpl != 0) {
|
||||
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
|
||||
} else {
|
||||
if (mod == 3)
|
||||
goto illegal_op;
|
||||
gen_lea_modrm(s, modrm, ®_addr, &offset_addr);
|
||||
gen_op_invlpg_A0();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
goto illegal_op;
|
||||
}
|
||||
break;
|
||||
case 0x102: /* lar */
|
||||
case 0x103: /* lsl */
|
||||
@@ -3361,6 +3500,83 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
||||
s->cc_op = CC_OP_EFLAGS;
|
||||
gen_op_mov_reg_T1[ot][reg]();
|
||||
break;
|
||||
case 0x118:
|
||||
modrm = ldub(s->pc++);
|
||||
mod = (modrm >> 6) & 3;
|
||||
op = (modrm >> 3) & 7;
|
||||
switch(op) {
|
||||
case 0: /* prefetchnta */
|
||||
case 1: /* prefetchnt0 */
|
||||
case 2: /* prefetchnt0 */
|
||||
case 3: /* prefetchnt0 */
|
||||
if (mod == 3)
|
||||
goto illegal_op;
|
||||
gen_lea_modrm(s, modrm, ®_addr, &offset_addr);
|
||||
/* nothing more to do */
|
||||
break;
|
||||
default:
|
||||
goto illegal_op;
|
||||
}
|
||||
break;
|
||||
case 0x120: /* mov reg, crN */
|
||||
case 0x122: /* mov crN, reg */
|
||||
if (s->cpl != 0) {
|
||||
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
|
||||
} else {
|
||||
modrm = ldub(s->pc++);
|
||||
if ((modrm & 0xc0) != 0xc0)
|
||||
goto illegal_op;
|
||||
rm = modrm & 7;
|
||||
reg = (modrm >> 3) & 7;
|
||||
switch(reg) {
|
||||
case 0:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
if (b & 2) {
|
||||
gen_op_mov_TN_reg[OT_LONG][0][rm]();
|
||||
gen_op_movl_crN_T0(reg);
|
||||
s->is_jmp = 2;
|
||||
} else {
|
||||
gen_op_movl_T0_env(offsetof(CPUX86State,cr[reg]));
|
||||
gen_op_mov_reg_T0[OT_LONG][rm]();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
goto illegal_op;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x121: /* mov reg, drN */
|
||||
case 0x123: /* mov drN, reg */
|
||||
if (s->cpl != 0) {
|
||||
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
|
||||
} else {
|
||||
modrm = ldub(s->pc++);
|
||||
if ((modrm & 0xc0) != 0xc0)
|
||||
goto illegal_op;
|
||||
rm = modrm & 7;
|
||||
reg = (modrm >> 3) & 7;
|
||||
/* XXX: do it dynamically with CR4.DE bit */
|
||||
if (reg == 4 || reg == 5)
|
||||
goto illegal_op;
|
||||
if (b & 2) {
|
||||
gen_op_mov_TN_reg[OT_LONG][0][rm]();
|
||||
gen_op_movl_drN_T0(reg);
|
||||
s->is_jmp = 2;
|
||||
} else {
|
||||
gen_op_movl_T0_env(offsetof(CPUX86State,dr[reg]));
|
||||
gen_op_mov_reg_T0[OT_LONG][rm]();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x106: /* clts */
|
||||
if (s->cpl != 0) {
|
||||
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
|
||||
} else {
|
||||
gen_op_clts();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
goto illegal_op;
|
||||
}
|
||||
@@ -3666,7 +3882,7 @@ static void optimize_flags(uint16_t *opc_buf, int opc_buf_len)
|
||||
/* generate intermediate code in gen_opc_buf and gen_opparam_buf for
|
||||
basic block 'tb'. If search_pc is TRUE, also generate PC
|
||||
information for each intermediate instruction. */
|
||||
int gen_intermediate_code(TranslationBlock *tb, int search_pc)
|
||||
static inline int gen_intermediate_code_internal(TranslationBlock *tb, int search_pc)
|
||||
{
|
||||
DisasContext dc1, *dc = &dc1;
|
||||
uint8_t *pc_ptr;
|
||||
@@ -3692,7 +3908,8 @@ int gen_intermediate_code(TranslationBlock *tb, int search_pc)
|
||||
dc->cc_op = CC_OP_DYNAMIC;
|
||||
dc->cs_base = cs_base;
|
||||
dc->tb = tb;
|
||||
|
||||
dc->popl_esp_hack = 0;
|
||||
|
||||
gen_opc_ptr = gen_opc_buf;
|
||||
gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
|
||||
gen_opparam_ptr = gen_opparam_buf;
|
||||
@@ -3708,6 +3925,7 @@ int gen_intermediate_code(TranslationBlock *tb, int search_pc)
|
||||
while (lj < j)
|
||||
gen_opc_instr_start[lj++] = 0;
|
||||
gen_opc_pc[lj] = (uint32_t)pc_ptr;
|
||||
gen_opc_cc_op[lj] = dc->cc_op;
|
||||
gen_opc_instr_start[lj] = 1;
|
||||
}
|
||||
}
|
||||
@@ -3774,6 +3992,16 @@ int gen_intermediate_code(TranslationBlock *tb, int search_pc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gen_intermediate_code(TranslationBlock *tb)
|
||||
{
|
||||
return gen_intermediate_code_internal(tb, 0);
|
||||
}
|
||||
|
||||
int gen_intermediate_code_pc(TranslationBlock *tb)
|
||||
{
|
||||
return gen_intermediate_code_internal(tb, 1);
|
||||
}
|
||||
|
||||
CPUX86State *cpu_x86_init(void)
|
||||
{
|
||||
CPUX86State *env;
|
||||
@@ -3806,6 +4034,208 @@ void cpu_x86_close(CPUX86State *env)
|
||||
free(env);
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
/* x86 mmu */
|
||||
/* XXX: add PGE support */
|
||||
|
||||
/* called when cr3 or PG bit are modified */
|
||||
static int last_pg_state = -1;
|
||||
int phys_ram_size;
|
||||
int phys_ram_fd;
|
||||
uint8_t *phys_ram_base;
|
||||
|
||||
void cpu_x86_update_cr0(CPUX86State *env)
|
||||
{
|
||||
int pg_state;
|
||||
void *map_addr;
|
||||
|
||||
#ifdef DEBUG_MMU
|
||||
printf("CR0 update: CR0=0x%08x\n", env->cr[0]);
|
||||
#endif
|
||||
pg_state = env->cr[0] & CR0_PG_MASK;
|
||||
if (pg_state != last_pg_state) {
|
||||
if (!pg_state) {
|
||||
/* we map the physical memory at address 0 */
|
||||
|
||||
map_addr = mmap((void *)0, phys_ram_size, PROT_WRITE | PROT_READ,
|
||||
MAP_SHARED | MAP_FIXED, phys_ram_fd, 0);
|
||||
if (map_addr == MAP_FAILED) {
|
||||
fprintf(stderr,
|
||||
"Could not map physical memory at host address 0x%08x\n",
|
||||
0);
|
||||
exit(1);
|
||||
}
|
||||
page_set_flags(0, phys_ram_size,
|
||||
PAGE_VALID | PAGE_READ | PAGE_WRITE | PAGE_EXEC);
|
||||
} else {
|
||||
/* we unmap the physical memory */
|
||||
munmap((void *)0, phys_ram_size);
|
||||
page_set_flags(0, phys_ram_size, 0);
|
||||
}
|
||||
last_pg_state = pg_state;
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_x86_update_cr3(CPUX86State *env)
|
||||
{
|
||||
if (env->cr[0] & CR0_PG_MASK) {
|
||||
#ifdef DEBUG_MMU
|
||||
printf("CR3 update: CR3=%08x\n", env->cr[3]);
|
||||
#endif
|
||||
page_unmap();
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_x86_init_mmu(CPUX86State *env)
|
||||
{
|
||||
last_pg_state = -1;
|
||||
cpu_x86_update_cr0(env);
|
||||
}
|
||||
|
||||
/* XXX: also flush 4MB pages */
|
||||
void cpu_x86_flush_tlb(CPUX86State *env, uint32_t addr)
|
||||
{
|
||||
int flags;
|
||||
unsigned long virt_addr;
|
||||
|
||||
flags = page_get_flags(addr);
|
||||
if (flags & PAGE_VALID) {
|
||||
virt_addr = addr & ~0xfff;
|
||||
munmap((void *)virt_addr, 4096);
|
||||
page_set_flags(virt_addr, virt_addr + 4096, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* return value:
|
||||
-1 = cannot handle fault
|
||||
0 = nothing more to do
|
||||
1 = generate PF fault
|
||||
*/
|
||||
int cpu_x86_handle_mmu_fault(CPUX86State *env, uint32_t addr, int is_write)
|
||||
{
|
||||
uint8_t *pde_ptr, *pte_ptr;
|
||||
uint32_t pde, pte, virt_addr;
|
||||
int cpl, error_code, is_dirty, is_user, prot, page_size;
|
||||
void *map_addr;
|
||||
|
||||
cpl = env->segs[R_CS].selector & 3;
|
||||
is_user = (cpl == 3);
|
||||
|
||||
#ifdef DEBUG_MMU
|
||||
printf("MMU fault: addr=0x%08x w=%d u=%d eip=%08x\n",
|
||||
addr, is_write, is_user, env->eip);
|
||||
#endif
|
||||
|
||||
if (env->user_mode_only) {
|
||||
/* user mode only emulation */
|
||||
error_code = 0;
|
||||
goto do_fault;
|
||||
}
|
||||
|
||||
if (!(env->cr[0] & CR0_PG_MASK))
|
||||
return -1;
|
||||
|
||||
/* page directory entry */
|
||||
pde_ptr = phys_ram_base + ((env->cr[3] & ~0xfff) + ((addr >> 20) & ~3));
|
||||
pde = ldl(pde_ptr);
|
||||
if (!(pde & PG_PRESENT_MASK)) {
|
||||
error_code = 0;
|
||||
goto do_fault;
|
||||
}
|
||||
if (is_user) {
|
||||
if (!(pde & PG_USER_MASK))
|
||||
goto do_fault_protect;
|
||||
if (is_write && !(pde & PG_RW_MASK))
|
||||
goto do_fault_protect;
|
||||
} else {
|
||||
if ((env->cr[0] & CR0_WP_MASK) && (pde & PG_USER_MASK) &&
|
||||
is_write && !(pde & PG_RW_MASK))
|
||||
goto do_fault_protect;
|
||||
}
|
||||
/* if PSE bit is set, then we use a 4MB page */
|
||||
if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
|
||||
is_dirty = is_write && !(pde & PG_DIRTY_MASK);
|
||||
if (!(pde & PG_ACCESSED_MASK)) {
|
||||
pde |= PG_ACCESSED_MASK;
|
||||
if (is_dirty)
|
||||
pde |= PG_DIRTY_MASK;
|
||||
stl(pde_ptr, pde);
|
||||
}
|
||||
|
||||
pte = pde & ~0x003ff000; /* align to 4MB */
|
||||
page_size = 4096 * 1024;
|
||||
virt_addr = addr & ~0x003fffff;
|
||||
} else {
|
||||
if (!(pde & PG_ACCESSED_MASK)) {
|
||||
pde |= PG_ACCESSED_MASK;
|
||||
stl(pde_ptr, pde);
|
||||
}
|
||||
|
||||
/* page directory entry */
|
||||
pte_ptr = phys_ram_base + ((pde & ~0xfff) + ((addr >> 10) & 0xffc));
|
||||
pte = ldl(pte_ptr);
|
||||
if (!(pte & PG_PRESENT_MASK)) {
|
||||
error_code = 0;
|
||||
goto do_fault;
|
||||
}
|
||||
if (is_user) {
|
||||
if (!(pte & PG_USER_MASK))
|
||||
goto do_fault_protect;
|
||||
if (is_write && !(pte & PG_RW_MASK))
|
||||
goto do_fault_protect;
|
||||
} else {
|
||||
if ((env->cr[0] & CR0_WP_MASK) && (pte & PG_USER_MASK) &&
|
||||
is_write && !(pte & PG_RW_MASK))
|
||||
goto do_fault_protect;
|
||||
}
|
||||
is_dirty = is_write && !(pte & PG_DIRTY_MASK);
|
||||
if (!(pte & PG_ACCESSED_MASK) || is_dirty) {
|
||||
pte |= PG_ACCESSED_MASK;
|
||||
if (is_dirty)
|
||||
pte |= PG_DIRTY_MASK;
|
||||
stl(pte_ptr, pte);
|
||||
}
|
||||
page_size = 4096;
|
||||
virt_addr = addr & ~0xfff;
|
||||
}
|
||||
/* the page can be put in the TLB */
|
||||
prot = PROT_READ;
|
||||
if (is_user) {
|
||||
if (pte & PG_RW_MASK)
|
||||
prot |= PROT_WRITE;
|
||||
} else {
|
||||
if (!(env->cr[0] & CR0_WP_MASK) || !(pte & PG_USER_MASK) ||
|
||||
(pte & PG_RW_MASK))
|
||||
prot |= PROT_WRITE;
|
||||
}
|
||||
map_addr = mmap((void *)virt_addr, page_size, prot,
|
||||
MAP_SHARED | MAP_FIXED, phys_ram_fd, pte & ~0xfff);
|
||||
if (map_addr == MAP_FAILED) {
|
||||
fprintf(stderr,
|
||||
"mmap failed when mapped physical address 0x%08x to virtual address 0x%08x\n",
|
||||
pte & ~0xfff, virt_addr);
|
||||
exit(1);
|
||||
}
|
||||
page_set_flags(virt_addr, virt_addr + page_size,
|
||||
PAGE_VALID | PAGE_EXEC | prot);
|
||||
#ifdef DEBUG_MMU
|
||||
printf("mmaping 0x%08x to virt 0x%08x pse=%d\n",
|
||||
pte & ~0xfff, virt_addr, (page_size != 4096));
|
||||
#endif
|
||||
return 0;
|
||||
do_fault_protect:
|
||||
error_code = PG_ERROR_P_MASK;
|
||||
do_fault:
|
||||
env->cr[2] = addr;
|
||||
env->error_code = (is_write << PG_ERROR_W_BIT) | error_code;
|
||||
if (is_user)
|
||||
env->error_code |= PG_ERROR_U_MASK;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
/* x86 debug */
|
||||
|
||||
static const char *cc_op_str[] = {
|
||||
"DYNAMIC",
|
||||
"EFLAGS",
|
||||
@@ -3859,12 +4289,12 @@ void cpu_x86_dump_state(CPUX86State *env, FILE *f, int flags)
|
||||
eflags & CC_P ? 'P' : '-',
|
||||
eflags & CC_C ? 'C' : '-');
|
||||
fprintf(f, "CS=%04x SS=%04x DS=%04x ES=%04x FS=%04x GS=%04x\n",
|
||||
env->segs[R_CS],
|
||||
env->segs[R_SS],
|
||||
env->segs[R_DS],
|
||||
env->segs[R_ES],
|
||||
env->segs[R_FS],
|
||||
env->segs[R_GS]);
|
||||
env->segs[R_CS].selector,
|
||||
env->segs[R_SS].selector,
|
||||
env->segs[R_DS].selector,
|
||||
env->segs[R_ES].selector,
|
||||
env->segs[R_FS].selector,
|
||||
env->segs[R_GS].selector);
|
||||
if (flags & X86_DUMP_CCOP) {
|
||||
if ((unsigned)env->cc_op < CC_OP_NB)
|
||||
strcpy(cc_op_name, cc_op_str[env->cc_op]);
|
||||
|
57
translate.c
57
translate.c
@@ -24,37 +24,55 @@
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define IN_OP_I386
|
||||
#include "cpu-" TARGET_ARCH ".h"
|
||||
#if defined(TARGET_I386)
|
||||
#include "cpu-i386.h"
|
||||
#define OPC_CPU_H "opc-i386.h"
|
||||
#elif defined(TARGET_ARM)
|
||||
#include "cpu-arm.h"
|
||||
#define OPC_CPU_H "opc-arm.h"
|
||||
#else
|
||||
#error unsupported target CPU
|
||||
#endif
|
||||
|
||||
#include "exec.h"
|
||||
#include "disas.h"
|
||||
|
||||
enum {
|
||||
#define DEF(s, n, copy_size) INDEX_op_ ## s,
|
||||
#include "opc-" TARGET_ARCH ".h"
|
||||
#include OPC_CPU_H
|
||||
#undef DEF
|
||||
NB_OPS,
|
||||
};
|
||||
|
||||
#include "dyngen.h"
|
||||
#include "op-" TARGET_ARCH ".h"
|
||||
#if defined(TARGET_I386)
|
||||
#include "op-i386.h"
|
||||
#elif defined(TARGET_ARM)
|
||||
#include "op-arm.h"
|
||||
#else
|
||||
#error unsupported target CPU
|
||||
#endif
|
||||
|
||||
uint16_t gen_opc_buf[OPC_BUF_SIZE];
|
||||
uint32_t gen_opparam_buf[OPPARAM_BUF_SIZE];
|
||||
uint32_t gen_opc_pc[OPC_BUF_SIZE];
|
||||
uint8_t gen_opc_instr_start[OPC_BUF_SIZE];
|
||||
|
||||
#if defined(TARGET_I386)
|
||||
uint8_t gen_opc_cc_op[OPC_BUF_SIZE];
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_DISAS
|
||||
static const char *op_str[] = {
|
||||
#define DEF(s, n, copy_size) #s,
|
||||
#include "opc-" TARGET_ARCH ".h"
|
||||
#include OPC_CPU_H
|
||||
#undef DEF
|
||||
};
|
||||
|
||||
static uint8_t op_nb_args[] = {
|
||||
#define DEF(s, n, copy_size) n,
|
||||
#include "opc-" TARGET_ARCH ".h"
|
||||
#include OPC_CPU_H
|
||||
#undef DEF
|
||||
};
|
||||
|
||||
@@ -95,7 +113,7 @@ int cpu_gen_code(TranslationBlock *tb,
|
||||
uint8_t *gen_code_buf;
|
||||
int gen_code_size;
|
||||
|
||||
if (gen_intermediate_code(tb, 0) < 0)
|
||||
if (gen_intermediate_code(tb) < 0)
|
||||
return -1;
|
||||
|
||||
/* generate machine code */
|
||||
@@ -123,22 +141,20 @@ int cpu_gen_code(TranslationBlock *tb,
|
||||
|
||||
static const unsigned short opc_copy_size[] = {
|
||||
#define DEF(s, n, copy_size) copy_size,
|
||||
#include "opc-" TARGET_ARCH ".h"
|
||||
#include OPC_CPU_H
|
||||
#undef DEF
|
||||
};
|
||||
|
||||
/* The simulated PC corresponding to
|
||||
'searched_pc' in the generated code is searched. 0 is returned if
|
||||
found. *found_pc contains the found PC.
|
||||
/* The cpu state corresponding to 'searched_pc' is restored.
|
||||
*/
|
||||
int cpu_search_pc(TranslationBlock *tb,
|
||||
uint32_t *found_pc, unsigned long searched_pc)
|
||||
int cpu_restore_state(TranslationBlock *tb,
|
||||
CPUState *env, unsigned long searched_pc)
|
||||
{
|
||||
int j, c;
|
||||
unsigned long tc_ptr;
|
||||
uint16_t *opc_ptr;
|
||||
|
||||
if (gen_intermediate_code(tb, 1) < 0)
|
||||
if (gen_intermediate_code_pc(tb) < 0)
|
||||
return -1;
|
||||
|
||||
/* find opc index corresponding to search_pc */
|
||||
@@ -160,7 +176,18 @@ int cpu_search_pc(TranslationBlock *tb,
|
||||
/* now find start of instruction before */
|
||||
while (gen_opc_instr_start[j] == 0)
|
||||
j--;
|
||||
*found_pc = gen_opc_pc[j];
|
||||
#if defined(TARGET_I386)
|
||||
{
|
||||
int cc_op;
|
||||
|
||||
env->eip = gen_opc_pc[j] - tb->cs_base;
|
||||
cc_op = gen_opc_cc_op[j];
|
||||
if (cc_op != CC_OP_DYNAMIC)
|
||||
env->cc_op = cc_op;
|
||||
}
|
||||
#elif defined(TARGET_ARM)
|
||||
env->regs[15] = gen_opc_pc[j];
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user