Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
fd429f2f6c | ||
|
fb3e5849bb | ||
|
7854b05654 | ||
|
500dab07e8 | ||
|
f6630e791b | ||
|
168485b75b | ||
|
5cd4393b14 | ||
|
7ed601b782 | ||
|
d1f2367bc0 | ||
|
851e67a1b4 | ||
|
fc2b4c4879 | ||
|
9c605cb135 | ||
|
24f9e90b0e | ||
|
a4a0ffdb2b | ||
|
0ea00c9a3c | ||
|
e1d4294a45 | ||
|
c3c7c29246 | ||
|
31bb950be6 | ||
|
8083a3e508 | ||
|
644c433cb3 | ||
|
d691f66983 | ||
|
386405f786 |
@@ -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.
|
||||
|
22
Makefile
22
Makefile
@@ -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
20
README
@@ -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
|
||||
-------------
|
||||
|
7
configure
vendored
7
configure
vendored
@@ -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
|
||||
|
36
cpu-i386.h
36
cpu-i386.h
@@ -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,
|
||||
|
71
dyngen.c
71
dyngen.c
@@ -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);
|
||||
}
|
||||
|
29
exec-i386.c
29
exec-i386.c
@@ -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
|
||||
|
@@ -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 ("");
|
||||
|
@@ -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;
|
||||
|
@@ -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 = ®s1;
|
||||
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;
|
||||
|
@@ -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);
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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
124
op-i386.c
@@ -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
245
op_string.h
Normal 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
1170
opc-i386.h
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
274
ops_template.h
274
ops_template.h
@@ -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
382
qemu-doc.texi
Normal 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}).
|
222
syscall-i386.h
222
syscall-i386.h
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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();
|
||||
|
@@ -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)
|
||||
|
396
translate-i386.c
396
translate-i386.c
@@ -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, ®_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, ®_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, ®_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, ®_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, ®_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) {
|
||||
|
Reference in New Issue
Block a user