Compare commits

...

22 Commits

Author SHA1 Message Date
bellard
fd429f2f6c update
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@66 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-30 20:59:46 +00:00
bellard
fb3e5849bb s390 support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@65 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-29 17:32:36 +00:00
bellard
7854b05654 endian fixes by Ulrich weigand
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@64 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-29 17:22:23 +00:00
bellard
500dab07e8 update
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@63 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-29 16:58:09 +00:00
bellard
f6630e791b version
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@62 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-29 16:57:48 +00:00
bellard
168485b75b wine help
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@61 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-29 16:57:34 +00:00
bellard
5cd4393b14 first vm86 support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@60 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-29 16:54:36 +00:00
bellard
7ed601b782 more syscalls
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@59 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-29 16:54:05 +00:00
bellard
d1f2367bc0 changed flag names
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@58 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-29 16:53:34 +00:00
bellard
851e67a1b4 primitive vm86 support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@57 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-29 16:53:14 +00:00
bellard
fc2b4c4879 eflags update
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@56 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-29 16:52:44 +00:00
bellard
9c605cb135 added cmpxchg8b, cpuid, bound, eflags support, vm86 mode, 16bit/override string ops
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@55 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-29 16:51:35 +00:00
bellard
24f9e90b0e 16bit/override support in string operations
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@54 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-29 16:50:40 +00:00
bellard
a4a0ffdb2b added cmpxchg8b, cpuid, bound, eflags support, vm86 mode
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@53 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-29 16:49:21 +00:00
bellard
0ea00c9a3c added number of arguments
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@52 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-29 16:47:34 +00:00
bellard
e1d4294a45 more tests
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@51 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-29 16:45:07 +00:00
bellard
c3c7c29246 added runcom
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@50 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-29 16:44:42 +00:00
bellard
31bb950be6 xchg lock, xlat instr
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@49 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-26 22:33:47 +00:00
bellard
8083a3e508 dirent fixes
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@48 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-24 23:12:16 +00:00
bellard
644c433cb3 ld.so load fix
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@47 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-24 23:00:36 +00:00
bellard
d691f66983 glibc2.2 fixes - more command line options - misc doc fixes
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@46 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-24 21:58:34 +00:00
bellard
386405f786 documentation
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@45 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-23 21:28:45 +00:00
26 changed files with 2888 additions and 1124 deletions

View File

@@ -1,3 +1,9 @@
version 0.1.1:
- glibc 2.2 compilation fixes
- added -s and -L options
- binary distribution of x86 glibc and wine
version 0.1:
- initial public release.

View File

@@ -19,6 +19,10 @@ ifeq ($(ARCH),ppc)
OP_CFLAGS=$(CFLAGS)
endif
ifeq ($(ARCH),s390)
OP_CFLAGS=$(CFLAGS)
endif
ifeq ($(GCC_MAJOR),3)
# very important to generate a return at the end of every operation
OP_CFLAGS+=-fno-reorder-blocks -fno-optimize-sibling-calls
@@ -94,19 +98,20 @@ qemu-doc.html: qemu-doc.texi
texi2html -monolithic -number $<
FILES= \
README COPYING COPYING.LIB TODO Changelog VERSION \
dyngen.c ioctls.h ops_template.h syscall_types.h\
README README.distrib COPYING COPYING.LIB TODO Changelog VERSION \
dyngen.c ioctls.h ops_template.h op_string.h syscall_types.h\
Makefile elf.h linux_bin.h segment.h thunk.c\
elfload.c main.c signal.c thunk.h\
cpu-i386.h qemu.h op-i386.c opc-i386.h syscall-i386.h translate-i386.c\
cpu-i386.h qemu.h op-i386.c opc-i386.h syscall-i386.h translate-i386.c\
dis-asm.h gen-i386.h op-i386.h syscall.c\
dis-buf.c i386-dis.c opreg_template.h syscall_defs.h\
i386.ld ppc.ld exec-i386.h exec-i386.c configure \
i386.ld ppc.ld s390.ld exec-i386.h exec-i386.c configure \
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/hello.c tests/hello tests/sha1.c \
tests/testsig.c tests/testclone.c tests/testthread.c \
tests/runcom.c tests/pi_10.com \
qemu-doc.texi qemu-doc.html
FILE=qemu-$(VERSION)
@@ -118,6 +123,15 @@ tar:
( cd /tmp ; tar zcvf ~/$(FILE).tar.gz $(FILE) )
rm -rf /tmp/$(FILE)
# generate a binary distribution including the test binary environnment
BINPATH=/usr/local/qemu-i386
tarbin:
tar zcvf /tmp/qemu-i386-glibc21.tar.gz \
$(BINPATH)/etc $(BINPATH)/lib $(BINPATH)/bin
tar zcvf /tmp/qemu-i386-wine.tar.gz \
$(BINPATH)/X11R6 $(BINPATH)/wine
ifneq ($(wildcard .depend),)
include .depend
endif

20
README
View File

@@ -15,8 +15,26 @@ Type
make install
to install qemu in /usr/local/bin
to install QEMU in /usr/local/bin
* On x86 you should be able to launch any program by using the
libraries installed on your PC. For example:
./qemu -L / /bin/ls
* On non x86 CPUs, you need first to download at least an x86 glibc
(qemu-i386-glibc21.tar.gz on the qemu web page). Ensure that
LD_LIBRARY_PATH is not set:
unset LD_LIBRARY_PATH
Then you can launch the precompiled 'ls' x86 executable:
./qemu /usr/local/qemu-i386/bin/ls-i386
You can look at /usr/local/qemu-i386/bin/qemu-conf.sh so that QEMU is
automatically launched by the Linux kernel when you try to launch x86
executables.
Documentation
-------------

View File

@@ -1 +1 @@
0.1
0.1.4

7
configure vendored
View File

@@ -42,6 +42,9 @@ case "$cpu" in
mips)
cpu="mips"
;;
s390)
cpu="s390"
;;
*)
cpu="unknown"
;;
@@ -137,7 +140,7 @@ fi
else
# if cross compiling, cannot launch a program, so make a static guess
if test "$cpu" = "powerpc" -o "$cpu" = "mips" ; then
if test "$cpu" = "powerpc" -o "$cpu" = "mips" -o "$cpu" = "s390" ; then
bigendian="yes"
fi
@@ -212,6 +215,8 @@ elif test "$cpu" = "powerpc" ; then
echo "ARCH=ppc" >> config.mak
elif test "$cpu" = "mips" ; then
echo "ARCH=mips" >> config.mak
elif test "$cpu" = "s390" ; then
echo "ARCH=s390" >> config.mak
else
echo "Unsupported CPU"
exit 1

View File

@@ -48,6 +48,7 @@
#define R_FS 4
#define R_GS 5
/* eflags masks */
#define CC_C 0x0001
#define CC_P 0x0004
#define CC_A 0x0010
@@ -55,15 +56,17 @@
#define CC_S 0x0080
#define CC_O 0x0800
#define TRAP_FLAG 0x0100
#define INTERRUPT_FLAG 0x0200
#define DIRECTION_FLAG 0x0400
#define IOPL_FLAG_MASK 0x3000
#define NESTED_FLAG 0x4000
#define BYTE_FL 0x8000 /* Intel reserved! */
#define RF_FLAG 0x10000
#define VM_FLAG 0x20000
/* AC 0x40000 */
#define TF_MASK 0x00000100
#define IF_MASK 0x00000200
#define DF_MASK 0x00000400
#define IOPL_MASK 0x00003000
#define NT_MASK 0x00004000
#define RF_MASK 0x00010000
#define VM_MASK 0x00020000
#define AC_MASK 0x00040000
#define VIF_MASK 0x00080000
#define VIP_MASK 0x00100000
#define ID_MASK 0x00200000
#define EXCP00_DIVZ 1
#define EXCP01_SSTP 2
@@ -158,7 +161,9 @@ typedef struct CPUX86State {
/* standard registers */
uint32_t regs[8];
uint32_t eip;
uint32_t eflags;
uint32_t eflags; /* eflags register. During CPU emulation, CC
flags and DF are set to zero because they are
store elsewhere */
/* emulator internal eflags handling */
uint32_t cc_src;
@@ -183,13 +188,13 @@ typedef struct CPUX86State {
SegmentDescriptorTable ldt;
SegmentDescriptorTable idt;
/* various CPU modes */
int vm86;
/* exception/interrupt handling */
jmp_buf jmp_env;
int exception_index;
int interrupt_request;
/* user data */
void *opaque;
} CPUX86State;
/* all CPU memory access use these macros */
@@ -406,7 +411,7 @@ void cpu_x86_close(CPUX86State *s);
/* needed to load some predefinied segment registers */
void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector);
/* you can call these signal handler from you SIGBUS and SIGSEGV
/* you can call this signal handler from your SIGBUS and SIGSEGV
signal handlers to inform the virtual CPU of exceptions. non zero
is returned if the signal was handled by the virtual CPU. */
struct siginfo;
@@ -418,7 +423,8 @@ int cpu_x86_signal_handler(int host_signum, struct siginfo *info,
#define GEN_FLAG_CODE32_SHIFT 0
#define GEN_FLAG_ADDSEG_SHIFT 1
#define GEN_FLAG_SS32_SHIFT 2
#define GEN_FLAG_ST_SHIFT 3
#define GEN_FLAG_VM_SHIFT 3
#define GEN_FLAG_ST_SHIFT 4
int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size,
int *gen_code_size_ptr,

View File

@@ -28,6 +28,15 @@
#include "thunk.h"
/* temporary fix to make it compile with old elf headers (XXX: use
included elf.h in all cases) */
#ifndef EM_390
#define EM_S390 22 /* IBM S390 */
#define R_390_8 1 /* Direct 8 bit. */
#define R_390_16 3 /* Direct 16 bit. */
#define R_390_32 4 /* Direct 32 bit. */
#endif
/* all dynamically generated functions begin with this code */
#define OP_PREFIX "op_"
@@ -236,6 +245,17 @@ void gen_code(const char *name, unsigned long offset, unsigned long size,
copy_size = p - p_start;
}
break;
case EM_S390:
{
uint8_t *p;
p = (void *)(p_end - 2);
if (p == p_start)
error("empty code for %s", name);
if (get16((uint16_t *)p) != 0x07fe && get16((uint16_t *)p) != 0x07f4)
error("br %r14 expected at the end of %s", name);
copy_size = p - p_start;
}
break;
default:
error("unsupported CPU (%d)", e_machine);
}
@@ -282,7 +302,9 @@ void gen_code(const char *name, unsigned long offset, unsigned long size,
error("inconsistent argument numbering in %s", name);
}
if (gen_switch) {
if (gen_switch == 2) {
fprintf(outfile, "DEF(%s, %d)\n", name + 3, nb_args);
} else if (gen_switch == 1) {
/* output C code */
fprintf(outfile, "case INDEX_%s: {\n", name);
@@ -403,6 +425,42 @@ void gen_code(const char *name, unsigned long offset, unsigned long size,
}
}
break;
case EM_S390:
{
Elf32_Rela *rel;
char name[256];
int type;
long addend;
for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
if (rel->r_offset >= offset && rel->r_offset < offset + copy_size) {
sym_name = strtab + symtab[ELF32_R_SYM(rel->r_info)].st_name;
if (strstart(sym_name, "__op_param", &p)) {
snprintf(name, sizeof(name), "param%s", p);
} else {
snprintf(name, sizeof(name), "(long)(&%s)", sym_name);
}
type = ELF32_R_TYPE(rel->r_info);
addend = rel->r_addend;
switch(type) {
case R_390_32:
fprintf(outfile, " *(uint32_t *)(gen_code_ptr + %ld) = %s + %ld;\n",
rel->r_offset - offset, name, addend);
break;
case R_390_16:
fprintf(outfile, " *(uint16_t *)(gen_code_ptr + %ld) = %s + %ld;\n",
rel->r_offset - offset, name, addend);
break;
case R_390_8:
fprintf(outfile, " *(uint8_t *)(gen_code_ptr + %ld) = %s + %ld;\n",
rel->r_offset - offset, name, addend);
break;
default:
error("unsupported s390 relocation (%d)", type);
}
}
}
}
break;
default:
error("unsupported CPU for relocations (%d)", e_machine);
}
@@ -554,17 +612,21 @@ int load_elf(const char *filename, FILE *outfile, int do_print_enum)
case EM_SPARC:
cpu_name = "sparc";
break;
case EM_S390:
cpu_name = "s390";
break;
default:
error("unsupported CPU (e_machine=%d)", e_machine);
}
if (do_print_enum) {
fprintf(outfile, "DEF(end)\n");
fprintf(outfile, "DEF(end, 0)\n");
for(i = 0, sym = symtab; i < nb_syms; i++, sym++) {
const char *name, *p;
name = strtab + sym->st_name;
if (strstart(name, OP_PREFIX, &p)) {
fprintf(outfile, "DEF(%s)\n", p);
gen_code(name, sym->st_value, sym->st_size, outfile,
text, relocs, nb_relocs, reloc_sh_type, symtab, strtab, 2);
}
}
} else {
@@ -614,6 +676,9 @@ fprintf(outfile,
case EM_PPC:
fprintf(outfile, "*((uint32_t *)gen_code_ptr)++ = 0x4e800020; /* blr */\n");
break;
case EM_S390:
fprintf(outfile, "*((uint16_t *)gen_code_ptr)++ = 0x07fe; /* br %%r14 */\n");
break;
default:
error("no return generation for cpu '%s'", cpu_name);
}

View File

@@ -87,6 +87,20 @@ static inline int testandset (int *p)
}
#endif
#ifdef __s390__
static inline int testandset (int *p)
{
int ret;
__asm__ __volatile__ ("0: cs %0,%1,0(%2)\n"
" jl 0b"
: "=&d" (ret)
: "r" (1), "a" (p), "0" (*p)
: "cc", "memory" );
return ret;
}
#endif
int global_cpu_lock = 0;
void cpu_lock(void)
@@ -330,9 +344,10 @@ int cpu_x86_exec(CPUX86State *env1)
#endif
/* put eflags in CPU temporary format */
T0 = env->eflags;
op_movl_eflags_T0();
CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
DF = 1 - (2 * ((env->eflags >> 10) & 1));
CC_OP = CC_OP_EFLAGS;
env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
env->interrupt_request = 0;
/* prepare setjmp context for exception handling */
@@ -354,6 +369,7 @@ int cpu_x86_exec(CPUX86State *env1)
(unsigned long)env->seg_cache[R_ES].base |
(unsigned long)env->seg_cache[R_SS].base) != 0) <<
GEN_FLAG_ADDSEG_SHIFT;
flags |= (env->eflags & VM_MASK) >> (17 - GEN_FLAG_VM_SHIFT);
cs_base = env->seg_cache[R_CS].base;
pc = cs_base + env->eip;
tb = tb_find(&ptb, (unsigned long)pc, (unsigned long)cs_base,
@@ -390,8 +406,7 @@ int cpu_x86_exec(CPUX86State *env1)
ret = env->exception_index;
/* restore flags in standard format */
op_movl_T0_eflags();
env->eflags = T0;
env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK);
/* restore global registers */
#ifdef reg_EAX
@@ -485,7 +500,11 @@ int cpu_x86_signal_handler(int host_signum, struct siginfo *info,
unsigned long pc;
sigset_t *pold_set;
pc = uc->uc_mcontext.gregs[EIP];
#ifndef REG_EIP
/* for glibc 2.1 */
#define REG_EIP EIP
#endif
pc = uc->uc_mcontext.gregs[REG_EIP];
pold_set = &uc->uc_sigmask;
return handle_cpu_signal(pc, pold_set);
#else

View File

@@ -93,6 +93,12 @@ register unsigned int T1 asm("l1");
register unsigned int A0 asm("l2");
register struct CPUX86State *env asm("l3");
#endif
#ifdef __s390__
register unsigned int T0 asm("r7");
register unsigned int T1 asm("r8");
register unsigned int A0 asm("r9");
register struct CPUX86State *env asm("r10");
#endif
/* force GCC to generate only one epilog at the end of the function */
#define FORCE_RET() asm volatile ("");

View File

@@ -42,8 +42,7 @@
#define DLINFO_ITEMS 12
/* Where we find X86 libraries... */
//#define X86_DEFAULT_LIB_DIR "/usr/x86/"
#define X86_DEFAULT_LIB_DIR "/"
//extern void * mmap4k();
#define mmap4k(a, b, c, d, e, f) mmap((void *)(a), b, c, d, e, f)
@@ -361,9 +360,6 @@ static unsigned int * create_elf_tables(char *p, int argc, int envc,
put_user (tswapl(val), dlinfo++)
if (exec) { /* Put this here for an ELF program interpreter */
struct elf_phdr * eppnt;
eppnt = (struct elf_phdr *)((unsigned long)exec->e_phoff);
NEW_AUX_ENT (AT_PHDR, (unsigned int)(load_addr + exec->e_phoff));
NEW_AUX_ENT (AT_PHENT, (unsigned int)(sizeof (struct elf_phdr)));
NEW_AUX_ENT (AT_PHNUM, (unsigned int)(exec->e_phnum));
@@ -419,6 +415,9 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
*/
load_addr = INTERP_LOADADDR;
#ifdef BSWAP_NEEDED
bswap_ehdr(interp_elf_ex);
#endif
/* First of all, some simple consistency checks */
if ((interp_elf_ex->e_type != ET_EXEC &&
interp_elf_ex->e_type != ET_DYN) ||
@@ -426,6 +425,7 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
return ~0UL;
}
/* Now read in all of the header information */
if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > X86_PAGE_SIZE)
@@ -453,7 +453,6 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
(char *) elf_phdata,
sizeof(struct elf_phdr) * interp_elf_ex->e_phnum);
}
if (retval < 0) {
perror("load_elf_interp");
exit(-1);
@@ -638,7 +637,8 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r
* is an a.out format binary
*/
elf_interpreter = (char *)malloc(elf_ppnt->p_filesz+strlen(X86_DEFAULT_LIB_DIR));
elf_interpreter = (char *)malloc(elf_ppnt->p_filesz+
strlen(bprm->interp_prefix));
if (elf_interpreter == NULL) {
free (elf_phdata);
@@ -646,11 +646,11 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r
return -ENOMEM;
}
strcpy(elf_interpreter, X86_DEFAULT_LIB_DIR);
strcpy(elf_interpreter, bprm->interp_prefix);
retval = lseek(bprm->fd, elf_ppnt->p_offset, SEEK_SET);
if(retval >= 0) {
retval = read(bprm->fd,
elf_interpreter+strlen(X86_DEFAULT_LIB_DIR),
elf_interpreter+strlen(bprm->interp_prefix),
elf_ppnt->p_filesz);
}
if(retval < 0) {
@@ -911,7 +911,8 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r
int elf_exec(const char * filename, char ** argv, char ** envp,
int elf_exec(const char *interp_prefix,
const char * filename, char ** argv, char ** envp,
struct target_pt_regs * regs, struct image_info *infop)
{
struct linux_binprm bprm;
@@ -930,6 +931,7 @@ int elf_exec(const char * filename, char ** argv, char ** envp,
else {
bprm.fd = retval;
}
bprm.interp_prefix = (char *)interp_prefix;
bprm.filename = (char *)filename;
bprm.sh_bang = 0;
bprm.loader = 0;

View File

@@ -32,6 +32,7 @@
FILE *logfile = NULL;
int loglevel;
const char *interp_prefix = CONFIG_QEMU_PREFIX "/qemu-i386";
/* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
we allocate a bigger stack. Need a better solution, for example
@@ -103,35 +104,99 @@ void write_dt(void *ptr, unsigned long addr, unsigned long limit,
uint64_t gdt_table[6];
//#define DEBUG_VM86
void cpu_loop(struct CPUX86State *env)
{
int err;
uint8_t *pc;
target_siginfo_t info;
for(;;) {
err = cpu_x86_exec(env);
pc = env->seg_cache[R_CS].base + env->eip;
switch(err) {
case EXCP0D_GPF:
if (pc[0] == 0xcd && pc[1] == 0x80) {
/* syscall */
env->eip += 2;
env->regs[R_EAX] = do_syscall(env,
env->regs[R_EAX],
env->regs[R_EBX],
env->regs[R_ECX],
env->regs[R_EDX],
env->regs[R_ESI],
env->regs[R_EDI],
env->regs[R_EBP]);
if (env->eflags & VM_MASK) {
TaskState *ts;
int ret;
#ifdef DEBUG_VM86
printf("VM86 exception %04x:%08x %02x\n",
env->segs[R_CS], env->eip, pc[0]);
#endif
/* VM86 mode */
ts = env->opaque;
/* XXX: add all cases */
switch(pc[0]) {
case 0xcd: /* int */
env->eip += 2;
ret = TARGET_VM86_INTx | (pc[1] << 8);
break;
default:
/* real VM86 GPF exception */
ret = TARGET_VM86_UNKNOWN;
break;
}
#ifdef DEBUG_VM86
printf("ret=0x%x\n", ret);
#endif
/* put the VM86 registers in the userspace register structure */
ts->target_v86->regs.eax = tswap32(env->regs[R_EAX]);
ts->target_v86->regs.ebx = tswap32(env->regs[R_EBX]);
ts->target_v86->regs.ecx = tswap32(env->regs[R_ECX]);
ts->target_v86->regs.edx = tswap32(env->regs[R_EDX]);
ts->target_v86->regs.esi = tswap32(env->regs[R_ESI]);
ts->target_v86->regs.edi = tswap32(env->regs[R_EDI]);
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]);
/* restore 32 bit registers */
env->regs[R_EBX] = ts->vm86_saved_regs.ebx;
env->regs[R_ECX] = ts->vm86_saved_regs.ecx;
env->regs[R_EDX] = ts->vm86_saved_regs.edx;
env->regs[R_ESI] = ts->vm86_saved_regs.esi;
env->regs[R_EDI] = ts->vm86_saved_regs.edi;
env->regs[R_EBP] = ts->vm86_saved_regs.ebp;
env->regs[R_ESP] = ts->vm86_saved_regs.esp;
env->eflags = ts->vm86_saved_regs.eflags;
env->eip = ts->vm86_saved_regs.eip;
cpu_x86_load_seg(env, R_CS, ts->vm86_saved_regs.cs);
cpu_x86_load_seg(env, R_SS, ts->vm86_saved_regs.ss);
cpu_x86_load_seg(env, R_DS, ts->vm86_saved_regs.ds);
cpu_x86_load_seg(env, R_ES, ts->vm86_saved_regs.es);
cpu_x86_load_seg(env, R_FS, ts->vm86_saved_regs.fs);
cpu_x86_load_seg(env, R_GS, ts->vm86_saved_regs.gs);
env->regs[R_EAX] = ret;
} else {
/* XXX: more precise info */
info.si_signo = SIGSEGV;
info.si_errno = 0;
info.si_code = 0;
info._sifields._sigfault._addr = 0;
queue_signal(info.si_signo, &info);
if (pc[0] == 0xcd && pc[1] == 0x80) {
/* syscall */
env->eip += 2;
env->regs[R_EAX] = do_syscall(env,
env->regs[R_EAX],
env->regs[R_EBX],
env->regs[R_ECX],
env->regs[R_EDX],
env->regs[R_ESI],
env->regs[R_EDI],
env->regs[R_EBP]);
} else {
/* XXX: more precise info */
info.si_signo = SIGSEGV;
info.si_errno = 0;
info.si_code = 0;
info._sifields._sigfault._addr = 0;
queue_signal(info.si_signo, &info);
}
}
break;
case EXCP00_DIVZ:
@@ -172,31 +237,67 @@ void cpu_loop(struct CPUX86State *env)
void usage(void)
{
printf("qemu version " QEMU_VERSION ", Copyright (c) 2003 Fabrice Bellard\n"
"usage: qemu [-d] program [arguments...]\n"
"usage: qemu [-h] [-d] [-L path] [-s size] program [arguments...]\n"
"Linux x86 emulator\n"
);
"\n"
"-h print this help\n"
"-d activate log (logfile=%s)\n"
"-L path set the x86 elf interpreter prefix (default=%s)\n"
"-s size set the x86 stack size in bytes (default=%ld)\n",
DEBUG_LOGFILE,
interp_prefix,
x86_stack_size);
exit(1);
}
/* XXX: currently only used for async signals (see signal.c) */
CPUX86State *global_env;
/* used to free thread contexts */
TaskState *first_task_state;
int main(int argc, char **argv)
{
const char *filename;
struct target_pt_regs regs1, *regs = &regs1;
struct image_info info1, *info = &info1;
TaskState ts1, *ts = &ts1;
CPUX86State *env;
int optind;
const char *r;
if (argc <= 1)
usage();
loglevel = 0;
optind = 1;
if (argv[optind] && !strcmp(argv[optind], "-d")) {
loglevel = 1;
for(;;) {
if (optind >= argc)
break;
r = argv[optind];
if (r[0] != '-')
break;
optind++;
r++;
if (!strcmp(r, "-")) {
break;
} else if (!strcmp(r, "d")) {
loglevel = 1;
} else if (!strcmp(r, "s")) {
r = argv[optind++];
x86_stack_size = strtol(r, (char **)&r, 0);
if (x86_stack_size <= 0)
usage();
if (*r == 'M')
x86_stack_size *= 1024 * 1024;
else if (*r == 'k' || *r == 'K')
x86_stack_size *= 1024;
} else if (!strcmp(r, "L")) {
interp_prefix = argv[optind++];
} else {
usage();
}
}
if (optind >= argc)
usage();
filename = argv[optind];
/* init debug */
@@ -215,7 +316,7 @@ int main(int argc, char **argv)
/* Zero out image_info */
memset(info, 0, sizeof(struct image_info));
if(elf_exec(filename, argv+optind, environ, regs, info) != 0) {
if(elf_exec(interp_prefix, filename, argv+optind, environ, regs, info) != 0) {
printf("Error loading %s\n", filename);
exit(1);
}
@@ -238,6 +339,11 @@ int main(int argc, char **argv)
env = cpu_x86_init();
global_env = env;
/* build Task State */
memset(ts, 0, sizeof(TaskState));
env->opaque = ts;
ts->used = 1;
/* linux register setup */
env->regs[R_EAX] = regs->eax;
env->regs[R_EBX] = regs->ebx;

View File

@@ -33,7 +33,35 @@ struct image_info {
int personality;
};
int elf_exec(const char * filename, char ** argv, char ** envp,
/* Information about the current linux thread */
struct vm86_saved_state {
uint32_t eax; /* return code */
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
uint32_t esi;
uint32_t edi;
uint32_t ebp;
uint32_t esp;
uint32_t eflags;
uint32_t eip;
uint16_t cs, ss, ds, es, fs, gs;
};
/* NOTE: we force a big alignment so that the stack stored after is
aligned too */
typedef struct TaskState {
struct TaskState *next;
struct target_vm86plus_struct *target_v86;
struct vm86_saved_state vm86_saved_regs;
int used; /* non zero if used */
uint8_t stack[0];
} __attribute__((aligned(16))) TaskState;
extern TaskState *first_task_state;
int elf_exec(const char *interp_prefix,
const char * filename, char ** argv, char ** envp,
struct target_pt_regs * regs, struct image_info *infop);
void target_set_brk(char *new_brk);

View File

@@ -570,8 +570,6 @@ get_sigframe(struct emulated_sigaction *ka, CPUX86State *env, size_t frame_size)
return (void *)((esp - frame_size) & -8ul);
}
#define TF_MASK TRAP_FLAG
static void setup_frame(int sig, struct emulated_sigaction *ka,
target_sigset_t *set, CPUX86State *env)
{

View File

@@ -26,6 +26,7 @@
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
@@ -40,6 +41,7 @@
#include <sys/uio.h>
#include <sys/poll.h>
//#include <sys/user.h>
#include <netinet/tcp.h>
#define termios host_termios
#define winsize host_winsize
@@ -103,10 +105,10 @@ extern int personality(int);
extern int flock(int, int);
extern int setfsuid(int);
extern int setfsgid(int);
extern int setresuid(int,int,int);
extern int getresuid(int *,int *,int *);
extern int setresgid(int,int,int);
extern int getresgid(int *,int *,int *);
extern int setresuid(uid_t, uid_t, uid_t);
extern int getresuid(uid_t *, uid_t *, uid_t *);
extern int setresgid(gid_t, gid_t, gid_t);
extern int getresgid(gid_t *, gid_t *, gid_t *);
static inline long get_errno(long ret)
{
@@ -166,7 +168,7 @@ static long do_brk(char *new_brk)
static inline fd_set *target_to_host_fds(fd_set *fds,
target_long *target_fds, int n)
{
#if !defined(BSWP_NEEDED) && !defined(WORD_BIGENDIAN)
#if !defined(BSWAP_NEEDED) && !defined(WORDS_BIGENDIAN)
return (fd_set *)target_fds;
#else
int i, b;
@@ -188,7 +190,7 @@ static inline fd_set *target_to_host_fds(fd_set *fds,
static inline void host_to_target_fds(target_long *target_fds,
fd_set *fds, int n)
{
#if !defined(BSWP_NEEDED) && !defined(WORD_BIGENDIAN)
#if !defined(BSWAP_NEEDED) && !defined(WORDS_BIGENDIAN)
/* nothing to do */
#else
int i, nw, j, k;
@@ -210,14 +212,14 @@ static inline void host_to_target_fds(target_long *target_fds,
}
static inline void target_to_host_timeval(struct timeval *tv,
struct target_timeval *target_tv)
const struct target_timeval *target_tv)
{
tv->tv_sec = tswapl(target_tv->tv_sec);
tv->tv_usec = tswapl(target_tv->tv_usec);
}
static inline void host_to_target_timeval(struct target_timeval *target_tv,
struct timeval *tv)
const struct timeval *tv)
{
target_tv->tv_sec = tswapl(tv->tv_sec);
target_tv->tv_usec = tswapl(tv->tv_usec);
@@ -238,8 +240,7 @@ static long do_select(long n,
efds_ptr = target_to_host_fds(&efds, target_efds, n);
if (target_tv) {
tv.tv_sec = tswapl(target_tv->tv_sec);
tv.tv_usec = tswapl(target_tv->tv_usec);
target_to_host_timeval(&tv, target_tv);
tv_ptr = &tv;
} else {
tv_ptr = NULL;
@@ -251,62 +252,273 @@ static long do_select(long n,
host_to_target_fds(target_efds, efds_ptr, n);
if (target_tv) {
target_tv->tv_sec = tswapl(tv.tv_sec);
target_tv->tv_usec = tswapl(tv.tv_usec);
host_to_target_timeval(target_tv, &tv);
}
}
return ret;
}
static long do_socketcall(int num, long *vptr)
static inline void target_to_host_sockaddr(struct sockaddr *addr,
struct target_sockaddr *target_addr,
socklen_t len)
{
memcpy(addr, target_addr, len);
addr->sa_family = tswap16(target_addr->sa_family);
}
static inline void host_to_target_sockaddr(struct target_sockaddr *target_addr,
struct sockaddr *addr,
socklen_t len)
{
memcpy(target_addr, addr, len);
target_addr->sa_family = tswap16(addr->sa_family);
}
static inline void target_to_host_cmsg(struct msghdr *msgh,
struct target_msghdr *target_msgh)
{
struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
struct target_cmsghdr *target_cmsg = TARGET_CMSG_FIRSTHDR(target_msgh);
socklen_t space = 0;
while (cmsg && target_cmsg) {
void *data = CMSG_DATA(cmsg);
void *target_data = TARGET_CMSG_DATA(target_cmsg);
int len = tswapl(target_cmsg->cmsg_len)
- TARGET_CMSG_ALIGN(sizeof (struct target_cmsghdr));
space += CMSG_SPACE(len);
if (space > msgh->msg_controllen) {
space -= CMSG_SPACE(len);
gemu_log("Host cmsg overflow");
break;
}
cmsg->cmsg_level = tswap32(target_cmsg->cmsg_level);
cmsg->cmsg_type = tswap32(target_cmsg->cmsg_type);
cmsg->cmsg_len = CMSG_LEN(len);
if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
gemu_log("Unsupported ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type);
memcpy(data, target_data, len);
} else {
int *fd = (int *)data;
int *target_fd = (int *)target_data;
int i, numfds = len / sizeof(int);
for (i = 0; i < numfds; i++)
fd[i] = tswap32(target_fd[i]);
}
cmsg = CMSG_NXTHDR(msgh, cmsg);
target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg);
}
msgh->msg_controllen = space;
}
static inline void host_to_target_cmsg(struct target_msghdr *target_msgh,
struct msghdr *msgh)
{
struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
struct target_cmsghdr *target_cmsg = TARGET_CMSG_FIRSTHDR(target_msgh);
socklen_t space = 0;
while (cmsg && target_cmsg) {
void *data = CMSG_DATA(cmsg);
void *target_data = TARGET_CMSG_DATA(target_cmsg);
int len = cmsg->cmsg_len - CMSG_ALIGN(sizeof (struct cmsghdr));
space += TARGET_CMSG_SPACE(len);
if (space > tswapl(target_msgh->msg_controllen)) {
space -= TARGET_CMSG_SPACE(len);
gemu_log("Target cmsg overflow");
break;
}
target_cmsg->cmsg_level = tswap32(cmsg->cmsg_level);
target_cmsg->cmsg_type = tswap32(cmsg->cmsg_type);
target_cmsg->cmsg_len = tswapl(TARGET_CMSG_LEN(len));
if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
gemu_log("Unsupported ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type);
memcpy(target_data, data, len);
} else {
int *fd = (int *)data;
int *target_fd = (int *)target_data;
int i, numfds = len / sizeof(int);
for (i = 0; i < numfds; i++)
target_fd[i] = tswap32(fd[i]);
}
cmsg = CMSG_NXTHDR(msgh, cmsg);
target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg);
}
msgh->msg_controllen = tswapl(space);
}
static long do_setsockopt(int sockfd, int level, int optname,
void *optval, socklen_t optlen)
{
if (level == SOL_TCP) {
/* TCP options all take an 'int' value. */
int val;
if (optlen < sizeof(uint32_t))
return -EINVAL;
val = tswap32(*(uint32_t *)optval);
return get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val)));
}
else if (level != SOL_SOCKET) {
gemu_log("Unsupported setsockopt level: %d\n", level);
return -ENOSYS;
}
switch (optname) {
/* Options with 'int' argument. */
case SO_DEBUG:
case SO_REUSEADDR:
case SO_TYPE:
case SO_ERROR:
case SO_DONTROUTE:
case SO_BROADCAST:
case SO_SNDBUF:
case SO_RCVBUF:
case SO_KEEPALIVE:
case SO_OOBINLINE:
case SO_NO_CHECK:
case SO_PRIORITY:
case SO_BSDCOMPAT:
case SO_PASSCRED:
case SO_TIMESTAMP:
case SO_RCVLOWAT:
case SO_RCVTIMEO:
case SO_SNDTIMEO:
{
int val;
if (optlen < sizeof(uint32_t))
return -EINVAL;
val = tswap32(*(uint32_t *)optval);
return get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val)));
}
default:
gemu_log("Unsupported setsockopt SOL_SOCKET option: %d\n", optname);
return -ENOSYS;
}
}
static long do_getsockopt(int sockfd, int level, int optname,
void *optval, socklen_t *optlen)
{
gemu_log("getsockopt not yet supported\n");
return -ENOSYS;
}
static long do_socketcall(int num, int32_t *vptr)
{
long ret;
switch(num) {
case SOCKOP_socket:
ret = get_errno(socket(vptr[0], vptr[1], vptr[2]));
{
int domain = tswap32(vptr[0]);
int type = tswap32(vptr[1]);
int protocol = tswap32(vptr[2]);
ret = get_errno(socket(domain, type, protocol));
}
break;
case SOCKOP_bind:
ret = get_errno(bind(vptr[0], (struct sockaddr *)vptr[1], vptr[2]));
{
int sockfd = tswap32(vptr[0]);
void *target_addr = (void *)tswap32(vptr[1]);
socklen_t addrlen = tswap32(vptr[2]);
void *addr = alloca(addrlen);
target_to_host_sockaddr(addr, target_addr, addrlen);
ret = get_errno(bind(sockfd, addr, addrlen));
}
break;
case SOCKOP_connect:
ret = get_errno(connect(vptr[0], (struct sockaddr *)vptr[1], vptr[2]));
{
int sockfd = tswap32(vptr[0]);
void *target_addr = (void *)tswap32(vptr[1]);
socklen_t addrlen = tswap32(vptr[2]);
void *addr = alloca(addrlen);
target_to_host_sockaddr(addr, target_addr, addrlen);
ret = get_errno(connect(sockfd, addr, addrlen));
}
break;
case SOCKOP_listen:
ret = get_errno(listen(vptr[0], vptr[1]));
{
int sockfd = tswap32(vptr[0]);
int backlog = tswap32(vptr[1]);
ret = get_errno(listen(sockfd, backlog));
}
break;
case SOCKOP_accept:
{
socklen_t size;
size = tswap32(*(int32_t *)vptr[2]);
ret = get_errno(accept(vptr[0], (struct sockaddr *)vptr[1], &size));
if (!is_error(ret))
*(int32_t *)vptr[2] = size;
int sockfd = tswap32(vptr[0]);
void *target_addr = (void *)tswap32(vptr[1]);
uint32_t *target_addrlen = (void *)tswap32(vptr[2]);
socklen_t addrlen = tswap32(*target_addrlen);
void *addr = alloca(addrlen);
ret = get_errno(accept(sockfd, addr, &addrlen));
if (!is_error(ret)) {
host_to_target_sockaddr(target_addr, addr, addrlen);
*target_addrlen = tswap32(addrlen);
}
}
break;
case SOCKOP_getsockname:
{
socklen_t size;
size = tswap32(*(int32_t *)vptr[2]);
ret = get_errno(getsockname(vptr[0], (struct sockaddr *)vptr[1], &size));
if (!is_error(ret))
*(int32_t *)vptr[2] = size;
int sockfd = tswap32(vptr[0]);
void *target_addr = (void *)tswap32(vptr[1]);
uint32_t *target_addrlen = (void *)tswap32(vptr[2]);
socklen_t addrlen = tswap32(*target_addrlen);
void *addr = alloca(addrlen);
ret = get_errno(getsockname(sockfd, addr, &addrlen));
if (!is_error(ret)) {
host_to_target_sockaddr(target_addr, addr, addrlen);
*target_addrlen = tswap32(addrlen);
}
}
break;
case SOCKOP_getpeername:
{
socklen_t size;
size = tswap32(*(int32_t *)vptr[2]);
ret = get_errno(getpeername(vptr[0], (struct sockaddr *)vptr[1], &size));
if (!is_error(ret))
*(int32_t *)vptr[2] = size;
int sockfd = tswap32(vptr[0]);
void *target_addr = (void *)tswap32(vptr[1]);
uint32_t *target_addrlen = (void *)tswap32(vptr[2]);
socklen_t addrlen = tswap32(*target_addrlen);
void *addr = alloca(addrlen);
ret = get_errno(getpeername(sockfd, addr, &addrlen));
if (!is_error(ret)) {
host_to_target_sockaddr(target_addr, addr, addrlen);
*target_addrlen = tswap32(addrlen);
}
}
break;
case SOCKOP_socketpair:
{
int domain = tswap32(vptr[0]);
int type = tswap32(vptr[1]);
int protocol = tswap32(vptr[2]);
int32_t *target_tab = (void *)tswap32(vptr[3]);
int tab[2];
int32_t *target_tab = (int32_t *)vptr[3];
ret = get_errno(socketpair(vptr[0], vptr[1], vptr[2], tab));
ret = get_errno(socketpair(domain, type, protocol, tab));
if (!is_error(ret)) {
target_tab[0] = tswap32(tab[0]);
target_tab[1] = tswap32(tab[1]);
@@ -314,27 +526,64 @@ static long do_socketcall(int num, long *vptr)
}
break;
case SOCKOP_send:
ret = get_errno(send(vptr[0], (void *)vptr[1], vptr[2], vptr[3]));
{
int sockfd = tswap32(vptr[0]);
void *msg = (void *)tswap32(vptr[1]);
size_t len = tswap32(vptr[2]);
int flags = tswap32(vptr[3]);
ret = get_errno(send(sockfd, msg, len, flags));
}
break;
case SOCKOP_recv:
ret = get_errno(recv(vptr[0], (void *)vptr[1], vptr[2], vptr[3]));
{
int sockfd = tswap32(vptr[0]);
void *msg = (void *)tswap32(vptr[1]);
size_t len = tswap32(vptr[2]);
int flags = tswap32(vptr[3]);
ret = get_errno(recv(sockfd, msg, len, flags));
}
break;
case SOCKOP_sendto:
ret = get_errno(sendto(vptr[0], (void *)vptr[1], vptr[2], vptr[3],
(struct sockaddr *)vptr[4], vptr[5]));
{
int sockfd = tswap32(vptr[0]);
void *msg = (void *)tswap32(vptr[1]);
size_t len = tswap32(vptr[2]);
int flags = tswap32(vptr[3]);
void *target_addr = (void *)tswap32(vptr[4]);
socklen_t addrlen = tswap32(vptr[5]);
void *addr = alloca(addrlen);
target_to_host_sockaddr(addr, target_addr, addrlen);
ret = get_errno(sendto(sockfd, msg, len, flags, addr, addrlen));
}
break;
case SOCKOP_recvfrom:
{
socklen_t size;
size = tswap32(*(int32_t *)vptr[5]);
ret = get_errno(recvfrom(vptr[0], (void *)vptr[1], vptr[2],
vptr[3], (struct sockaddr *)vptr[4], &size));
if (!is_error(ret))
*(int32_t *)vptr[5] = size;
int sockfd = tswap32(vptr[0]);
void *msg = (void *)tswap32(vptr[1]);
size_t len = tswap32(vptr[2]);
int flags = tswap32(vptr[3]);
void *target_addr = (void *)tswap32(vptr[4]);
uint32_t *target_addrlen = (void *)tswap32(vptr[5]);
socklen_t addrlen = tswap32(*target_addrlen);
void *addr = alloca(addrlen);
ret = get_errno(recvfrom(sockfd, msg, len, flags, addr, &addrlen));
if (!is_error(ret)) {
host_to_target_sockaddr(target_addr, addr, addrlen);
*target_addrlen = tswap32(addrlen);
}
}
break;
case SOCKOP_shutdown:
ret = get_errno(shutdown(vptr[0], vptr[1]));
{
int sockfd = tswap32(vptr[0]);
int how = tswap32(vptr[1]);
ret = get_errno(shutdown(sockfd, how));
}
break;
case SOCKOP_sendmsg:
case SOCKOP_recvmsg:
@@ -346,11 +595,11 @@ static long do_socketcall(int num, long *vptr)
struct iovec *vec;
struct target_iovec *target_vec;
msgp = (void *)vptr[1];
msgp = (void *)tswap32(vptr[1]);
msg.msg_name = (void *)tswapl(msgp->msg_name);
msg.msg_namelen = tswapl(msgp->msg_namelen);
msg.msg_control = (void *)tswapl(msgp->msg_control);
msg.msg_controllen = tswapl(msgp->msg_controllen);
msg.msg_controllen = 2 * tswapl(msgp->msg_controllen);
msg.msg_control = alloca(msg.msg_controllen);
msg.msg_flags = tswap32(msgp->msg_flags);
count = tswapl(msgp->msg_iovlen);
@@ -363,17 +612,43 @@ static long do_socketcall(int num, long *vptr)
msg.msg_iovlen = count;
msg.msg_iov = vec;
fd = vptr[0];
flags = vptr[2];
if (num == SOCKOP_sendmsg)
ret = sendmsg(fd, &msg, flags);
else
ret = recvmsg(fd, &msg, flags);
ret = get_errno(ret);
fd = tswap32(vptr[0]);
flags = tswap32(vptr[2]);
if (num == SOCKOP_sendmsg) {
target_to_host_cmsg(&msg, msgp);
ret = get_errno(sendmsg(fd, &msg, flags));
} else {
ret = get_errno(recvmsg(fd, &msg, flags));
if (!is_error(ret))
host_to_target_cmsg(msgp, &msg);
}
}
break;
case SOCKOP_setsockopt:
{
int sockfd = tswap32(vptr[0]);
int level = tswap32(vptr[1]);
int optname = tswap32(vptr[2]);
void *optval = (void *)tswap32(vptr[3]);
socklen_t optlen = tswap32(vptr[4]);
ret = do_setsockopt(sockfd, level, optname, optval, optlen);
}
break;
case SOCKOP_getsockopt:
{
int sockfd = tswap32(vptr[0]);
int level = tswap32(vptr[1]);
int optname = tswap32(vptr[2]);
void *optval = (void *)tswap32(vptr[3]);
uint32_t *target_len = (void *)tswap32(vptr[4]);
socklen_t optlen = tswap32(*target_len);
ret = do_getsockopt(sockfd, level, optname, optval, &optlen);
if (!is_error(ret))
*target_len = tswap32(optlen);
}
break;
default:
gemu_log("Unsupported socketcall: %d\n", num);
ret = -ENOSYS;
@@ -755,7 +1030,7 @@ install:
}
/* specific and weird i386 syscalls */
int gemu_modify_ldt(CPUX86State *env, int func, void *ptr, unsigned long bytecount)
int do_modify_ldt(CPUX86State *env, int func, void *ptr, unsigned long bytecount)
{
int ret = -ENOSYS;
@@ -773,6 +1048,79 @@ int gemu_modify_ldt(CPUX86State *env, int func, void *ptr, unsigned long bytecou
return ret;
}
/* vm86 emulation */
#define SAFE_MASK (0xDD5)
int do_vm86(CPUX86State *env, long subfunction,
struct target_vm86plus_struct * target_v86)
{
TaskState *ts = env->opaque;
int ret;
switch (subfunction) {
case TARGET_VM86_REQUEST_IRQ:
case TARGET_VM86_FREE_IRQ:
case TARGET_VM86_GET_IRQ_BITS:
case TARGET_VM86_GET_AND_RESET_IRQ:
gemu_log("qemu: unsupported vm86 subfunction (%ld)\n", subfunction);
ret = -EINVAL;
goto out;
case TARGET_VM86_PLUS_INSTALL_CHECK:
/* NOTE: on old vm86 stuff this will return the error
from verify_area(), because the subfunction is
interpreted as (invalid) address to vm86_struct.
So the installation check works.
*/
ret = 0;
goto out;
}
ts->target_v86 = target_v86;
/* save current CPU regs */
ts->vm86_saved_regs.eax = 0; /* default vm86 syscall return code */
ts->vm86_saved_regs.ebx = env->regs[R_EBX];
ts->vm86_saved_regs.ecx = env->regs[R_ECX];
ts->vm86_saved_regs.edx = env->regs[R_EDX];
ts->vm86_saved_regs.esi = env->regs[R_ESI];
ts->vm86_saved_regs.edi = env->regs[R_EDI];
ts->vm86_saved_regs.ebp = env->regs[R_EBP];
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];
/* build vm86 CPU state */
env->eflags = (env->eflags & ~SAFE_MASK) |
(tswap32(target_v86->regs.eflags) & SAFE_MASK) | VM_MASK;
env->regs[R_EBX] = tswap32(target_v86->regs.ebx);
env->regs[R_ECX] = tswap32(target_v86->regs.ecx);
env->regs[R_EDX] = tswap32(target_v86->regs.edx);
env->regs[R_ESI] = tswap32(target_v86->regs.esi);
env->regs[R_EDI] = tswap32(target_v86->regs.edi);
env->regs[R_EBP] = tswap32(target_v86->regs.ebp);
env->regs[R_ESP] = tswap32(target_v86->regs.esp);
env->eip = tswap32(target_v86->regs.eip);
cpu_x86_load_seg(env, R_CS, tswap16(target_v86->regs.cs));
cpu_x86_load_seg(env, R_SS, tswap16(target_v86->regs.ss));
cpu_x86_load_seg(env, R_DS, tswap16(target_v86->regs.ds));
cpu_x86_load_seg(env, R_ES, tswap16(target_v86->regs.es));
cpu_x86_load_seg(env, R_FS, tswap16(target_v86->regs.fs));
cpu_x86_load_seg(env, R_GS, tswap16(target_v86->regs.gs));
ret = tswap32(target_v86->regs.eax); /* eax will be restored at
the end of the syscall */
/* now the virtual CPU is ready for vm86 execution ! */
out:
return ret;
}
/* this stack is the equivalent of the kernel stack associated with a
thread/process */
#define NEW_STACK_SIZE 8192
@@ -788,19 +1136,26 @@ static int clone_func(void *arg)
int do_fork(CPUX86State *env, unsigned int flags, unsigned long newsp)
{
int ret;
TaskState *ts;
uint8_t *new_stack;
CPUX86State *new_env;
if (flags & CLONE_VM) {
if (!newsp)
newsp = env->regs[R_ESP];
new_stack = malloc(NEW_STACK_SIZE);
ts = malloc(sizeof(TaskState) + NEW_STACK_SIZE);
memset(ts, 0, sizeof(TaskState));
new_stack = ts->stack;
ts->used = 1;
/* add in task state list */
ts->next = first_task_state;
first_task_state = ts;
/* we create a new CPU instance. */
new_env = cpu_x86_init();
memcpy(new_env, env, sizeof(CPUX86State));
new_env->regs[R_ESP] = newsp;
new_env->regs[R_EAX] = 0;
new_env->opaque = ts;
ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env);
} else {
/* if no CLONE_VM, we consider it is a fork */
@@ -882,7 +1237,27 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
ret = get_errno(unlink((const char *)arg1));
break;
case TARGET_NR_execve:
ret = get_errno(execve((const char *)arg1, (void *)arg2, (void *)arg3));
{
char **argp, **envp;
int argc = 0, envc = 0;
uint32_t *p;
char **q;
for (p = (void *)arg2; *p; p++)
argc++;
for (p = (void *)arg3; *p; p++)
envc++;
argp = alloca(argc * sizeof(void *));
envp = alloca(envc * sizeof(void *));
for (p = (void *)arg2, q = argp; *p; p++, q++)
*q = (void *)tswap32(*p);
for (p = (void *)arg3, q = envp; *p; p++, q++)
*q = (void *)tswap32(*p);
ret = get_errno(execve((const char *)arg1, argp, envp));
}
break;
case TARGET_NR_chdir:
ret = get_errno(chdir((const char *)arg1));
@@ -1281,8 +1656,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
struct timeval tv;
ret = get_errno(gettimeofday(&tv, NULL));
if (!is_error(ret)) {
target_tv->tv_sec = tswapl(tv.tv_sec);
target_tv->tv_usec = tswapl(tv.tv_usec);
host_to_target_timeval(target_tv, &tv);
}
}
break;
@@ -1290,8 +1664,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
{
struct target_timeval *target_tv = (void *)arg1;
struct timeval tv;
tv.tv_sec = tswapl(target_tv->tv_sec);
tv.tv_usec = tswapl(target_tv->tv_usec);
target_to_host_timeval(&tv, target_tv);
ret = get_errno(settimeofday(&tv, NULL));
}
break;
@@ -1408,7 +1781,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
case TARGET_NR_ioperm:
goto unimplemented;
case TARGET_NR_socketcall:
ret = do_socketcall(arg1, (long *)arg2);
ret = do_socketcall(arg1, (int32_t *)arg2);
break;
case TARGET_NR_syslog:
goto unimplemented;
@@ -1472,9 +1845,9 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
target_st->st_size = tswapl(st.st_size);
target_st->st_blksize = tswapl(st.st_blksize);
target_st->st_blocks = tswapl(st.st_blocks);
target_st->st_atime = tswapl(st.st_atime);
target_st->st_mtime = tswapl(st.st_mtime);
target_st->st_ctime = tswapl(st.st_ctime);
target_st->target_st_atime = tswapl(st.st_atime);
target_st->target_st_mtime = tswapl(st.st_mtime);
target_st->target_st_ctime = tswapl(st.st_ctime);
}
}
break;
@@ -1487,8 +1860,6 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
break;
case TARGET_NR_idle:
goto unimplemented;
case TARGET_NR_vm86old:
goto unimplemented;
case TARGET_NR_wait4:
{
int status;
@@ -1548,7 +1919,12 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
break;
#ifdef TARGET_I386
case TARGET_NR_modify_ldt:
ret = get_errno(gemu_modify_ldt(cpu_env, arg1, (void *)arg2, arg3));
ret = get_errno(do_modify_ldt(cpu_env, arg1, (void *)arg2, arg3));
break;
case TARGET_NR_vm86old:
goto unimplemented;
case TARGET_NR_vm86:
ret = do_vm86(cpu_env, arg1, (void *)arg2);
break;
#endif
case TARGET_NR_adjtimex:
@@ -1603,10 +1979,10 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
int reclen;
de = dirp;
while (len > 0) {
reclen = tswap16(de->d_reclen);
reclen = de->d_reclen;
if (reclen > len)
break;
de->d_reclen = reclen;
de->d_reclen = tswap16(reclen);
tswapls(&de->d_ino);
tswapls(&de->d_off);
de = (struct dirent *)((char *)de + reclen);
@@ -1626,10 +2002,10 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
int reclen;
de = dirp;
while (len > 0) {
reclen = tswap16(de->d_reclen);
reclen = de->d_reclen;
if (reclen > len)
break;
de->d_reclen = reclen;
de->d_reclen = tswap16(reclen);
tswap64s(&de->d_ino);
tswap64s(&de->d_off);
de = (struct dirent64 *)((char *)de + reclen);
@@ -1648,17 +2024,17 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
unsigned int nfds = arg2;
int timeout = arg3;
struct pollfd *pfd;
int i;
unsigned int i;
pfd = alloca(sizeof(struct pollfd) * nfds);
for(i = 0; i < nfds; i++) {
pfd->fd = tswap32(target_pfd->fd);
pfd->events = tswap16(target_pfd->events);
pfd[i].fd = tswap32(target_pfd[i].fd);
pfd[i].events = tswap16(target_pfd[i].events);
}
ret = get_errno(poll(pfd, nfds, timeout));
if (!is_error(ret)) {
for(i = 0; i < nfds; i++) {
target_pfd->revents = tswap16(pfd->revents);
target_pfd[i].revents = tswap16(pfd[i].revents);
}
}
}
@@ -1702,25 +2078,59 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
ret = get_errno(getsid(arg1));
break;
case TARGET_NR_fdatasync:
goto unimplemented;
ret = get_errno(fdatasync(arg1));
break;
case TARGET_NR__sysctl:
goto unimplemented;
case TARGET_NR_sched_setparam:
goto unimplemented;
{
struct sched_param *target_schp = (void *)arg2;
struct sched_param schp;
schp.sched_priority = tswap32(target_schp->sched_priority);
ret = get_errno(sched_setparam(arg1, &schp));
}
break;
case TARGET_NR_sched_getparam:
goto unimplemented;
{
struct sched_param *target_schp = (void *)arg2;
struct sched_param schp;
ret = get_errno(sched_getparam(arg1, &schp));
if (!is_error(ret)) {
target_schp->sched_priority = tswap32(schp.sched_priority);
}
}
break;
case TARGET_NR_sched_setscheduler:
goto unimplemented;
{
struct sched_param *target_schp = (void *)arg3;
struct sched_param schp;
schp.sched_priority = tswap32(target_schp->sched_priority);
ret = get_errno(sched_setscheduler(arg1, arg2, &schp));
}
break;
case TARGET_NR_sched_getscheduler:
goto unimplemented;
ret = get_errno(sched_getscheduler(arg1));
break;
case TARGET_NR_sched_yield:
ret = get_errno(sched_yield());
break;
case TARGET_NR_sched_get_priority_max:
ret = get_errno(sched_get_priority_max(arg1));
break;
case TARGET_NR_sched_get_priority_min:
ret = get_errno(sched_get_priority_min(arg1));
break;
case TARGET_NR_sched_rr_get_interval:
goto unimplemented;
{
struct target_timespec *target_ts = (void *)arg2;
struct timespec ts;
ret = get_errno(sched_rr_get_interval(arg1, &ts));
if (!is_error(ret)) {
target_ts->tv_sec = tswapl(ts.tv_sec);
target_ts->tv_nsec = tswapl(ts.tv_nsec);
}
}
break;
case TARGET_NR_nanosleep:
{
struct target_timespec *target_req = (void *)arg1;
@@ -1767,11 +2177,14 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
}
}
break;
case TARGET_NR_vm86:
case TARGET_NR_query_module:
goto unimplemented;
case TARGET_NR_nfsservctl:
goto unimplemented;
case TARGET_NR_prctl:
goto unimplemented;
case TARGET_NR_pread:
goto unimplemented;
case TARGET_NR_pwrite:
goto unimplemented;
case TARGET_NR_chown:
@@ -1781,16 +2194,24 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
ret = get_errno(sys_getcwd1((char *)arg1, arg2));
break;
case TARGET_NR_capget:
goto unimplemented;
case TARGET_NR_capset:
goto unimplemented;
case TARGET_NR_sigaltstack:
goto unimplemented;
case TARGET_NR_sendfile:
goto unimplemented;
case TARGET_NR_getpmsg:
goto unimplemented;
case TARGET_NR_putpmsg:
goto unimplemented;
case TARGET_NR_vfork:
ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD, 0));
break;
case TARGET_NR_ugetrlimit:
goto unimplemented;
case TARGET_NR_truncate64:
goto unimplemented;
case TARGET_NR_ftruncate64:
goto unimplemented;
case TARGET_NR_stat64:
@@ -1816,9 +2237,9 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
target_st->st_size = tswapl(st.st_size);
target_st->st_blksize = tswapl(st.st_blksize);
target_st->st_blocks = tswapl(st.st_blocks);
target_st->st_atime = tswapl(st.st_atime);
target_st->st_mtime = tswapl(st.st_mtime);
target_st->st_ctime = tswapl(st.st_ctime);
target_st->target_st_atime = tswapl(st.st_atime);
target_st->target_st_mtime = tswapl(st.st_mtime);
target_st->target_st_ctime = tswapl(st.st_ctime);
}
}
break;
@@ -1919,6 +2340,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
ret = get_errno(gettid());
break;
case TARGET_NR_readahead:
goto unimplemented;
case TARGET_NR_setxattr:
case TARGET_NR_lsetxattr:
case TARGET_NR_fsetxattr:
@@ -1931,10 +2353,14 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
case TARGET_NR_removexattr:
case TARGET_NR_lremovexattr:
case TARGET_NR_fremovexattr:
goto unimplemented;
goto unimplemented_nowarn;
case TARGET_NR_set_thread_area:
case TARGET_NR_get_thread_area:
goto unimplemented_nowarn;
default:
unimplemented:
gemu_log("gemu: Unsupported syscall: %d\n", num);
gemu_log("qemu: Unsupported syscall: %d\n", num);
unimplemented_nowarn:
ret = -ENOSYS;
break;
}

View File

@@ -19,6 +19,11 @@
#define SOCKOP_sendmsg 16
#define SOCKOP_recvmsg 17
struct target_sockaddr {
uint16_t sa_family;
uint8_t sa_data[14];
};
struct target_timeval {
target_long tv_sec;
target_long tv_usec;
@@ -49,6 +54,43 @@ struct target_msghdr {
unsigned int msg_flags;
};
struct target_cmsghdr {
target_long cmsg_len;
int cmsg_level;
int cmsg_type;
};
#define TARGET_CMSG_DATA(cmsg) ((unsigned char *) ((struct target_cmsghdr *) (cmsg) + 1))
#define TARGET_CMSG_NXTHDR(mhdr, cmsg) __target_cmsg_nxthdr (mhdr, cmsg)
#define TARGET_CMSG_FIRSTHDR(mhdr) \
((size_t) tswapl((mhdr)->msg_controllen) >= sizeof (struct target_cmsghdr) \
? (struct target_cmsghdr *) tswapl((mhdr)->msg_control) : (struct target_cmsghdr *) NULL)
#define TARGET_CMSG_ALIGN(len) (((len) + sizeof (target_long) - 1) \
& (size_t) ~(sizeof (target_long) - 1))
#define TARGET_CMSG_SPACE(len) (TARGET_CMSG_ALIGN (len) \
+ TARGET_CMSG_ALIGN (sizeof (struct target_cmsghdr)))
#define TARGET_CMSG_LEN(len) (TARGET_CMSG_ALIGN (sizeof (struct target_cmsghdr)) + (len))
static __inline__ struct target_cmsghdr *
__target_cmsg_nxthdr (struct target_msghdr *__mhdr, struct target_cmsghdr *__cmsg)
{
if (tswapl(__cmsg->cmsg_len) < sizeof (struct target_cmsghdr))
/* The kernel header does this so there may be a reason. */
return 0;
__cmsg = (struct target_cmsghdr *) ((unsigned char *) __cmsg
+ TARGET_CMSG_ALIGN (tswapl(__cmsg->cmsg_len)));
if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) tswapl(__mhdr->msg_control)
+ tswapl(__mhdr->msg_controllen))
|| ((unsigned char *) __cmsg + TARGET_CMSG_ALIGN (tswapl(__cmsg->cmsg_len))
> ((unsigned char *) tswapl(__mhdr->msg_control)
+ tswapl(__mhdr->msg_controllen))))
/* No more entries. */
return 0;
return __cmsg;
}
struct target_rusage {
struct target_timeval ru_utime; /* user time used */
struct target_timeval ru_stime; /* system time used */

124
op-i386.c
View File

@@ -489,6 +489,11 @@ void OPPROTO op_addl_A0_im(void)
A0 += PARAM1;
}
void OPPROTO op_addl_A0_AL(void)
{
A0 += (EAX & 0xff);
}
void OPPROTO op_andl_A0_ffff(void)
{
A0 = A0 & 0xffff;
@@ -602,13 +607,51 @@ void OPPROTO op_into(void)
int eflags;
eflags = cc_table[CC_OP].compute_all();
if (eflags & CC_O) {
EIP = PARAM1;
raise_exception(EXCP04_INTO);
} else {
EIP = PARAM2;
}
}
void OPPROTO op_boundw(void)
{
int low, high, v;
low = ldsw((uint8_t *)A0);
high = ldsw((uint8_t *)A0 + 2);
v = (int16_t)T0;
if (v < low || v > high)
raise_exception(EXCP05_BOUND);
FORCE_RET();
}
void OPPROTO op_boundl(void)
{
int low, high, v;
low = ldl((uint8_t *)A0);
high = ldl((uint8_t *)A0 + 4);
v = T0;
if (v < low || v > high)
raise_exception(EXCP05_BOUND);
FORCE_RET();
}
void OPPROTO op_cmpxchg8b(void)
{
uint64_t d;
int eflags;
eflags = cc_table[CC_OP].compute_all();
d = ldq((uint8_t *)A0);
if (d == (((uint64_t)EDX << 32) | EAX)) {
stq((uint8_t *)A0, ((uint64_t)ECX << 32) | EBX);
eflags |= CC_Z;
} else {
EDX = d >> 32;
EAX = d;
eflags &= ~CC_Z;
}
CC_SRC = eflags;
FORCE_RET();
}
/* string ops */
#define ldul ldl
@@ -788,7 +831,8 @@ void op_addw_ESP_im(void)
#ifndef __i386__
uint64_t emu_time;
#endif
void op_rdtsc(void)
void OPPROTO op_rdtsc(void)
{
uint64_t val;
#ifdef __i386__
@@ -801,6 +845,51 @@ void op_rdtsc(void)
EDX = val >> 32;
}
/* We simulate a pre-MMX pentium as in valgrind */
#define CPUID_FP87 (1 << 0)
#define CPUID_VME (1 << 1)
#define CPUID_DE (1 << 2)
#define CPUID_PSE (1 << 3)
#define CPUID_TSC (1 << 4)
#define CPUID_MSR (1 << 5)
#define CPUID_PAE (1 << 6)
#define CPUID_MCE (1 << 7)
#define CPUID_CX8 (1 << 8)
#define CPUID_APIC (1 << 9)
#define CPUID_SEP (1 << 11) /* sysenter/sysexit */
#define CPUID_MTRR (1 << 12)
#define CPUID_PGE (1 << 13)
#define CPUID_MCA (1 << 14)
#define CPUID_CMOV (1 << 15)
/* ... */
#define CPUID_MMX (1 << 23)
#define CPUID_FXSR (1 << 24)
#define CPUID_SSE (1 << 25)
#define CPUID_SSE2 (1 << 26)
void helper_cpuid(void)
{
if (EAX == 0) {
EAX = 1; /* max EAX index supported */
EBX = 0x756e6547;
ECX = 0x6c65746e;
EDX = 0x49656e69;
} else {
/* EAX = 1 info */
EAX = 0x52b;
EBX = 0;
ECX = 0;
EDX = CPUID_FP87 | CPUID_VME | CPUID_DE | CPUID_PSE |
CPUID_TSC | CPUID_MSR | CPUID_MCE |
CPUID_CX8;
}
}
void OPPROTO op_cpuid(void)
{
helper_cpuid();
}
/* bcd */
/* XXX: exception */
@@ -933,6 +1022,7 @@ void OPPROTO op_das(void)
/* segment handling */
/* XXX: use static VM86 information */
void load_seg(int seg_reg, int selector)
{
SegmentCache *sc;
@@ -943,7 +1033,7 @@ void load_seg(int seg_reg, int selector)
env->segs[seg_reg] = selector;
sc = &env->seg_cache[seg_reg];
if (env->vm86) {
if (env->eflags & VM_MASK) {
sc->base = (void *)(selector << 4);
sc->limit = 0xffff;
sc->seg_32bit = 0;
@@ -980,6 +1070,11 @@ void OPPROTO op_movl_T0_seg(void)
T0 = env->segs[PARAM1];
}
void OPPROTO op_movl_A0_seg(void)
{
A0 = *(unsigned long *)((char *)env + PARAM1);
}
void OPPROTO op_addl_A0_seg(void)
{
A0 += *(unsigned long *)((char *)env + PARAM1);
@@ -1139,10 +1234,16 @@ void OPPROTO op_set_cc_op(void)
CC_OP = PARAM1;
}
#define FL_UPDATE_MASK (TF_MASK | AC_MASK | ID_MASK)
void OPPROTO op_movl_eflags_T0(void)
{
CC_SRC = T0;
DF = 1 - (2 * ((T0 >> 10) & 1));
int eflags;
eflags = T0;
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_MASK) | (eflags & FL_UPDATE_MASK);
}
/* XXX: compute only O flag */
@@ -1150,13 +1251,16 @@ void OPPROTO op_movb_eflags_T0(void)
{
int of;
of = cc_table[CC_OP].compute_all() & CC_O;
CC_SRC = T0 | of;
CC_SRC = (T0 & (CC_S | CC_Z | CC_A | CC_P | CC_C)) | of;
}
void OPPROTO op_movl_T0_eflags(void)
{
T0 = cc_table[CC_OP].compute_all();
T0 |= (DF & DIRECTION_FLAG);
int eflags;
eflags = cc_table[CC_OP].compute_all();
eflags |= (DF & DF_MASK);
eflags |= env->eflags & ~(VM_MASK | RF_MASK);
T0 = eflags;
}
void OPPROTO op_cld(void)

245
op_string.h Normal file
View File

@@ -0,0 +1,245 @@
void OPPROTO glue(glue(op_movs, SUFFIX), STRING_SUFFIX)(void)
{
int v, inc;
inc = (DF << SHIFT);
v = glue(ldu, SUFFIX)(SI_ADDR);
glue(st, SUFFIX)(DI_ADDR, v);
inc = (DF << SHIFT);
INC_SI();
INC_DI();
}
void OPPROTO glue(glue(op_rep_movs, SUFFIX), STRING_SUFFIX)(void)
{
int v, inc;
inc = (DF << SHIFT);
while (CX != 0) {
v = glue(ldu, SUFFIX)(SI_ADDR);
glue(st, SUFFIX)(DI_ADDR, v);
INC_SI();
INC_DI();
DEC_CX();
}
FORCE_RET();
}
void OPPROTO glue(glue(op_stos, SUFFIX), STRING_SUFFIX)(void)
{
int inc;
glue(st, SUFFIX)(DI_ADDR, EAX);
inc = (DF << SHIFT);
INC_DI();
}
void OPPROTO glue(glue(op_rep_stos, SUFFIX), STRING_SUFFIX)(void)
{
int inc;
inc = (DF << SHIFT);
while (CX != 0) {
glue(st, SUFFIX)(DI_ADDR, EAX);
INC_DI();
DEC_CX();
}
FORCE_RET();
}
void OPPROTO glue(glue(op_lods, SUFFIX), STRING_SUFFIX)(void)
{
int v, inc;
v = glue(ldu, SUFFIX)(SI_ADDR);
#if SHIFT == 0
EAX = (EAX & ~0xff) | v;
#elif SHIFT == 1
EAX = (EAX & ~0xffff) | v;
#else
EAX = v;
#endif
inc = (DF << SHIFT);
INC_SI();
}
/* don't know if it is used */
void OPPROTO glue(glue(op_rep_lods, SUFFIX), STRING_SUFFIX)(void)
{
int v, inc;
inc = (DF << SHIFT);
while (CX != 0) {
v = glue(ldu, SUFFIX)(SI_ADDR);
#if SHIFT == 0
EAX = (EAX & ~0xff) | v;
#elif SHIFT == 1
EAX = (EAX & ~0xffff) | v;
#else
EAX = v;
#endif
INC_SI();
DEC_CX();
}
FORCE_RET();
}
void OPPROTO glue(glue(op_scas, SUFFIX), STRING_SUFFIX)(void)
{
int v, inc;
v = glue(ldu, SUFFIX)(DI_ADDR);
inc = (DF << SHIFT);
INC_DI();
CC_SRC = EAX;
CC_DST = EAX - v;
}
void OPPROTO glue(glue(op_repz_scas, SUFFIX), STRING_SUFFIX)(void)
{
int v1, v2, inc;
if (CX != 0) {
/* NOTE: the flags are not modified if CX == 0 */
v1 = EAX & DATA_MASK;
inc = (DF << SHIFT);
do {
v2 = glue(ldu, SUFFIX)(DI_ADDR);
INC_DI();
DEC_CX();
if (v1 != v2)
break;
} while (CX != 0);
CC_SRC = v1;
CC_DST = v1 - v2;
CC_OP = CC_OP_SUBB + SHIFT;
}
FORCE_RET();
}
void OPPROTO glue(glue(op_repnz_scas, SUFFIX), STRING_SUFFIX)(void)
{
int v1, v2, inc;
if (CX != 0) {
/* NOTE: the flags are not modified if CX == 0 */
v1 = EAX & DATA_MASK;
inc = (DF << SHIFT);
do {
v2 = glue(ldu, SUFFIX)(DI_ADDR);
INC_DI();
DEC_CX();
if (v1 == v2)
break;
} while (CX != 0);
CC_SRC = v1;
CC_DST = v1 - v2;
CC_OP = CC_OP_SUBB + SHIFT;
}
FORCE_RET();
}
void OPPROTO glue(glue(op_cmps, SUFFIX), STRING_SUFFIX)(void)
{
int v1, v2, inc;
v1 = glue(ldu, SUFFIX)(SI_ADDR);
v2 = glue(ldu, SUFFIX)(DI_ADDR);
inc = (DF << SHIFT);
INC_SI();
INC_DI();
CC_SRC = v1;
CC_DST = v1 - v2;
}
void OPPROTO glue(glue(op_repz_cmps, SUFFIX), STRING_SUFFIX)(void)
{
int v1, v2, inc;
if (CX != 0) {
inc = (DF << SHIFT);
do {
v1 = glue(ldu, SUFFIX)(SI_ADDR);
v2 = glue(ldu, SUFFIX)(DI_ADDR);
INC_SI();
INC_DI();
DEC_CX();
if (v1 != v2)
break;
} while (CX != 0);
CC_SRC = v1;
CC_DST = v1 - v2;
CC_OP = CC_OP_SUBB + SHIFT;
}
FORCE_RET();
}
void OPPROTO glue(glue(op_repnz_cmps, SUFFIX), STRING_SUFFIX)(void)
{
int v1, v2, inc;
if (CX != 0) {
inc = (DF << SHIFT);
do {
v1 = glue(ldu, SUFFIX)(SI_ADDR);
v2 = glue(ldu, SUFFIX)(DI_ADDR);
INC_SI();
INC_DI();
DEC_CX();
if (v1 == v2)
break;
} while (CX != 0);
CC_SRC = v1;
CC_DST = v1 - v2;
CC_OP = CC_OP_SUBB + SHIFT;
}
FORCE_RET();
}
void OPPROTO glue(glue(op_outs, SUFFIX), STRING_SUFFIX)(void)
{
int v, dx, inc;
dx = EDX & 0xffff;
v = glue(ldu, SUFFIX)(SI_ADDR);
glue(cpu_x86_out, SUFFIX)(dx, v);
inc = (DF << SHIFT);
INC_SI();
}
void OPPROTO glue(glue(op_rep_outs, SUFFIX), STRING_SUFFIX)(void)
{
int v, dx, inc;
inc = (DF << SHIFT);
dx = EDX & 0xffff;
while (CX != 0) {
v = glue(ldu, SUFFIX)(SI_ADDR);
glue(cpu_x86_out, SUFFIX)(dx, v);
INC_SI();
DEC_CX();
}
FORCE_RET();
}
void OPPROTO glue(glue(op_ins, SUFFIX), STRING_SUFFIX)(void)
{
int v, dx, inc;
dx = EDX & 0xffff;
v = glue(cpu_x86_in, SUFFIX)(dx);
glue(st, SUFFIX)(DI_ADDR, v);
inc = (DF << SHIFT);
INC_DI();
}
void OPPROTO glue(glue(op_rep_ins, SUFFIX), STRING_SUFFIX)(void)
{
int v, dx, inc;
inc = (DF << SHIFT);
dx = EDX & 0xffff;
while (CX != 0) {
v = glue(cpu_x86_in, SUFFIX)(dx);
glue(st, SUFFIX)(DI_ADDR, v);
INC_DI();
DEC_CX();
}
FORCE_RET();
}
#undef STRING_SUFFIX
#undef SI_ADDR
#undef DI_ADDR
#undef INC_SI
#undef INC_DI
#undef CX
#undef DEC_CX

1170
opc-i386.h

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,23 @@
/* templates for various register related operations */
/*
* i386 micro operations (templates for various register related
* operations)
*
* Copyright (c) 2003 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
void OPPROTO glue(op_movl_A0,REGNAME)(void)
{
A0 = REG;

View File

@@ -4,21 +4,20 @@
*
* Copyright (c) 2003 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define DATA_BITS (1 << (3 + SHIFT))
#define SHIFT_MASK (DATA_BITS - 1)
#define SIGN_MASK (1 << (DATA_BITS - 1))
@@ -807,238 +806,37 @@ void OPPROTO glue(glue(op_bsr, SUFFIX), _T0_cc)(void)
#endif
/* string operations */
/* XXX: maybe use lower level instructions to ease exception handling */
/* XXX: maybe use lower level instructions to ease 16 bit / segment handling */
void OPPROTO glue(op_movs, SUFFIX)(void)
{
int v;
v = glue(ldu, SUFFIX)((void *)ESI);
glue(st, SUFFIX)((void *)EDI, v);
ESI += (DF << SHIFT);
EDI += (DF << SHIFT);
}
#define STRING_SUFFIX _fast
#define SI_ADDR (void *)ESI
#define DI_ADDR (void *)EDI
#define INC_SI() ESI += inc
#define INC_DI() EDI += inc
#define CX ECX
#define DEC_CX() ECX--
#include "op_string.h"
void OPPROTO glue(op_rep_movs, SUFFIX)(void)
{
int v, inc;
inc = (DF << SHIFT);
while (ECX != 0) {
v = glue(ldu, SUFFIX)((void *)ESI);
glue(st, SUFFIX)((void *)EDI, v);
ESI += inc;
EDI += inc;
ECX--;
}
FORCE_RET();
}
#define STRING_SUFFIX _a32
#define SI_ADDR (uint8_t *)A0 + ESI
#define DI_ADDR env->seg_cache[R_ES].base + EDI
#define INC_SI() ESI += inc
#define INC_DI() EDI += inc
#define CX ECX
#define DEC_CX() ECX--
#include "op_string.h"
void OPPROTO glue(op_stos, SUFFIX)(void)
{
glue(st, SUFFIX)((void *)EDI, EAX);
EDI += (DF << SHIFT);
}
void OPPROTO glue(op_rep_stos, SUFFIX)(void)
{
int inc;
inc = (DF << SHIFT);
while (ECX != 0) {
glue(st, SUFFIX)((void *)EDI, EAX);
EDI += inc;
ECX--;
}
FORCE_RET();
}
void OPPROTO glue(op_lods, SUFFIX)(void)
{
int v;
v = glue(ldu, SUFFIX)((void *)ESI);
#if SHIFT == 0
EAX = (EAX & ~0xff) | v;
#elif SHIFT == 1
EAX = (EAX & ~0xffff) | v;
#else
EAX = v;
#endif
ESI += (DF << SHIFT);
}
/* don't know if it is used */
void OPPROTO glue(op_rep_lods, SUFFIX)(void)
{
int v, inc;
inc = (DF << SHIFT);
while (ECX != 0) {
v = glue(ldu, SUFFIX)((void *)ESI);
#if SHIFT == 0
EAX = (EAX & ~0xff) | v;
#elif SHIFT == 1
EAX = (EAX & ~0xffff) | v;
#else
EAX = v;
#endif
ESI += inc;
ECX--;
}
FORCE_RET();
}
void OPPROTO glue(op_scas, SUFFIX)(void)
{
int v;
v = glue(ldu, SUFFIX)((void *)EDI);
EDI += (DF << SHIFT);
CC_SRC = EAX;
CC_DST = EAX - v;
}
void OPPROTO glue(op_repz_scas, SUFFIX)(void)
{
int v1, v2, inc;
if (ECX != 0) {
/* NOTE: the flags are not modified if ECX == 0 */
v1 = EAX & DATA_MASK;
inc = (DF << SHIFT);
do {
v2 = glue(ldu, SUFFIX)((void *)EDI);
EDI += inc;
ECX--;
if (v1 != v2)
break;
} while (ECX != 0);
CC_SRC = v1;
CC_DST = v1 - v2;
CC_OP = CC_OP_SUBB + SHIFT;
}
FORCE_RET();
}
void OPPROTO glue(op_repnz_scas, SUFFIX)(void)
{
int v1, v2, inc;
if (ECX != 0) {
/* NOTE: the flags are not modified if ECX == 0 */
v1 = EAX & DATA_MASK;
inc = (DF << SHIFT);
do {
v2 = glue(ldu, SUFFIX)((void *)EDI);
EDI += inc;
ECX--;
if (v1 == v2)
break;
} while (ECX != 0);
CC_SRC = v1;
CC_DST = v1 - v2;
CC_OP = CC_OP_SUBB + SHIFT;
}
FORCE_RET();
}
void OPPROTO glue(op_cmps, SUFFIX)(void)
{
int v1, v2;
v1 = glue(ldu, SUFFIX)((void *)ESI);
v2 = glue(ldu, SUFFIX)((void *)EDI);
ESI += (DF << SHIFT);
EDI += (DF << SHIFT);
CC_SRC = v1;
CC_DST = v1 - v2;
}
void OPPROTO glue(op_repz_cmps, SUFFIX)(void)
{
int v1, v2, inc;
if (ECX != 0) {
inc = (DF << SHIFT);
do {
v1 = glue(ldu, SUFFIX)((void *)ESI);
v2 = glue(ldu, SUFFIX)((void *)EDI);
ESI += inc;
EDI += inc;
ECX--;
if (v1 != v2)
break;
} while (ECX != 0);
CC_SRC = v1;
CC_DST = v1 - v2;
CC_OP = CC_OP_SUBB + SHIFT;
}
FORCE_RET();
}
void OPPROTO glue(op_repnz_cmps, SUFFIX)(void)
{
int v1, v2, inc;
if (ECX != 0) {
inc = (DF << SHIFT);
do {
v1 = glue(ldu, SUFFIX)((void *)ESI);
v2 = glue(ldu, SUFFIX)((void *)EDI);
ESI += inc;
EDI += inc;
ECX--;
if (v1 == v2)
break;
} while (ECX != 0);
CC_SRC = v1;
CC_DST = v1 - v2;
CC_OP = CC_OP_SUBB + SHIFT;
}
FORCE_RET();
}
#define STRING_SUFFIX _a16
#define SI_ADDR (uint8_t *)A0 + (ESI & 0xffff)
#define DI_ADDR env->seg_cache[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)
#define DEC_CX() ECX = (ECX & ~0xffff) | ((ECX - 1) & 0xffff)
#include "op_string.h"
/* port I/O */
void OPPROTO glue(op_outs, SUFFIX)(void)
{
int v, dx;
dx = EDX & 0xffff;
v = glue(ldu, SUFFIX)((void *)ESI);
glue(cpu_x86_out, SUFFIX)(dx, v);
ESI += (DF << SHIFT);
}
void OPPROTO glue(op_rep_outs, SUFFIX)(void)
{
int v, dx, inc;
inc = (DF << SHIFT);
dx = EDX & 0xffff;
while (ECX != 0) {
v = glue(ldu, SUFFIX)((void *)ESI);
glue(cpu_x86_out, SUFFIX)(dx, v);
ESI += inc;
ECX--;
}
FORCE_RET();
}
void OPPROTO glue(op_ins, SUFFIX)(void)
{
int v, dx;
dx = EDX & 0xffff;
v = glue(cpu_x86_in, SUFFIX)(dx);
glue(st, SUFFIX)((void *)EDI, v);
EDI += (DF << SHIFT);
}
void OPPROTO glue(op_rep_ins, SUFFIX)(void)
{
int v, dx, inc;
inc = (DF << SHIFT);
dx = EDX & 0xffff;
while (ECX != 0) {
v = glue(cpu_x86_in, SUFFIX)(dx);
glue(st, SUFFIX)((void *)EDI, v);
EDI += (DF << SHIFT);
ECX--;
}
FORCE_RET();
}
void OPPROTO glue(glue(op_out, SUFFIX), _T0_T1)(void)
{
glue(cpu_x86_out, SUFFIX)(T0 & 0xffff, T1 & DATA_MASK);

382
qemu-doc.texi Normal file
View File

@@ -0,0 +1,382 @@
\input texinfo @c -*- texinfo -*-
@settitle QEMU x86 Emulator Reference Documentation
@titlepage
@sp 7
@center @titlefont{QEMU x86 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 or ARM. 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}) on
non-x86 CPUs.
QEMU features:
@itemize
@item User space only x86 emulator.
@item Currently ported on i386, PowerPC and S390.
@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
(experimental).
@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 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.
@end itemize
Current QEMU Limitations:
@itemize
@item Not all x86 exceptions are precise (yet). [Very few programs need that].
@item Not self virtualizable (yet). [You cannot launch qemu with qemu on the same CPU].
@item No support for self modifying code (yet). [Very few programs need that, a notable exception is QEMU itself !].
@item No SSE/MMX support (yet).
@item No x86-64 support.
@item Some Linux 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).
@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.
@end itemize
@chapter Invocation
@section Quick Start
In order to launch a Linux process, QEMU needs the process executable
itself and all the target (x86) dynamic libraries used by it.
@itemize
@item On x86, you can just try to launch any process by using the native
libraries:
@example
qemu -L / /bin/ls
@end example
@code{-L /} tells that the x86 dynamic linker must be searched with a
@file{/} prefix.
@item On non x86 CPUs, you need first to download at least an x86 glibc
(@file{qemu-i386-glibc21.tar.gz} on the QEMU web page). Ensure that
@code{LD_LIBRARY_PATH} is not set:
@example
unset LD_LIBRARY_PATH
@end example
Then you can launch the precompiled @file{ls} x86 executable:
@example
qemu /usr/local/qemu-i386/bin/ls-i386
@end example
You can look at @file{/usr/local/qemu-i386/bin/qemu-conf.sh} so that
QEMU is automatically launched by the Linux kernel when you try to
launch x86 executables. It requires the @code{binfmt_misc} module in the
Linux kernel.
@end itemize
@section Wine launch (Currently only tested when emulating x86 on x86)
@itemize
@item Ensure that you have a working QEMU with the x86 glibc
distribution (see previous section). In order to verify it, you must be
able to do:
@example
qemu /usr/local/qemu-i386/bin/ls-i386
@end example
@item Download the binary x86 Wine install
(@file{qemu-i386-wine.tar.gz} on the QEMU web page).
@item Configure Wine on your account. Look at the provided script
@file{/usr/local/qemu-i386/bin/wine-conf.sh}. Your previous
@code{$@{HOME@}/.wine} directory is saved to @code{$@{HOME@}/.wine.org}.
@item Then you can try the example @file{putty.exe}:
@example
qemu /usr/local/qemu-i386/wine/bin/wine /usr/local/qemu-i386/wine/c/Program\ Files/putty.exe
@end example
@end itemize
@section Command line options
@example
usage: qemu [-h] [-d] [-L path] [-s size] program [arguments...]
@end example
@table @samp
@item -h
Print the help
@item -d
Activate log (logfile=/tmp/qemu.log)
@item -L path
Set the x86 elf interpreter prefix (default=/usr/local/qemu-i386)
@item -s size
Set the x86 stack size in bytes (default=524288)
@end table
@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 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.
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]).
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
executables. Such an approach as greater potential because most of the
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.
@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
which make it relatively easily portable and simple while achieving good
performances.
The basic idea is to split every x86 instruction into fewer simpler
instructions. Each simple instruction is implemented by a piece of C
code (see @file{op-i386.c}). Then a compile time tool (@file{dyngen})
takes the corresponding object file (@file{op-i386.o}) to generate a
dynamic code generator which concatenates the simple instructions to
build a function (see @file{op-i386.h:dyngen_code()}).
In essence, the process is similar to [1], but more work is done at
compile time.
A key idea to get optimal performances is that constant parameters can
be passed to the simple operations. For that purpose, dummy ELF
relocations are generated with gcc for each constant parameter. Then,
the tool (@file{dyngen}) can locate the relocations and generate the
appriopriate C code to resolve them when building the dynamic code.
That way, QEMU is no more difficult to port than a dynamic linker.
To go even faster, GCC static register variables are used to keep the
state of the virtual CPU.
@section Register allocation
Since QEMU uses fixed simple instructions, no efficient register
allocation can be done. However, because RISC CPUs have a lot of
register, most of the virtual CPU state can be put in registers without
doing complicated register allocation.
@section Condition code optimisations
Good CPU condition codes emulation (@code{EFLAGS} register on x86) is a
critical point to get good performances. QEMU uses lazy condition code
evaluation: instead of computing the condition codes after each x86
instruction, it just stores one operand (called @code{CC_SRC}), the
result (called @code{CC_DST}) and the type of operation (called
@code{CC_OP}).
@code{CC_OP} is almost never explicitely set in the generated code
because it is known at translation time.
In order to increase performances, a backward pass is performed on the
generated simple instructions (see
@code{translate-i386.c:optimize_flags()}). When it can be proved that
the condition codes are not needed by the next instructions, no
condition codes are computed at all.
@section CPU state optimisations
The x86 CPU has many internal states which change the way it evaluates
instructions. In order to achieve a good speed, the translation phase
considers that some state information of the virtual x86 CPU cannot
change in it. For example, if the SS, DS and ES segments have a zero
base, then the translator does not even generate an addition for the
segment base.
[The FPU stack pointer register is not handled that way yet].
@section Translation cache
A 2MByte cache holds the most recently used translations. For
simplicity, it is completely flushed when it is full. A translation unit
contains just a single basic block (a block of x86 instructions
terminated by a jump or by a virtual CPU state change which the
translator cannot deduce statically).
[Currently, the translated code is not patched if it jumps to another
translated code].
@section Exception support
longjmp() is used when an exception such as division by zero is
encountered. The host SIGSEGV and SIGBUS signal handlers are used to get
invalid memory accesses.
[Currently, the virtual CPU cannot retrieve the exact CPU state in some
exceptions, although it could except for the @code{EFLAGS} register].
@section Linux system call translation
QEMU includes a generic system call translator for Linux. It means that
the parameters of the system calls can be converted to fix the
endianness and 32/64 bit issues. The IOCTLs are converted with a generic
type description system (see @file{ioctls.h} and @file{thunk.c}).
@section Linux signals
Normal and real-time signals are queued along with their information
(@code{siginfo_t}) as it is done in the Linux kernel. Then an interrupt
request is done to the virtual CPU. When it is interrupted, one queued
signal is handled by generating a stack frame in the virtual CPU as the
Linux kernel does. The @code{sigreturn()} system call is emulated to return
from the virtual signal handler.
Some signals (such as SIGALRM) directly come from the host. Other
signals are synthetized from the virtual CPU exceptions such as SIGFPE
when a division by zero is done (see @code{main.c:cpu_loop()}).
The blocked signal mask is still handled by the host Linux kernel so
that most signal system calls can be redirected directly to the host
Linux kernel. Only the @code{sigaction()} and @code{sigreturn()} system
calls need to be fully emulated (see @file{signal.c}).
@section clone() system call and threads
The Linux clone() system call is usually used to create a thread. QEMU
uses the host clone() system call so that real host threads are created
for each emulated thread. One virtual CPU instance is created for each
thread.
The virtual x86 CPU atomic operations are emulated with a global lock so
that their semantic is preserved.
@section Bibliography
@table @asis
@item [1]
@url{http://citeseer.nj.nec.com/piumarta98optimizing.html}, Optimizing
direct threaded code by selective inlining (1998) by Ian Piumarta, Fabio
Riccardi.
@item [2]
@url{http://developer.kde.org/~sewardj/}, Valgrind, an open-source
memory debugger for x86-GNU/Linux, by Julian Seward.
@item [3]
@url{http://bochs.sourceforge.net/}, the Bochs IA-32 Emulator Project,
by Kevin Lawton et al.
@item [4]
@url{http://www.cs.rose-hulman.edu/~donaldlf/em86/index.html}, the EM86
x86 emulator on Alpha-Linux.
@item [5]
@url{http://www.usenix.org/publications/library/proceedings/usenix-nt97/full_papers/chernoff/chernoff.pdf},
DIGITAL FX!32: Running 32-Bit x86 Applications on Alpha NT, by Anton
Chernoff and Ray Hookway.
@item [6]
@url{http://www.willows.com/}, Windows API library emulation from
Willows Software.
@end table
@chapter Regression Tests
In the directory @file{tests/}, various interesting x86 testing programs
are available. There are used for regression testing.
@section @file{hello}
Very simple statically linked x86 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
generates a text output. It can be compared with the output obtained with
a real CPU or another emulator. The target @code{make test} runs this
program and a @code{diff} on the generated output.
The Linux system call @code{modify_ldt()} is used to create x86 selectors
to test some 16 bit addressing and 32 bit with segmentation cases.
@section @file{testsig}
This program tests various signal cases, including SIGFPE, SIGSEGV and
SIGILL.
@section @file{testclone}
Tests the @code{clone()} system call (basic test).
@section @file{testthread}
Tests the glibc threads (more complicated than @code{clone()} because signals
are also used).
@section @file{sha1}
It is a simple benchmark. Care must be taken to interpret the results
because it mostly tests the ability of the virtual CPU to optimize the
@code{rol} x86 instruction and the condition code computations.
@section @file{runcom}
A very simple MSDOS emulator to test the Linux vm86() system call
emulation. The excellent 54 byte @file{pi_10.com} PI number calculator
can be launched with it. @file{pi_10.com} was written by Bertram
Felgenhauer (more information at @url{http://www.boo.net/~jasonp/pipage.html}).

View File

@@ -237,6 +237,36 @@
#define TARGET_NR_removexattr 235
#define TARGET_NR_lremovexattr 236
#define TARGET_NR_fremovexattr 237
#define TARGET_NR_tkill 238
#define TARGET_NR_sendfile64 239
#define TARGET_NR_futex 240
#define TARGET_NR_sched_setaffinity 241
#define TARGET_NR_sched_getaffinity 242
#define TARGET_NR_set_thread_area 243
#define TARGET_NR_get_thread_area 244
#define TARGET_NR_io_setup 245
#define TARGET_NR_io_destroy 246
#define TARGET_NR_io_getevents 247
#define TARGET_NR_io_submit 248
#define TARGET_NR_io_cancel 249
#define TARGET_NR_fadvise64 250
#define TARGET_NR_exit_group 252
#define TARGET_NR_lookup_dcookie 253
#define TARGET_NR_epoll_create 254
#define TARGET_NR_epoll_ctl 255
#define TARGET_NR_epoll_wait 256
#define TARGET_NR_remap_file_pages 257
#define TARGET_NR_set_tid_address 258
#define TARGET_NR_timer_create 259
#define TARGET_NR_timer_settime (TARGET_NR_timer_create+1)
#define TARGET_NR_timer_gettime (TARGET_NR_timer_create+2)
#define TARGET_NR_timer_getoverrun (TARGET_NR_timer_create+3)
#define TARGET_NR_timer_delete (TARGET_NR_timer_create+4)
#define TARGET_NR_clock_settime (TARGET_NR_timer_create+5)
#define TARGET_NR_clock_gettime (TARGET_NR_timer_create+6)
#define TARGET_NR_clock_getres (TARGET_NR_timer_create+7)
#define TARGET_NR_clock_nanosleep (TARGET_NR_timer_create+8)
#define TARGET_SIG_BLOCK 0 /* for blocking signals */
#define TARGET_SIG_UNBLOCK 1 /* for unblocking signals */
@@ -255,11 +285,11 @@ struct target_stat {
target_ulong st_size;
target_ulong st_blksize;
target_ulong st_blocks;
target_ulong st_atime;
target_ulong target_st_atime;
target_ulong __unused1;
target_ulong st_mtime;
target_ulong target_st_mtime;
target_ulong __unused2;
target_ulong st_ctime;
target_ulong target_st_ctime;
target_ulong __unused3;
target_ulong __unused4;
target_ulong __unused5;
@@ -290,13 +320,13 @@ struct target_stat64 {
target_ulong st_blocks; /* Number 512-byte blocks allocated. */
target_ulong __pad4; /* future possible st_blocks high bits */
target_ulong st_atime;
target_ulong target_st_atime;
target_ulong __pad5;
target_ulong st_mtime;
target_ulong target_st_mtime;
target_ulong __pad6;
target_ulong st_ctime;
target_ulong target_st_ctime;
target_ulong __pad7; /* will be high 32 bits of ctime someday */
unsigned long long st_ino;
@@ -714,6 +744,10 @@ struct target_termios {
#define TARGET_LDT_ENTRIES 8192
#define TARGET_LDT_ENTRY_SIZE 8
#define TARGET_GDT_ENTRY_TLS_ENTRIES 3
#define TARGET_GDT_ENTRY_TLS_MIN 6
#define TARGET_GDT_ENTRY_TLS_MAX (TARGET_GDT_ENTRY_TLS_MIN + TARGET_GDT_ENTRY_TLS_ENTRIES - 1)
struct target_modify_ldt_ldt_s {
unsigned int entry_number;
target_ulong base_addr;
@@ -721,6 +755,182 @@ struct target_modify_ldt_ldt_s {
unsigned int flags;
};
#define TARGET_VM86_SIGNAL 0 /* return due to signal */
#define TARGET_VM86_UNKNOWN 1 /* unhandled GP fault - IO-instruction or similar */
#define TARGET_VM86_INTx 2 /* int3/int x instruction (ARG = x) */
#define TARGET_VM86_STI 3 /* sti/popf/iret instruction enabled virtual interrupts */
/*
* Additional return values when invoking new vm86()
*/
#define TARGET_VM86_PICRETURN 4 /* return due to pending PIC request */
#define TARGET_VM86_TRAP 6 /* return due to DOS-debugger request */
/*
* function codes when invoking new vm86()
*/
#define TARGET_VM86_PLUS_INSTALL_CHECK 0
#define TARGET_VM86_ENTER 1
#define TARGET_VM86_ENTER_NO_BYPASS 2
#define TARGET_VM86_REQUEST_IRQ 3
#define TARGET_VM86_FREE_IRQ 4
#define TARGET_VM86_GET_IRQ_BITS 5
#define TARGET_VM86_GET_AND_RESET_IRQ 6
/*
* This is the stack-layout seen by the user space program when we have
* done a translation of "SAVE_ALL" from vm86 mode. The real kernel layout
* is 'kernel_vm86_regs' (see below).
*/
struct target_vm86_regs {
/*
* normal regs, with special meaning for the segment descriptors..
*/
target_long ebx;
target_long ecx;
target_long edx;
target_long esi;
target_long edi;
target_long ebp;
target_long eax;
target_long __null_ds;
target_long __null_es;
target_long __null_fs;
target_long __null_gs;
target_long orig_eax;
target_long eip;
unsigned short cs, __csh;
target_long eflags;
target_long esp;
unsigned short ss, __ssh;
/*
* these are specific to v86 mode:
*/
unsigned short es, __esh;
unsigned short ds, __dsh;
unsigned short fs, __fsh;
unsigned short gs, __gsh;
};
struct target_revectored_struct {
target_ulong __map[8]; /* 256 bits */
};
struct target_vm86_struct {
struct target_vm86_regs regs;
target_ulong flags;
target_ulong screen_bitmap;
target_ulong cpu_type;
struct target_revectored_struct int_revectored;
struct target_revectored_struct int21_revectored;
};
/*
* flags masks
*/
#define TARGET_VM86_SCREEN_BITMAP 0x0001
struct target_vm86plus_info_struct {
target_ulong flags;
#define TARGET_force_return_for_pic (1 << 0)
#define TARGET_vm86dbg_active (1 << 1) /* for debugger */
#define TARGET_vm86dbg_TFpendig (1 << 2) /* for debugger */
#define TARGET_is_vm86pus (1 << 31) /* for vm86 internal use */
unsigned char vm86dbg_intxxtab[32]; /* for debugger */
};
struct target_vm86plus_struct {
struct target_vm86_regs regs;
target_ulong flags;
target_ulong screen_bitmap;
target_ulong cpu_type;
struct target_revectored_struct int_revectored;
struct target_revectored_struct int21_revectored;
struct target_vm86plus_info_struct vm86plus;
};
/* ipcs */
#define TARGET_SEMOP 1
#define TARGET_SEMGET 2
#define TARGET_SEMCTL 3
#define TARGET_MSGSND 11
#define TARGET_MSGRCV 12
#define TARGET_MSGGET 13
#define TARGET_MSGCTL 14
#define TARGET_SHMAT 21
#define TARGET_SHMDT 22
#define TARGET_SHMGET 23
#define TARGET_SHMCTL 24
struct target_msgbuf {
int mtype;
char mtext[1];
};
struct target_ipc_kludge {
unsigned int msgp; /* Really (struct msgbuf *) */
int msgtyp;
};
struct alpha_msgbuf {
long mtype;
char mtext[4096];
};
struct target_ipc_perm {
int key;
unsigned short uid;
unsigned short gid;
unsigned short cuid;
unsigned short cgid;
unsigned short mode;
unsigned short seq;
};
struct target_msqid_ds {
struct target_ipc_perm msg_perm;
unsigned int msg_first; /* really struct target_msg* */
unsigned int msg_last; /* really struct target_msg* */
unsigned int msg_stime; /* really target_time_t */
unsigned int msg_rtime; /* really target_time_t */
unsigned int msg_ctime; /* really target_time_t */
unsigned int wwait; /* really struct wait_queue* */
unsigned int rwait; /* really struct wait_queue* */
unsigned short msg_cbytes;
unsigned short msg_qnum;
unsigned short msg_qbytes;
unsigned short msg_lspid;
unsigned short msg_lrpid;
};
struct target_shmid_ds {
struct target_ipc_perm shm_perm;
int shm_segsz;
unsigned int shm_atime; /* really target_time_t */
unsigned int shm_dtime; /* really target_time_t */
unsigned int shm_ctime; /* really target_time_t */
unsigned short shm_cpid;
unsigned short shm_lpid;
short shm_nattch;
unsigned short shm_npages;
unsigned long *shm_pages;
void *attaches; /* really struct shm_desc * */
};
#define TARGET_IPC_RMID 0
#define TARGET_IPC_SET 1
#define TARGET_IPC_STAT 2
union target_semun {
int val;
unsigned int buf; /* really struct semid_ds * */
unsigned int array; /* really unsigned short * */
unsigned int __buf; /* really struct seminfo * */
unsigned int __pad; /* really void* */
};
/* soundcard defines (XXX: move them to generic file syscall_defs.h) */
#define TARGET_SNDCTL_COPR_HALT 0xc0144307

View File

@@ -4,7 +4,7 @@ CFLAGS=-Wall -O2 -g
LDFLAGS=
ifeq ($(ARCH),i386)
TESTS=testclone testsig testthread sha1-i386 test-i386
TESTS=testclone testsig testthread sha1-i386 test-i386 runcom
endif
TESTS+=sha1
@@ -28,7 +28,7 @@ testthread: testthread.c
# i386 emulation test (test various opcodes) */
test-i386: test-i386.c test-i386-code16.S \
test-i386.h test-i386-shift.h test-i386-muldiv.h
$(CC) $(CFLAGS) $(LDFLAGS) -static -o $@ test-i386.c test-i386-code16.S -lm
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ test-i386.c test-i386-code16.S -lm
test: test-i386
ifeq ($(ARCH),i386)
@@ -48,5 +48,9 @@ speed: sha1 sha1-i386
time ./sha1
time $(QEMU) ./sha1-i386
# vm86 test
runcom: runcom.c
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
clean:
rm -f *~ *.o $(TESTS)

View File

@@ -714,6 +714,10 @@ void test_segs(void)
long long ldt_table[3];
int res, res2;
char tmp;
struct {
uint32_t offset;
uint16_t seg;
} __attribute__((packed)) segoff;
ldt.entry_number = 1;
ldt.base_addr = (unsigned long)&seg_data1;
@@ -772,6 +776,14 @@ void test_segs(void)
: "r" (MK_SEL(1)), "r" (&tmp));
printf("DS[1] = %02x\n", res);
printf("SS[tmp] = %02x\n", res2);
segoff.seg = MK_SEL(2);
segoff.offset = 0xabcdef12;
asm volatile("lfs %2, %0\n\t"
"movl %%fs, %1\n\t"
: "=r" (res), "=g" (res2)
: "m" (segoff));
printf("FS:reg = %04x:%08x\n", res2, res);
}
/* 16 bit code test */
@@ -812,6 +824,71 @@ void test_code16(void)
printf("func3() = 0x%08x\n", res);
}
void test_misc(void)
{
char table[256];
int res, i;
for(i=0;i<256;i++) table[i] = 256 - i;
res = 0x12345678;
asm ("xlat" : "=a" (res) : "b" (table), "0" (res));
printf("xlat: EAX=%08x\n", res);
}
uint8_t str_buffer[4096];
#define TEST_STRING1(OP, size, DF, REP)\
{\
int esi, edi, eax, ecx, eflags;\
\
esi = (long)(str_buffer + sizeof(str_buffer) / 2);\
edi = (long)(str_buffer + sizeof(str_buffer) / 2) + 16;\
eax = 0x12345678;\
ecx = 17;\
\
asm volatile ("pushl $0\n\t"\
"popf\n\t"\
DF "\n\t"\
REP #OP size "\n\t"\
"cld\n\t"\
"pushf\n\t"\
"popl %4\n\t"\
: "=S" (esi), "=D" (edi), "=a" (eax), "=c" (ecx), "=g" (eflags)\
: "0" (esi), "1" (edi), "2" (eax), "3" (ecx));\
printf("%-10s ESI=%08x EDI=%08x EAX=%08x ECX=%08x EFL=%04x\n",\
REP #OP size, esi, edi, eax, ecx,\
eflags & (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A));\
}
#define TEST_STRING(OP, REP)\
TEST_STRING1(OP, "b", "", REP);\
TEST_STRING1(OP, "w", "", REP);\
TEST_STRING1(OP, "l", "", REP);\
TEST_STRING1(OP, "b", "std", REP);\
TEST_STRING1(OP, "w", "std", REP);\
TEST_STRING1(OP, "l", "std", REP)
void test_string(void)
{
int i;
for(i = 0;i < sizeof(str_buffer); i++)
str_buffer[i] = i + 0x56;
TEST_STRING(stos, "");
TEST_STRING(stos, "rep ");
TEST_STRING(lods, ""); /* to verify stos */
TEST_STRING(lods, "rep ");
TEST_STRING(movs, "");
TEST_STRING(movs, "rep ");
TEST_STRING(lods, ""); /* to verify stos */
/* XXX: better tests */
TEST_STRING(scas, "");
TEST_STRING(scas, "repz ");
TEST_STRING(scas, "repnz ");
TEST_STRING(cmps, "");
TEST_STRING(cmps, "repz ");
TEST_STRING(cmps, "repnz ");
}
static void *call_end __init_call = NULL;
@@ -831,6 +908,8 @@ int main(int argc, char **argv)
test_floats();
test_bcd();
test_xchg();
test_string();
test_misc();
test_lea();
test_segs();
test_code16();

View File

@@ -15,21 +15,34 @@ void alarm_handler(int sig)
alarm(1);
}
#ifndef REG_EAX
#define REG_EAX EAX
#define REG_EBX EBX
#define REG_ECX ECX
#define REG_EDX EDX
#define REG_ESI ESI
#define REG_EDI EDI
#define REG_EBP EBP
#define REG_ESP ESP
#define REG_EIP EIP
#define REG_EFL EFL
#endif
void dump_regs(struct ucontext *uc)
{
printf("EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n"
"ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n"
"EFL=%08x EIP=%08x\n",
uc->uc_mcontext.gregs[EAX],
uc->uc_mcontext.gregs[EBX],
uc->uc_mcontext.gregs[ECX],
uc->uc_mcontext.gregs[EDX],
uc->uc_mcontext.gregs[ESI],
uc->uc_mcontext.gregs[EDI],
uc->uc_mcontext.gregs[EBP],
uc->uc_mcontext.gregs[ESP],
uc->uc_mcontext.gregs[EFL],
uc->uc_mcontext.gregs[EIP]);
uc->uc_mcontext.gregs[REG_EAX],
uc->uc_mcontext.gregs[REG_EBX],
uc->uc_mcontext.gregs[REG_ECX],
uc->uc_mcontext.gregs[REG_EDX],
uc->uc_mcontext.gregs[REG_ESI],
uc->uc_mcontext.gregs[REG_EDI],
uc->uc_mcontext.gregs[REG_EBP],
uc->uc_mcontext.gregs[REG_ESP],
uc->uc_mcontext.gregs[REG_EFL],
uc->uc_mcontext.gregs[REG_EIP]);
}
void sig_handler(int sig, siginfo_t *info, void *puc)

View File

@@ -50,6 +50,12 @@ static inline void flush_icache_range(unsigned long start, unsigned long stop)
}
#endif
#ifdef __s390__
static inline void flush_icache_range(unsigned long start, unsigned long stop)
{
}
#endif
#ifdef __powerpc__
#define MIN_CACHE_LINE_SIZE 8 /* conservative value */
@@ -76,21 +82,16 @@ static void inline flush_icache_range(unsigned long start, unsigned long stop)
extern FILE *logfile;
extern int loglevel;
#define PREFIX_REPZ 1
#define PREFIX_REPNZ 2
#define PREFIX_LOCK 4
#define PREFIX_CS 8
#define PREFIX_SS 0x10
#define PREFIX_DS 0x20
#define PREFIX_ES 0x40
#define PREFIX_FS 0x80
#define PREFIX_GS 0x100
#define PREFIX_DATA 0x200
#define PREFIX_ADR 0x400
#define PREFIX_FWAIT 0x800
#define PREFIX_REPZ 0x01
#define PREFIX_REPNZ 0x02
#define PREFIX_LOCK 0x04
#define PREFIX_DATA 0x08
#define PREFIX_ADR 0x10
#define PREFIX_FWAIT 0x20
typedef struct DisasContext {
/* current insn context */
int override; /* -1 if no override */
int prefix;
int aflag, dflag;
uint8_t *pc; /* pc = eip + cs_base */
@@ -103,6 +104,7 @@ typedef struct DisasContext {
int cc_op; /* current CC operation */
int addseg; /* non zero if either DS/ES/SS have a non zero base */
int f_st; /* currently unused */
int vm86; /* vm86 mode */
} DisasContext;
/* i386 arith/logic operations */
@@ -130,7 +132,7 @@ enum {
};
enum {
#define DEF(s) INDEX_op_ ## s,
#define DEF(s, n) INDEX_op_ ## s,
#include "opc-i386.h"
#undef DEF
NB_OPS,
@@ -556,76 +558,100 @@ static GenOpFunc *gen_op_st_T0_A0[3] = {
gen_op_stl_T0_A0,
};
static GenOpFunc *gen_op_movs[6] = {
gen_op_movsb,
gen_op_movsw,
gen_op_movsl,
gen_op_rep_movsb,
gen_op_rep_movsw,
gen_op_rep_movsl,
/* the _a32 and _a16 string operations use A0 as the base register. */
#define STRINGOP(x) \
gen_op_ ## x ## b_fast, \
gen_op_ ## x ## w_fast, \
gen_op_ ## x ## l_fast, \
gen_op_ ## x ## b_a32, \
gen_op_ ## x ## w_a32, \
gen_op_ ## x ## l_a32, \
gen_op_ ## x ## b_a16, \
gen_op_ ## x ## w_a16, \
gen_op_ ## x ## l_a16,
static GenOpFunc *gen_op_movs[9 * 2] = {
STRINGOP(movs)
STRINGOP(rep_movs)
};
static GenOpFunc *gen_op_stos[6] = {
gen_op_stosb,
gen_op_stosw,
gen_op_stosl,
gen_op_rep_stosb,
gen_op_rep_stosw,
gen_op_rep_stosl,
static GenOpFunc *gen_op_stos[9 * 2] = {
STRINGOP(stos)
STRINGOP(rep_stos)
};
static GenOpFunc *gen_op_lods[6] = {
gen_op_lodsb,
gen_op_lodsw,
gen_op_lodsl,
gen_op_rep_lodsb,
gen_op_rep_lodsw,
gen_op_rep_lodsl,
static GenOpFunc *gen_op_lods[9 * 2] = {
STRINGOP(lods)
STRINGOP(rep_lods)
};
static GenOpFunc *gen_op_scas[9] = {
gen_op_scasb,
gen_op_scasw,
gen_op_scasl,
gen_op_repz_scasb,
gen_op_repz_scasw,
gen_op_repz_scasl,
gen_op_repnz_scasb,
gen_op_repnz_scasw,
gen_op_repnz_scasl,
static GenOpFunc *gen_op_scas[9 * 3] = {
STRINGOP(scas)
STRINGOP(repz_scas)
STRINGOP(repnz_scas)
};
static GenOpFunc *gen_op_cmps[9] = {
gen_op_cmpsb,
gen_op_cmpsw,
gen_op_cmpsl,
gen_op_repz_cmpsb,
gen_op_repz_cmpsw,
gen_op_repz_cmpsl,
gen_op_repnz_cmpsb,
gen_op_repnz_cmpsw,
gen_op_repnz_cmpsl,
static GenOpFunc *gen_op_cmps[9 * 3] = {
STRINGOP(cmps)
STRINGOP(repz_cmps)
STRINGOP(repnz_cmps)
};
static GenOpFunc *gen_op_ins[6] = {
gen_op_insb,
gen_op_insw,
gen_op_insl,
gen_op_rep_insb,
gen_op_rep_insw,
gen_op_rep_insl,
static GenOpFunc *gen_op_ins[9 * 2] = {
STRINGOP(ins)
STRINGOP(rep_ins)
};
static GenOpFunc *gen_op_outs[6] = {
gen_op_outsb,
gen_op_outsw,
gen_op_outsl,
gen_op_rep_outsb,
gen_op_rep_outsw,
gen_op_rep_outsl,
static GenOpFunc *gen_op_outs[9 * 2] = {
STRINGOP(outs)
STRINGOP(rep_outs)
};
static inline void gen_string_ds(DisasContext *s, int ot, GenOpFunc **func)
{
int index, override;
override = s->override;
if (s->aflag) {
/* 32 bit address */
if (s->addseg && override < 0)
override = R_DS;
if (override >= 0) {
gen_op_movl_A0_seg(offsetof(CPUX86State,seg_cache[override].base));
index = 3 + ot;
} else {
index = ot;
}
} else {
if (override < 0)
override = R_DS;
gen_op_movl_A0_seg(offsetof(CPUX86State,seg_cache[override].base));
/* 16 address, always override */
index = 6 + ot;
}
func[index]();
}
static inline void gen_string_es(DisasContext *s, int ot, GenOpFunc **func)
{
int index;
if (s->aflag) {
if (s->addseg) {
index = 3 + ot;
} else {
index = ot;
}
} else {
index = 6 + ot;
}
func[index]();
}
static GenOpFunc *gen_op_in[3] = {
gen_op_inb_T0_T1,
gen_op_inw_T0_T1,
@@ -849,26 +875,10 @@ static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_
int opreg;
int mod, rm, code, override, must_add_seg;
/* XXX: add a generation time variable to tell if base == 0 in DS/ES/SS */
override = -1;
override = s->override;
must_add_seg = s->addseg;
if (s->prefix & (PREFIX_CS | PREFIX_SS | PREFIX_DS |
PREFIX_ES | PREFIX_FS | PREFIX_GS)) {
if (s->prefix & PREFIX_ES)
override = R_ES;
else if (s->prefix & PREFIX_CS)
override = R_CS;
else if (s->prefix & PREFIX_SS)
override = R_SS;
else if (s->prefix & PREFIX_DS)
override = R_DS;
else if (s->prefix & PREFIX_FS)
override = R_FS;
else
override = R_GS;
if (override >= 0)
must_add_seg = 1;
}
mod = (modrm >> 6) & 3;
rm = modrm & 7;
@@ -1343,7 +1353,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
prefixes = 0;
aflag = s->code32;
dflag = s->code32;
// cur_pc = s->pc; /* for insn generation */
s->override = -1;
next_byte:
b = ldub(s->pc);
s->pc++;
@@ -1359,22 +1369,22 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
prefixes |= PREFIX_LOCK;
goto next_byte;
case 0x2e:
prefixes |= PREFIX_CS;
s->override = R_CS;
goto next_byte;
case 0x36:
prefixes |= PREFIX_SS;
s->override = R_SS;
goto next_byte;
case 0x3e:
prefixes |= PREFIX_DS;
s->override = R_DS;
goto next_byte;
case 0x26:
prefixes |= PREFIX_ES;
s->override = R_ES;
goto next_byte;
case 0x64:
prefixes |= PREFIX_FS;
s->override = R_FS;
goto next_byte;
case 0x65:
prefixes |= PREFIX_GS;
s->override = R_GS;
goto next_byte;
case 0x66:
prefixes |= PREFIX_DATA;
@@ -1830,6 +1840,17 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
}
s->cc_op = CC_OP_SUBB + ot;
break;
case 0x1c7: /* cmpxchg8b */
modrm = ldub(s->pc++);
mod = (modrm >> 6) & 3;
if (mod == 3)
goto illegal_op;
if (s->cc_op != CC_OP_DYNAMIC)
gen_op_set_cc_op(s->cc_op);
gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
gen_op_cmpxchg8b();
s->cc_op = CC_OP_EFLAGS;
break;
/**************************/
/* push/pop */
@@ -2027,8 +2048,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
modrm = ldub(s->pc++);
reg = (modrm >> 3) & 7;
/* we must ensure that no segment is added */
s->prefix &= ~(PREFIX_CS | PREFIX_SS | PREFIX_DS |
PREFIX_ES | PREFIX_FS | PREFIX_GS);
s->override = -1;
val = s->addseg;
s->addseg = 0;
gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
@@ -2050,26 +2070,14 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
offset_addr = insn_get(s, OT_WORD);
gen_op_movl_A0_im(offset_addr);
/* handle override */
/* XXX: factorize that */
{
int override, must_add_seg;
override = R_DS;
must_add_seg = s->addseg;
if (s->prefix & (PREFIX_CS | PREFIX_SS | PREFIX_DS |
PREFIX_ES | PREFIX_FS | PREFIX_GS)) {
if (s->prefix & PREFIX_ES)
override = R_ES;
else if (s->prefix & PREFIX_CS)
override = R_CS;
else if (s->prefix & PREFIX_SS)
override = R_SS;
else if (s->prefix & PREFIX_DS)
override = R_DS;
else if (s->prefix & PREFIX_FS)
override = R_FS;
else
override = R_GS;
if (s->override >= 0) {
override = s->override;
must_add_seg = 1;
} else {
override = R_DS;
}
if (must_add_seg) {
gen_op_addl_A0_seg(offsetof(CPUX86State,seg_cache[override].base));
@@ -2083,7 +2091,29 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
gen_op_st_T0_A0[ot]();
}
break;
case 0xd7: /* xlat */
gen_op_movl_A0_reg[R_EBX]();
gen_op_addl_A0_AL();
if (s->aflag == 0)
gen_op_andl_A0_ffff();
/* handle override */
{
int override, must_add_seg;
must_add_seg = s->addseg;
override = R_DS;
if (s->override >= 0) {
override = s->override;
must_add_seg = 1;
} else {
override = R_DS;
}
if (must_add_seg) {
gen_op_addl_A0_seg(offsetof(CPUX86State,seg_cache[override].base));
}
}
gen_op_ldub_T0_A0();
gen_op_mov_reg_T0[OT_BYTE][R_EAX]();
break;
case 0xb0 ... 0xb7: /* mov R, Ib */
val = insn_get(s, OT_BYTE);
gen_op_movl_T0_im(val);
@@ -2121,8 +2151,13 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
} else {
gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
gen_op_mov_TN_reg[ot][0][reg]();
/* for xchg, lock is implicit */
if (!(prefixes & PREFIX_LOCK))
gen_op_lock();
gen_op_ld_T1_A0[ot]();
gen_op_st_T0_A0[ot]();
if (!(prefixes & PREFIX_LOCK))
gen_op_unlock();
gen_op_mov_reg_T1[ot][reg]();
}
break;
@@ -2147,6 +2182,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
mod = (modrm >> 6) & 3;
if (mod == 3)
goto illegal_op;
gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
gen_op_ld_T1_A0[ot]();
gen_op_addl_A0_im(1 << (ot - OT_WORD + 1));
/* load the segment first to handle exceptions properly */
@@ -2620,16 +2656,18 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
break;
/************************/
/* string ops */
case 0xa4: /* movsS */
case 0xa5:
if ((b & 1) == 0)
ot = OT_BYTE;
else
ot = dflag ? OT_LONG : OT_WORD;
if (prefixes & PREFIX_REPZ) {
gen_op_movs[3 + ot]();
gen_string_ds(s, ot, gen_op_movs + 9);
} else {
gen_op_movs[ot]();
gen_string_ds(s, ot, gen_op_movs);
}
break;
@@ -2639,10 +2677,11 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
ot = OT_BYTE;
else
ot = dflag ? OT_LONG : OT_WORD;
if (prefixes & PREFIX_REPZ) {
gen_op_stos[3 + ot]();
gen_string_es(s, ot, gen_op_stos + 9);
} else {
gen_op_stos[ot]();
gen_string_es(s, ot, gen_op_stos);
}
break;
case 0xac: /* lodsS */
@@ -2652,9 +2691,9 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
else
ot = dflag ? OT_LONG : OT_WORD;
if (prefixes & PREFIX_REPZ) {
gen_op_lods[3 + ot]();
gen_string_ds(s, ot, gen_op_lods + 9);
} else {
gen_op_lods[ot]();
gen_string_ds(s, ot, gen_op_lods);
}
break;
case 0xae: /* scasS */
@@ -2662,19 +2701,19 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
if ((b & 1) == 0)
ot = OT_BYTE;
else
ot = dflag ? OT_LONG : OT_WORD;
ot = dflag ? OT_LONG : OT_WORD;
if (prefixes & PREFIX_REPNZ) {
if (s->cc_op != CC_OP_DYNAMIC)
gen_op_set_cc_op(s->cc_op);
gen_op_scas[6 + ot]();
gen_string_es(s, ot, gen_op_scas + 9 * 2);
s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */
} else if (prefixes & PREFIX_REPZ) {
if (s->cc_op != CC_OP_DYNAMIC)
gen_op_set_cc_op(s->cc_op);
gen_op_scas[3 + ot]();
gen_string_es(s, ot, gen_op_scas + 9);
s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */
} else {
gen_op_scas[ot]();
gen_string_es(s, ot, gen_op_scas);
s->cc_op = CC_OP_SUBB + ot;
}
break;
@@ -2688,21 +2727,18 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
if (prefixes & PREFIX_REPNZ) {
if (s->cc_op != CC_OP_DYNAMIC)
gen_op_set_cc_op(s->cc_op);
gen_op_cmps[6 + ot]();
gen_string_ds(s, ot, gen_op_cmps + 9 * 2);
s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */
} else if (prefixes & PREFIX_REPZ) {
if (s->cc_op != CC_OP_DYNAMIC)
gen_op_set_cc_op(s->cc_op);
gen_op_cmps[3 + ot]();
gen_string_ds(s, ot, gen_op_cmps + 9);
s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */
} else {
gen_op_cmps[ot]();
gen_string_ds(s, ot, gen_op_cmps);
s->cc_op = CC_OP_SUBB + ot;
}
break;
/************************/
/* port I/O */
case 0x6c: /* insS */
case 0x6d:
if ((b & 1) == 0)
@@ -2710,9 +2746,9 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
else
ot = dflag ? OT_LONG : OT_WORD;
if (prefixes & PREFIX_REPZ) {
gen_op_ins[3 + ot]();
gen_string_es(s, ot, gen_op_ins + 9);
} else {
gen_op_ins[ot]();
gen_string_es(s, ot, gen_op_ins);
}
break;
case 0x6e: /* outsS */
@@ -2722,11 +2758,14 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
else
ot = dflag ? OT_LONG : OT_WORD;
if (prefixes & PREFIX_REPZ) {
gen_op_outs[3 + ot]();
gen_string_ds(s, ot, gen_op_outs + 9);
} else {
gen_op_outs[ot]();
gen_string_ds(s, ot, gen_op_outs);
}
break;
/************************/
/* port I/O */
case 0xe4:
case 0xe5:
if ((b & 1) == 0)
@@ -3112,14 +3151,27 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
case 0xcd: /* int N */
val = ldub(s->pc++);
/* XXX: currently we ignore the interrupt number */
gen_op_int_im((long)pc_start);
gen_op_int_im(pc_start - s->cs_base);
s->is_jmp = 1;
break;
case 0xce: /* into */
if (s->cc_op != CC_OP_DYNAMIC)
gen_op_set_cc_op(s->cc_op);
gen_op_into((long)pc_start, (long)s->pc);
s->is_jmp = 1;
gen_op_into();
break;
case 0x62: /* bound */
ot = dflag ? OT_LONG : OT_WORD;
modrm = ldub(s->pc++);
reg = (modrm >> 3) & 7;
mod = (modrm >> 6) & 3;
if (mod == 3)
goto illegal_op;
gen_op_mov_reg_T0[ot][reg]();
gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
if (ot == OT_WORD)
gen_op_boundw();
else
gen_op_boundl();
break;
case 0x1c8 ... 0x1cf: /* bswap reg */
reg = b & 7;
@@ -3150,11 +3202,9 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
case 0x131: /* rdtsc */
gen_op_rdtsc();
break;
#if 0
case 0x1a2: /* cpuid */
gen_insn0(OP_ASM);
gen_op_cpuid();
break;
#endif
default:
goto illegal_op;
}
@@ -3361,28 +3411,30 @@ static uint16_t opc_write_flags[NB_OPS] = {
[INDEX_op_bsrw_T0_cc] = CC_OSZAPC,
[INDEX_op_bsrl_T0_cc] = CC_OSZAPC,
[INDEX_op_scasb] = CC_OSZAPC,
[INDEX_op_scasw] = CC_OSZAPC,
[INDEX_op_scasl] = CC_OSZAPC,
[INDEX_op_repz_scasb] = CC_OSZAPC,
[INDEX_op_repz_scasw] = CC_OSZAPC,
[INDEX_op_repz_scasl] = CC_OSZAPC,
[INDEX_op_repnz_scasb] = CC_OSZAPC,
[INDEX_op_repnz_scasw] = CC_OSZAPC,
[INDEX_op_repnz_scasl] = CC_OSZAPC,
#undef STRINGOP
#define STRINGOP(x) \
[INDEX_op_ ## x ## b_fast] = CC_OSZAPC, \
[INDEX_op_ ## x ## w_fast] = CC_OSZAPC, \
[INDEX_op_ ## x ## l_fast] = CC_OSZAPC, \
[INDEX_op_ ## x ## b_a32] = CC_OSZAPC, \
[INDEX_op_ ## x ## w_a32] = CC_OSZAPC, \
[INDEX_op_ ## x ## l_a32] = CC_OSZAPC, \
[INDEX_op_ ## x ## b_a16] = CC_OSZAPC, \
[INDEX_op_ ## x ## w_a16] = CC_OSZAPC, \
[INDEX_op_ ## x ## l_a16] = CC_OSZAPC,
[INDEX_op_cmpsb] = CC_OSZAPC,
[INDEX_op_cmpsw] = CC_OSZAPC,
[INDEX_op_cmpsl] = CC_OSZAPC,
[INDEX_op_repz_cmpsb] = CC_OSZAPC,
[INDEX_op_repz_cmpsw] = CC_OSZAPC,
[INDEX_op_repz_cmpsl] = CC_OSZAPC,
[INDEX_op_repnz_cmpsb] = CC_OSZAPC,
[INDEX_op_repnz_cmpsw] = CC_OSZAPC,
[INDEX_op_repnz_cmpsl] = CC_OSZAPC,
STRINGOP(scas)
STRINGOP(repz_scas)
STRINGOP(repnz_scas)
STRINGOP(cmps)
STRINGOP(repz_cmps)
STRINGOP(repnz_cmps)
[INDEX_op_cmpxchgb_T0_T1_EAX_cc] = CC_OSZAPC,
[INDEX_op_cmpxchgw_T0_T1_EAX_cc] = CC_OSZAPC,
[INDEX_op_cmpxchgl_T0_T1_EAX_cc] = CC_OSZAPC,
[INDEX_op_cmpxchg8b] = CC_Z,
};
/* simpler form of an operation if no flags need to be generated */
@@ -3457,21 +3509,36 @@ static void optimize_flags(uint16_t *opc_buf, int opc_buf_len)
#ifdef DEBUG_DISAS
static const char *op_str[] = {
#define DEF(s) #s,
#define DEF(s, n) #s,
#include "opc-i386.h"
#undef DEF
};
static void dump_ops(const uint16_t *opc_buf)
static uint8_t op_nb_args[] = {
#define DEF(s, n) n,
#include "opc-i386.h"
#undef DEF
};
static void dump_ops(const uint16_t *opc_buf, const uint32_t *opparam_buf)
{
const uint16_t *opc_ptr;
int c;
const uint32_t *opparam_ptr;
int c, n, i;
opc_ptr = opc_buf;
opparam_ptr = opparam_buf;
for(;;) {
c = *opc_ptr++;
fprintf(logfile, "0x%04x: %s\n", opc_ptr - opc_buf - 1, op_str[c]);
n = op_nb_args[c];
fprintf(logfile, "0x%04x: %s", opc_ptr - opc_buf - 1, op_str[c]);
for(i = 0; i < n; i++) {
fprintf(logfile, " 0x%x", opparam_ptr[i]);
}
fprintf(logfile, "\n");
if (c == INDEX_op_end)
break;
opparam_ptr += n;
}
}
@@ -3509,6 +3576,7 @@ int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size,
dc->ss32 = (flags >> GEN_FLAG_SS32_SHIFT) & 1;
dc->addseg = (flags >> GEN_FLAG_ADDSEG_SHIFT) & 1;
dc->f_st = (flags >> GEN_FLAG_ST_SHIFT) & 7;
dc->vm86 = (flags >> GEN_FLAG_VM_SHIFT) & 1;
dc->cc_op = CC_OP_DYNAMIC;
dc->cs_base = cs_base;
@@ -3572,7 +3640,7 @@ int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size,
fprintf(logfile, "\n");
fprintf(logfile, "OP:\n");
dump_ops(gen_opc_buf);
dump_ops(gen_opc_buf, gen_opparam_buf);
fprintf(logfile, "\n");
}
#endif
@@ -3583,7 +3651,7 @@ int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size,
#ifdef DEBUG_DISAS
if (loglevel) {
fprintf(logfile, "AFTER FLAGS OPT:\n");
dump_ops(gen_opc_buf);
dump_ops(gen_opc_buf, gen_opparam_buf);
fprintf(logfile, "\n");
}
#endif
@@ -3645,8 +3713,8 @@ CPUX86State *cpu_x86_init(void)
for(i = 0;i < 8; i++)
env->fptags[i] = 1;
env->fpuc = 0x37f;
/* flags setup */
env->eflags = 0;
/* flags setup : we activate the IRQs by default as in user mode */
env->eflags = 0x2 | IF_MASK;
/* init various static tables */
if (!inited) {