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:
|
version 0.1:
|
||||||
|
|
||||||
- initial public release.
|
- initial public release.
|
||||||
|
20
Makefile
20
Makefile
@@ -19,6 +19,10 @@ ifeq ($(ARCH),ppc)
|
|||||||
OP_CFLAGS=$(CFLAGS)
|
OP_CFLAGS=$(CFLAGS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(ARCH),s390)
|
||||||
|
OP_CFLAGS=$(CFLAGS)
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(GCC_MAJOR),3)
|
ifeq ($(GCC_MAJOR),3)
|
||||||
# very important to generate a return at the end of every operation
|
# very important to generate a return at the end of every operation
|
||||||
OP_CFLAGS+=-fno-reorder-blocks -fno-optimize-sibling-calls
|
OP_CFLAGS+=-fno-reorder-blocks -fno-optimize-sibling-calls
|
||||||
@@ -94,19 +98,20 @@ qemu-doc.html: qemu-doc.texi
|
|||||||
texi2html -monolithic -number $<
|
texi2html -monolithic -number $<
|
||||||
|
|
||||||
FILES= \
|
FILES= \
|
||||||
README COPYING COPYING.LIB TODO Changelog VERSION \
|
README README.distrib COPYING COPYING.LIB TODO Changelog VERSION \
|
||||||
dyngen.c ioctls.h ops_template.h syscall_types.h\
|
dyngen.c ioctls.h ops_template.h op_string.h syscall_types.h\
|
||||||
Makefile elf.h linux_bin.h segment.h thunk.c\
|
Makefile elf.h linux_bin.h segment.h thunk.c\
|
||||||
elfload.c main.c signal.c thunk.h\
|
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-asm.h gen-i386.h op-i386.h syscall.c\
|
||||||
dis-buf.c i386-dis.c opreg_template.h syscall_defs.h\
|
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/Makefile\
|
||||||
tests/test-i386.c tests/test-i386-shift.h tests/test-i386.h\
|
tests/test-i386.c tests/test-i386-shift.h tests/test-i386.h\
|
||||||
tests/test-i386-muldiv.h tests/test-i386-code16.S\
|
tests/test-i386-muldiv.h tests/test-i386-code16.S\
|
||||||
tests/hello.c tests/hello tests/sha1.c \
|
tests/hello.c tests/hello tests/sha1.c \
|
||||||
tests/testsig.c tests/testclone.c tests/testthread.c \
|
tests/testsig.c tests/testclone.c tests/testthread.c \
|
||||||
|
tests/runcom.c tests/pi_10.com \
|
||||||
qemu-doc.texi qemu-doc.html
|
qemu-doc.texi qemu-doc.html
|
||||||
|
|
||||||
FILE=qemu-$(VERSION)
|
FILE=qemu-$(VERSION)
|
||||||
@@ -118,6 +123,15 @@ tar:
|
|||||||
( cd /tmp ; tar zcvf ~/$(FILE).tar.gz $(FILE) )
|
( cd /tmp ; tar zcvf ~/$(FILE).tar.gz $(FILE) )
|
||||||
rm -rf /tmp/$(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),)
|
ifneq ($(wildcard .depend),)
|
||||||
include .depend
|
include .depend
|
||||||
endif
|
endif
|
||||||
|
20
README
20
README
@@ -15,8 +15,26 @@ Type
|
|||||||
|
|
||||||
make install
|
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
|
Documentation
|
||||||
-------------
|
-------------
|
||||||
|
7
configure
vendored
7
configure
vendored
@@ -42,6 +42,9 @@ case "$cpu" in
|
|||||||
mips)
|
mips)
|
||||||
cpu="mips"
|
cpu="mips"
|
||||||
;;
|
;;
|
||||||
|
s390)
|
||||||
|
cpu="s390"
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
cpu="unknown"
|
cpu="unknown"
|
||||||
;;
|
;;
|
||||||
@@ -137,7 +140,7 @@ fi
|
|||||||
else
|
else
|
||||||
|
|
||||||
# if cross compiling, cannot launch a program, so make a static guess
|
# 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"
|
bigendian="yes"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -212,6 +215,8 @@ elif test "$cpu" = "powerpc" ; then
|
|||||||
echo "ARCH=ppc" >> config.mak
|
echo "ARCH=ppc" >> config.mak
|
||||||
elif test "$cpu" = "mips" ; then
|
elif test "$cpu" = "mips" ; then
|
||||||
echo "ARCH=mips" >> config.mak
|
echo "ARCH=mips" >> config.mak
|
||||||
|
elif test "$cpu" = "s390" ; then
|
||||||
|
echo "ARCH=s390" >> config.mak
|
||||||
else
|
else
|
||||||
echo "Unsupported CPU"
|
echo "Unsupported CPU"
|
||||||
exit 1
|
exit 1
|
||||||
|
36
cpu-i386.h
36
cpu-i386.h
@@ -48,6 +48,7 @@
|
|||||||
#define R_FS 4
|
#define R_FS 4
|
||||||
#define R_GS 5
|
#define R_GS 5
|
||||||
|
|
||||||
|
/* eflags masks */
|
||||||
#define CC_C 0x0001
|
#define CC_C 0x0001
|
||||||
#define CC_P 0x0004
|
#define CC_P 0x0004
|
||||||
#define CC_A 0x0010
|
#define CC_A 0x0010
|
||||||
@@ -55,15 +56,17 @@
|
|||||||
#define CC_S 0x0080
|
#define CC_S 0x0080
|
||||||
#define CC_O 0x0800
|
#define CC_O 0x0800
|
||||||
|
|
||||||
#define TRAP_FLAG 0x0100
|
#define TF_MASK 0x00000100
|
||||||
#define INTERRUPT_FLAG 0x0200
|
#define IF_MASK 0x00000200
|
||||||
#define DIRECTION_FLAG 0x0400
|
#define DF_MASK 0x00000400
|
||||||
#define IOPL_FLAG_MASK 0x3000
|
#define IOPL_MASK 0x00003000
|
||||||
#define NESTED_FLAG 0x4000
|
#define NT_MASK 0x00004000
|
||||||
#define BYTE_FL 0x8000 /* Intel reserved! */
|
#define RF_MASK 0x00010000
|
||||||
#define RF_FLAG 0x10000
|
#define VM_MASK 0x00020000
|
||||||
#define VM_FLAG 0x20000
|
#define AC_MASK 0x00040000
|
||||||
/* AC 0x40000 */
|
#define VIF_MASK 0x00080000
|
||||||
|
#define VIP_MASK 0x00100000
|
||||||
|
#define ID_MASK 0x00200000
|
||||||
|
|
||||||
#define EXCP00_DIVZ 1
|
#define EXCP00_DIVZ 1
|
||||||
#define EXCP01_SSTP 2
|
#define EXCP01_SSTP 2
|
||||||
@@ -158,7 +161,9 @@ typedef struct CPUX86State {
|
|||||||
/* standard registers */
|
/* standard registers */
|
||||||
uint32_t regs[8];
|
uint32_t regs[8];
|
||||||
uint32_t eip;
|
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 */
|
/* emulator internal eflags handling */
|
||||||
uint32_t cc_src;
|
uint32_t cc_src;
|
||||||
@@ -183,13 +188,13 @@ typedef struct CPUX86State {
|
|||||||
SegmentDescriptorTable ldt;
|
SegmentDescriptorTable ldt;
|
||||||
SegmentDescriptorTable idt;
|
SegmentDescriptorTable idt;
|
||||||
|
|
||||||
/* various CPU modes */
|
|
||||||
int vm86;
|
|
||||||
|
|
||||||
/* exception/interrupt handling */
|
/* exception/interrupt handling */
|
||||||
jmp_buf jmp_env;
|
jmp_buf jmp_env;
|
||||||
int exception_index;
|
int exception_index;
|
||||||
int interrupt_request;
|
int interrupt_request;
|
||||||
|
|
||||||
|
/* user data */
|
||||||
|
void *opaque;
|
||||||
} CPUX86State;
|
} CPUX86State;
|
||||||
|
|
||||||
/* all CPU memory access use these macros */
|
/* all CPU memory access use these macros */
|
||||||
@@ -406,7 +411,7 @@ void cpu_x86_close(CPUX86State *s);
|
|||||||
/* needed to load some predefinied segment registers */
|
/* needed to load some predefinied segment registers */
|
||||||
void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector);
|
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
|
signal handlers to inform the virtual CPU of exceptions. non zero
|
||||||
is returned if the signal was handled by the virtual CPU. */
|
is returned if the signal was handled by the virtual CPU. */
|
||||||
struct siginfo;
|
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_CODE32_SHIFT 0
|
||||||
#define GEN_FLAG_ADDSEG_SHIFT 1
|
#define GEN_FLAG_ADDSEG_SHIFT 1
|
||||||
#define GEN_FLAG_SS32_SHIFT 2
|
#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 cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size,
|
||||||
int *gen_code_size_ptr,
|
int *gen_code_size_ptr,
|
||||||
|
71
dyngen.c
71
dyngen.c
@@ -28,6 +28,15 @@
|
|||||||
|
|
||||||
#include "thunk.h"
|
#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 */
|
/* all dynamically generated functions begin with this code */
|
||||||
#define OP_PREFIX "op_"
|
#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;
|
copy_size = p - p_start;
|
||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
error("unsupported CPU (%d)", e_machine);
|
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);
|
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 */
|
/* output C code */
|
||||||
fprintf(outfile, "case INDEX_%s: {\n", name);
|
fprintf(outfile, "case INDEX_%s: {\n", name);
|
||||||
@@ -403,6 +425,42 @@ void gen_code(const char *name, unsigned long offset, unsigned long size,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
error("unsupported CPU for relocations (%d)", e_machine);
|
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:
|
case EM_SPARC:
|
||||||
cpu_name = "sparc";
|
cpu_name = "sparc";
|
||||||
break;
|
break;
|
||||||
|
case EM_S390:
|
||||||
|
cpu_name = "s390";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
error("unsupported CPU (e_machine=%d)", e_machine);
|
error("unsupported CPU (e_machine=%d)", e_machine);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (do_print_enum) {
|
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++) {
|
for(i = 0, sym = symtab; i < nb_syms; i++, sym++) {
|
||||||
const char *name, *p;
|
const char *name, *p;
|
||||||
name = strtab + sym->st_name;
|
name = strtab + sym->st_name;
|
||||||
if (strstart(name, OP_PREFIX, &p)) {
|
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 {
|
} else {
|
||||||
@@ -614,6 +676,9 @@ fprintf(outfile,
|
|||||||
case EM_PPC:
|
case EM_PPC:
|
||||||
fprintf(outfile, "*((uint32_t *)gen_code_ptr)++ = 0x4e800020; /* blr */\n");
|
fprintf(outfile, "*((uint32_t *)gen_code_ptr)++ = 0x4e800020; /* blr */\n");
|
||||||
break;
|
break;
|
||||||
|
case EM_S390:
|
||||||
|
fprintf(outfile, "*((uint16_t *)gen_code_ptr)++ = 0x07fe; /* br %%r14 */\n");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
error("no return generation for cpu '%s'", cpu_name);
|
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
|
#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;
|
int global_cpu_lock = 0;
|
||||||
|
|
||||||
void cpu_lock(void)
|
void cpu_lock(void)
|
||||||
@@ -330,9 +344,10 @@ int cpu_x86_exec(CPUX86State *env1)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* put eflags in CPU temporary format */
|
/* put eflags in CPU temporary format */
|
||||||
T0 = env->eflags;
|
CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
|
||||||
op_movl_eflags_T0();
|
DF = 1 - (2 * ((env->eflags >> 10) & 1));
|
||||||
CC_OP = CC_OP_EFLAGS;
|
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;
|
env->interrupt_request = 0;
|
||||||
|
|
||||||
/* prepare setjmp context for exception handling */
|
/* 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_ES].base |
|
||||||
(unsigned long)env->seg_cache[R_SS].base) != 0) <<
|
(unsigned long)env->seg_cache[R_SS].base) != 0) <<
|
||||||
GEN_FLAG_ADDSEG_SHIFT;
|
GEN_FLAG_ADDSEG_SHIFT;
|
||||||
|
flags |= (env->eflags & VM_MASK) >> (17 - GEN_FLAG_VM_SHIFT);
|
||||||
cs_base = env->seg_cache[R_CS].base;
|
cs_base = env->seg_cache[R_CS].base;
|
||||||
pc = cs_base + env->eip;
|
pc = cs_base + env->eip;
|
||||||
tb = tb_find(&ptb, (unsigned long)pc, (unsigned long)cs_base,
|
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;
|
ret = env->exception_index;
|
||||||
|
|
||||||
/* restore flags in standard format */
|
/* restore flags in standard format */
|
||||||
op_movl_T0_eflags();
|
env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK);
|
||||||
env->eflags = T0;
|
|
||||||
|
|
||||||
/* restore global registers */
|
/* restore global registers */
|
||||||
#ifdef reg_EAX
|
#ifdef reg_EAX
|
||||||
@@ -485,7 +500,11 @@ int cpu_x86_signal_handler(int host_signum, struct siginfo *info,
|
|||||||
unsigned long pc;
|
unsigned long pc;
|
||||||
sigset_t *pold_set;
|
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;
|
pold_set = &uc->uc_sigmask;
|
||||||
return handle_cpu_signal(pc, pold_set);
|
return handle_cpu_signal(pc, pold_set);
|
||||||
#else
|
#else
|
||||||
|
@@ -93,6 +93,12 @@ register unsigned int T1 asm("l1");
|
|||||||
register unsigned int A0 asm("l2");
|
register unsigned int A0 asm("l2");
|
||||||
register struct CPUX86State *env asm("l3");
|
register struct CPUX86State *env asm("l3");
|
||||||
#endif
|
#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 */
|
/* force GCC to generate only one epilog at the end of the function */
|
||||||
#define FORCE_RET() asm volatile ("");
|
#define FORCE_RET() asm volatile ("");
|
||||||
|
@@ -42,8 +42,7 @@
|
|||||||
#define DLINFO_ITEMS 12
|
#define DLINFO_ITEMS 12
|
||||||
|
|
||||||
/* Where we find X86 libraries... */
|
/* Where we find X86 libraries... */
|
||||||
//#define X86_DEFAULT_LIB_DIR "/usr/x86/"
|
|
||||||
#define X86_DEFAULT_LIB_DIR "/"
|
|
||||||
|
|
||||||
//extern void * mmap4k();
|
//extern void * mmap4k();
|
||||||
#define mmap4k(a, b, c, d, e, f) mmap((void *)(a), b, c, d, e, f)
|
#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++)
|
put_user (tswapl(val), dlinfo++)
|
||||||
|
|
||||||
if (exec) { /* Put this here for an ELF program interpreter */
|
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_PHDR, (unsigned int)(load_addr + exec->e_phoff));
|
||||||
NEW_AUX_ENT (AT_PHENT, (unsigned int)(sizeof (struct elf_phdr)));
|
NEW_AUX_ENT (AT_PHENT, (unsigned int)(sizeof (struct elf_phdr)));
|
||||||
NEW_AUX_ENT (AT_PHNUM, (unsigned int)(exec->e_phnum));
|
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;
|
load_addr = INTERP_LOADADDR;
|
||||||
|
|
||||||
|
#ifdef BSWAP_NEEDED
|
||||||
|
bswap_ehdr(interp_elf_ex);
|
||||||
|
#endif
|
||||||
/* First of all, some simple consistency checks */
|
/* First of all, some simple consistency checks */
|
||||||
if ((interp_elf_ex->e_type != ET_EXEC &&
|
if ((interp_elf_ex->e_type != ET_EXEC &&
|
||||||
interp_elf_ex->e_type != ET_DYN) ||
|
interp_elf_ex->e_type != ET_DYN) ||
|
||||||
@@ -426,6 +425,7 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
|
|||||||
return ~0UL;
|
return ~0UL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Now read in all of the header information */
|
/* Now read in all of the header information */
|
||||||
|
|
||||||
if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > X86_PAGE_SIZE)
|
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,
|
(char *) elf_phdata,
|
||||||
sizeof(struct elf_phdr) * interp_elf_ex->e_phnum);
|
sizeof(struct elf_phdr) * interp_elf_ex->e_phnum);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (retval < 0) {
|
if (retval < 0) {
|
||||||
perror("load_elf_interp");
|
perror("load_elf_interp");
|
||||||
exit(-1);
|
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
|
* 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) {
|
if (elf_interpreter == NULL) {
|
||||||
free (elf_phdata);
|
free (elf_phdata);
|
||||||
@@ -646,11 +646,11 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r
|
|||||||
return -ENOMEM;
|
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);
|
retval = lseek(bprm->fd, elf_ppnt->p_offset, SEEK_SET);
|
||||||
if(retval >= 0) {
|
if(retval >= 0) {
|
||||||
retval = read(bprm->fd,
|
retval = read(bprm->fd,
|
||||||
elf_interpreter+strlen(X86_DEFAULT_LIB_DIR),
|
elf_interpreter+strlen(bprm->interp_prefix),
|
||||||
elf_ppnt->p_filesz);
|
elf_ppnt->p_filesz);
|
||||||
}
|
}
|
||||||
if(retval < 0) {
|
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 target_pt_regs * regs, struct image_info *infop)
|
||||||
{
|
{
|
||||||
struct linux_binprm bprm;
|
struct linux_binprm bprm;
|
||||||
@@ -930,6 +931,7 @@ int elf_exec(const char * filename, char ** argv, char ** envp,
|
|||||||
else {
|
else {
|
||||||
bprm.fd = retval;
|
bprm.fd = retval;
|
||||||
}
|
}
|
||||||
|
bprm.interp_prefix = (char *)interp_prefix;
|
||||||
bprm.filename = (char *)filename;
|
bprm.filename = (char *)filename;
|
||||||
bprm.sh_bang = 0;
|
bprm.sh_bang = 0;
|
||||||
bprm.loader = 0;
|
bprm.loader = 0;
|
||||||
|
@@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
FILE *logfile = NULL;
|
FILE *logfile = NULL;
|
||||||
int loglevel;
|
int loglevel;
|
||||||
|
const char *interp_prefix = CONFIG_QEMU_PREFIX "/qemu-i386";
|
||||||
|
|
||||||
/* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
|
/* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
|
||||||
we allocate a bigger stack. Need a better solution, for example
|
we allocate a bigger stack. Need a better solution, for example
|
||||||
@@ -103,6 +104,8 @@ void write_dt(void *ptr, unsigned long addr, unsigned long limit,
|
|||||||
|
|
||||||
uint64_t gdt_table[6];
|
uint64_t gdt_table[6];
|
||||||
|
|
||||||
|
//#define DEBUG_VM86
|
||||||
|
|
||||||
void cpu_loop(struct CPUX86State *env)
|
void cpu_loop(struct CPUX86State *env)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
@@ -114,6 +117,67 @@ void cpu_loop(struct CPUX86State *env)
|
|||||||
pc = env->seg_cache[R_CS].base + env->eip;
|
pc = env->seg_cache[R_CS].base + env->eip;
|
||||||
switch(err) {
|
switch(err) {
|
||||||
case EXCP0D_GPF:
|
case EXCP0D_GPF:
|
||||||
|
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 {
|
||||||
if (pc[0] == 0xcd && pc[1] == 0x80) {
|
if (pc[0] == 0xcd && pc[1] == 0x80) {
|
||||||
/* syscall */
|
/* syscall */
|
||||||
env->eip += 2;
|
env->eip += 2;
|
||||||
@@ -133,6 +197,7 @@ void cpu_loop(struct CPUX86State *env)
|
|||||||
info._sifields._sigfault._addr = 0;
|
info._sifields._sigfault._addr = 0;
|
||||||
queue_signal(info.si_signo, &info);
|
queue_signal(info.si_signo, &info);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case EXCP00_DIVZ:
|
case EXCP00_DIVZ:
|
||||||
/* division by zero */
|
/* division by zero */
|
||||||
@@ -172,31 +237,67 @@ void cpu_loop(struct CPUX86State *env)
|
|||||||
void usage(void)
|
void usage(void)
|
||||||
{
|
{
|
||||||
printf("qemu version " QEMU_VERSION ", Copyright (c) 2003 Fabrice Bellard\n"
|
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"
|
"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);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX: currently only used for async signals (see signal.c) */
|
/* XXX: currently only used for async signals (see signal.c) */
|
||||||
CPUX86State *global_env;
|
CPUX86State *global_env;
|
||||||
|
/* used to free thread contexts */
|
||||||
|
TaskState *first_task_state;
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
const char *filename;
|
const char *filename;
|
||||||
struct target_pt_regs regs1, *regs = ®s1;
|
struct target_pt_regs regs1, *regs = ®s1;
|
||||||
struct image_info info1, *info = &info1;
|
struct image_info info1, *info = &info1;
|
||||||
|
TaskState ts1, *ts = &ts1;
|
||||||
CPUX86State *env;
|
CPUX86State *env;
|
||||||
int optind;
|
int optind;
|
||||||
|
const char *r;
|
||||||
|
|
||||||
if (argc <= 1)
|
if (argc <= 1)
|
||||||
usage();
|
usage();
|
||||||
loglevel = 0;
|
loglevel = 0;
|
||||||
optind = 1;
|
optind = 1;
|
||||||
if (argv[optind] && !strcmp(argv[optind], "-d")) {
|
for(;;) {
|
||||||
loglevel = 1;
|
if (optind >= argc)
|
||||||
|
break;
|
||||||
|
r = argv[optind];
|
||||||
|
if (r[0] != '-')
|
||||||
|
break;
|
||||||
optind++;
|
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];
|
filename = argv[optind];
|
||||||
|
|
||||||
/* init debug */
|
/* init debug */
|
||||||
@@ -215,7 +316,7 @@ int main(int argc, char **argv)
|
|||||||
/* Zero out image_info */
|
/* Zero out image_info */
|
||||||
memset(info, 0, sizeof(struct 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);
|
printf("Error loading %s\n", filename);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@@ -238,6 +339,11 @@ int main(int argc, char **argv)
|
|||||||
env = cpu_x86_init();
|
env = cpu_x86_init();
|
||||||
global_env = env;
|
global_env = env;
|
||||||
|
|
||||||
|
/* build Task State */
|
||||||
|
memset(ts, 0, sizeof(TaskState));
|
||||||
|
env->opaque = ts;
|
||||||
|
ts->used = 1;
|
||||||
|
|
||||||
/* linux register setup */
|
/* linux register setup */
|
||||||
env->regs[R_EAX] = regs->eax;
|
env->regs[R_EAX] = regs->eax;
|
||||||
env->regs[R_EBX] = regs->ebx;
|
env->regs[R_EBX] = regs->ebx;
|
||||||
|
@@ -33,7 +33,35 @@ struct image_info {
|
|||||||
int personality;
|
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);
|
struct target_pt_regs * regs, struct image_info *infop);
|
||||||
|
|
||||||
void target_set_brk(char *new_brk);
|
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);
|
return (void *)((esp - frame_size) & -8ul);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TF_MASK TRAP_FLAG
|
|
||||||
|
|
||||||
static void setup_frame(int sig, struct emulated_sigaction *ka,
|
static void setup_frame(int sig, struct emulated_sigaction *ka,
|
||||||
target_sigset_t *set, CPUX86State *env)
|
target_sigset_t *set, CPUX86State *env)
|
||||||
{
|
{
|
||||||
|
@@ -26,6 +26,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <time.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
@@ -40,6 +41,7 @@
|
|||||||
#include <sys/uio.h>
|
#include <sys/uio.h>
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
//#include <sys/user.h>
|
//#include <sys/user.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
|
||||||
#define termios host_termios
|
#define termios host_termios
|
||||||
#define winsize host_winsize
|
#define winsize host_winsize
|
||||||
@@ -103,10 +105,10 @@ extern int personality(int);
|
|||||||
extern int flock(int, int);
|
extern int flock(int, int);
|
||||||
extern int setfsuid(int);
|
extern int setfsuid(int);
|
||||||
extern int setfsgid(int);
|
extern int setfsgid(int);
|
||||||
extern int setresuid(int,int,int);
|
extern int setresuid(uid_t, uid_t, uid_t);
|
||||||
extern int getresuid(int *,int *,int *);
|
extern int getresuid(uid_t *, uid_t *, uid_t *);
|
||||||
extern int setresgid(int,int,int);
|
extern int setresgid(gid_t, gid_t, gid_t);
|
||||||
extern int getresgid(int *,int *,int *);
|
extern int getresgid(gid_t *, gid_t *, gid_t *);
|
||||||
|
|
||||||
static inline long get_errno(long ret)
|
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,
|
static inline fd_set *target_to_host_fds(fd_set *fds,
|
||||||
target_long *target_fds, int n)
|
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;
|
return (fd_set *)target_fds;
|
||||||
#else
|
#else
|
||||||
int i, b;
|
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,
|
static inline void host_to_target_fds(target_long *target_fds,
|
||||||
fd_set *fds, int n)
|
fd_set *fds, int n)
|
||||||
{
|
{
|
||||||
#if !defined(BSWP_NEEDED) && !defined(WORD_BIGENDIAN)
|
#if !defined(BSWAP_NEEDED) && !defined(WORDS_BIGENDIAN)
|
||||||
/* nothing to do */
|
/* nothing to do */
|
||||||
#else
|
#else
|
||||||
int i, nw, j, k;
|
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,
|
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_sec = tswapl(target_tv->tv_sec);
|
||||||
tv->tv_usec = tswapl(target_tv->tv_usec);
|
tv->tv_usec = tswapl(target_tv->tv_usec);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void host_to_target_timeval(struct target_timeval *target_tv,
|
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_sec = tswapl(tv->tv_sec);
|
||||||
target_tv->tv_usec = tswapl(tv->tv_usec);
|
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);
|
efds_ptr = target_to_host_fds(&efds, target_efds, n);
|
||||||
|
|
||||||
if (target_tv) {
|
if (target_tv) {
|
||||||
tv.tv_sec = tswapl(target_tv->tv_sec);
|
target_to_host_timeval(&tv, target_tv);
|
||||||
tv.tv_usec = tswapl(target_tv->tv_usec);
|
|
||||||
tv_ptr = &tv;
|
tv_ptr = &tv;
|
||||||
} else {
|
} else {
|
||||||
tv_ptr = NULL;
|
tv_ptr = NULL;
|
||||||
@@ -251,62 +252,273 @@ static long do_select(long n,
|
|||||||
host_to_target_fds(target_efds, efds_ptr, n);
|
host_to_target_fds(target_efds, efds_ptr, n);
|
||||||
|
|
||||||
if (target_tv) {
|
if (target_tv) {
|
||||||
target_tv->tv_sec = tswapl(tv.tv_sec);
|
host_to_target_timeval(target_tv, &tv);
|
||||||
target_tv->tv_usec = tswapl(tv.tv_usec);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
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;
|
long ret;
|
||||||
|
|
||||||
switch(num) {
|
switch(num) {
|
||||||
case SOCKOP_socket:
|
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;
|
break;
|
||||||
case SOCKOP_bind:
|
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;
|
break;
|
||||||
case SOCKOP_connect:
|
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;
|
break;
|
||||||
case SOCKOP_listen:
|
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;
|
break;
|
||||||
case SOCKOP_accept:
|
case SOCKOP_accept:
|
||||||
{
|
{
|
||||||
socklen_t size;
|
int sockfd = tswap32(vptr[0]);
|
||||||
size = tswap32(*(int32_t *)vptr[2]);
|
void *target_addr = (void *)tswap32(vptr[1]);
|
||||||
ret = get_errno(accept(vptr[0], (struct sockaddr *)vptr[1], &size));
|
uint32_t *target_addrlen = (void *)tswap32(vptr[2]);
|
||||||
if (!is_error(ret))
|
socklen_t addrlen = tswap32(*target_addrlen);
|
||||||
*(int32_t *)vptr[2] = size;
|
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;
|
break;
|
||||||
case SOCKOP_getsockname:
|
case SOCKOP_getsockname:
|
||||||
{
|
{
|
||||||
socklen_t size;
|
int sockfd = tswap32(vptr[0]);
|
||||||
size = tswap32(*(int32_t *)vptr[2]);
|
void *target_addr = (void *)tswap32(vptr[1]);
|
||||||
ret = get_errno(getsockname(vptr[0], (struct sockaddr *)vptr[1], &size));
|
uint32_t *target_addrlen = (void *)tswap32(vptr[2]);
|
||||||
if (!is_error(ret))
|
socklen_t addrlen = tswap32(*target_addrlen);
|
||||||
*(int32_t *)vptr[2] = size;
|
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;
|
break;
|
||||||
case SOCKOP_getpeername:
|
case SOCKOP_getpeername:
|
||||||
{
|
{
|
||||||
socklen_t size;
|
int sockfd = tswap32(vptr[0]);
|
||||||
size = tswap32(*(int32_t *)vptr[2]);
|
void *target_addr = (void *)tswap32(vptr[1]);
|
||||||
ret = get_errno(getpeername(vptr[0], (struct sockaddr *)vptr[1], &size));
|
uint32_t *target_addrlen = (void *)tswap32(vptr[2]);
|
||||||
if (!is_error(ret))
|
socklen_t addrlen = tswap32(*target_addrlen);
|
||||||
*(int32_t *)vptr[2] = size;
|
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;
|
break;
|
||||||
case SOCKOP_socketpair:
|
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];
|
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)) {
|
if (!is_error(ret)) {
|
||||||
target_tab[0] = tswap32(tab[0]);
|
target_tab[0] = tswap32(tab[0]);
|
||||||
target_tab[1] = tswap32(tab[1]);
|
target_tab[1] = tswap32(tab[1]);
|
||||||
@@ -314,27 +526,64 @@ static long do_socketcall(int num, long *vptr)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SOCKOP_send:
|
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;
|
break;
|
||||||
case SOCKOP_recv:
|
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;
|
break;
|
||||||
case SOCKOP_sendto:
|
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;
|
break;
|
||||||
case SOCKOP_recvfrom:
|
case SOCKOP_recvfrom:
|
||||||
{
|
{
|
||||||
socklen_t size;
|
int sockfd = tswap32(vptr[0]);
|
||||||
size = tswap32(*(int32_t *)vptr[5]);
|
void *msg = (void *)tswap32(vptr[1]);
|
||||||
ret = get_errno(recvfrom(vptr[0], (void *)vptr[1], vptr[2],
|
size_t len = tswap32(vptr[2]);
|
||||||
vptr[3], (struct sockaddr *)vptr[4], &size));
|
int flags = tswap32(vptr[3]);
|
||||||
if (!is_error(ret))
|
void *target_addr = (void *)tswap32(vptr[4]);
|
||||||
*(int32_t *)vptr[5] = size;
|
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;
|
break;
|
||||||
case SOCKOP_shutdown:
|
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;
|
break;
|
||||||
case SOCKOP_sendmsg:
|
case SOCKOP_sendmsg:
|
||||||
case SOCKOP_recvmsg:
|
case SOCKOP_recvmsg:
|
||||||
@@ -346,11 +595,11 @@ static long do_socketcall(int num, long *vptr)
|
|||||||
struct iovec *vec;
|
struct iovec *vec;
|
||||||
struct target_iovec *target_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_name = (void *)tswapl(msgp->msg_name);
|
||||||
msg.msg_namelen = tswapl(msgp->msg_namelen);
|
msg.msg_namelen = tswapl(msgp->msg_namelen);
|
||||||
msg.msg_control = (void *)tswapl(msgp->msg_control);
|
msg.msg_controllen = 2 * tswapl(msgp->msg_controllen);
|
||||||
msg.msg_controllen = tswapl(msgp->msg_controllen);
|
msg.msg_control = alloca(msg.msg_controllen);
|
||||||
msg.msg_flags = tswap32(msgp->msg_flags);
|
msg.msg_flags = tswap32(msgp->msg_flags);
|
||||||
|
|
||||||
count = tswapl(msgp->msg_iovlen);
|
count = tswapl(msgp->msg_iovlen);
|
||||||
@@ -363,17 +612,43 @@ static long do_socketcall(int num, long *vptr)
|
|||||||
msg.msg_iovlen = count;
|
msg.msg_iovlen = count;
|
||||||
msg.msg_iov = vec;
|
msg.msg_iov = vec;
|
||||||
|
|
||||||
fd = vptr[0];
|
fd = tswap32(vptr[0]);
|
||||||
flags = vptr[2];
|
flags = tswap32(vptr[2]);
|
||||||
if (num == SOCKOP_sendmsg)
|
if (num == SOCKOP_sendmsg) {
|
||||||
ret = sendmsg(fd, &msg, flags);
|
target_to_host_cmsg(&msg, msgp);
|
||||||
else
|
ret = get_errno(sendmsg(fd, &msg, flags));
|
||||||
ret = recvmsg(fd, &msg, flags);
|
} else {
|
||||||
ret = get_errno(ret);
|
ret = get_errno(recvmsg(fd, &msg, flags));
|
||||||
|
if (!is_error(ret))
|
||||||
|
host_to_target_cmsg(msgp, &msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SOCKOP_setsockopt:
|
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:
|
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:
|
default:
|
||||||
gemu_log("Unsupported socketcall: %d\n", num);
|
gemu_log("Unsupported socketcall: %d\n", num);
|
||||||
ret = -ENOSYS;
|
ret = -ENOSYS;
|
||||||
@@ -755,7 +1030,7 @@ install:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* specific and weird i386 syscalls */
|
/* 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;
|
int ret = -ENOSYS;
|
||||||
|
|
||||||
@@ -773,6 +1048,79 @@ int gemu_modify_ldt(CPUX86State *env, int func, void *ptr, unsigned long bytecou
|
|||||||
return ret;
|
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
|
/* this stack is the equivalent of the kernel stack associated with a
|
||||||
thread/process */
|
thread/process */
|
||||||
#define NEW_STACK_SIZE 8192
|
#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 do_fork(CPUX86State *env, unsigned int flags, unsigned long newsp)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
TaskState *ts;
|
||||||
uint8_t *new_stack;
|
uint8_t *new_stack;
|
||||||
CPUX86State *new_env;
|
CPUX86State *new_env;
|
||||||
|
|
||||||
if (flags & CLONE_VM) {
|
if (flags & CLONE_VM) {
|
||||||
if (!newsp)
|
if (!newsp)
|
||||||
newsp = env->regs[R_ESP];
|
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. */
|
/* we create a new CPU instance. */
|
||||||
new_env = cpu_x86_init();
|
new_env = cpu_x86_init();
|
||||||
memcpy(new_env, env, sizeof(CPUX86State));
|
memcpy(new_env, env, sizeof(CPUX86State));
|
||||||
new_env->regs[R_ESP] = newsp;
|
new_env->regs[R_ESP] = newsp;
|
||||||
new_env->regs[R_EAX] = 0;
|
new_env->regs[R_EAX] = 0;
|
||||||
|
new_env->opaque = ts;
|
||||||
ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env);
|
ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env);
|
||||||
} else {
|
} else {
|
||||||
/* if no CLONE_VM, we consider it is a fork */
|
/* 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));
|
ret = get_errno(unlink((const char *)arg1));
|
||||||
break;
|
break;
|
||||||
case TARGET_NR_execve:
|
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;
|
break;
|
||||||
case TARGET_NR_chdir:
|
case TARGET_NR_chdir:
|
||||||
ret = get_errno(chdir((const char *)arg1));
|
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;
|
struct timeval tv;
|
||||||
ret = get_errno(gettimeofday(&tv, NULL));
|
ret = get_errno(gettimeofday(&tv, NULL));
|
||||||
if (!is_error(ret)) {
|
if (!is_error(ret)) {
|
||||||
target_tv->tv_sec = tswapl(tv.tv_sec);
|
host_to_target_timeval(target_tv, &tv);
|
||||||
target_tv->tv_usec = tswapl(tv.tv_usec);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
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 target_timeval *target_tv = (void *)arg1;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
tv.tv_sec = tswapl(target_tv->tv_sec);
|
target_to_host_timeval(&tv, target_tv);
|
||||||
tv.tv_usec = tswapl(target_tv->tv_usec);
|
|
||||||
ret = get_errno(settimeofday(&tv, NULL));
|
ret = get_errno(settimeofday(&tv, NULL));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1408,7 +1781,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
|
|||||||
case TARGET_NR_ioperm:
|
case TARGET_NR_ioperm:
|
||||||
goto unimplemented;
|
goto unimplemented;
|
||||||
case TARGET_NR_socketcall:
|
case TARGET_NR_socketcall:
|
||||||
ret = do_socketcall(arg1, (long *)arg2);
|
ret = do_socketcall(arg1, (int32_t *)arg2);
|
||||||
break;
|
break;
|
||||||
case TARGET_NR_syslog:
|
case TARGET_NR_syslog:
|
||||||
goto unimplemented;
|
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_size = tswapl(st.st_size);
|
||||||
target_st->st_blksize = tswapl(st.st_blksize);
|
target_st->st_blksize = tswapl(st.st_blksize);
|
||||||
target_st->st_blocks = tswapl(st.st_blocks);
|
target_st->st_blocks = tswapl(st.st_blocks);
|
||||||
target_st->st_atime = tswapl(st.st_atime);
|
target_st->target_st_atime = tswapl(st.st_atime);
|
||||||
target_st->st_mtime = tswapl(st.st_mtime);
|
target_st->target_st_mtime = tswapl(st.st_mtime);
|
||||||
target_st->st_ctime = tswapl(st.st_ctime);
|
target_st->target_st_ctime = tswapl(st.st_ctime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1487,8 +1860,6 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
|
|||||||
break;
|
break;
|
||||||
case TARGET_NR_idle:
|
case TARGET_NR_idle:
|
||||||
goto unimplemented;
|
goto unimplemented;
|
||||||
case TARGET_NR_vm86old:
|
|
||||||
goto unimplemented;
|
|
||||||
case TARGET_NR_wait4:
|
case TARGET_NR_wait4:
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
@@ -1548,7 +1919,12 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
|
|||||||
break;
|
break;
|
||||||
#ifdef TARGET_I386
|
#ifdef TARGET_I386
|
||||||
case TARGET_NR_modify_ldt:
|
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;
|
break;
|
||||||
#endif
|
#endif
|
||||||
case TARGET_NR_adjtimex:
|
case TARGET_NR_adjtimex:
|
||||||
@@ -1603,10 +1979,10 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
|
|||||||
int reclen;
|
int reclen;
|
||||||
de = dirp;
|
de = dirp;
|
||||||
while (len > 0) {
|
while (len > 0) {
|
||||||
reclen = tswap16(de->d_reclen);
|
reclen = de->d_reclen;
|
||||||
if (reclen > len)
|
if (reclen > len)
|
||||||
break;
|
break;
|
||||||
de->d_reclen = reclen;
|
de->d_reclen = tswap16(reclen);
|
||||||
tswapls(&de->d_ino);
|
tswapls(&de->d_ino);
|
||||||
tswapls(&de->d_off);
|
tswapls(&de->d_off);
|
||||||
de = (struct dirent *)((char *)de + reclen);
|
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;
|
int reclen;
|
||||||
de = dirp;
|
de = dirp;
|
||||||
while (len > 0) {
|
while (len > 0) {
|
||||||
reclen = tswap16(de->d_reclen);
|
reclen = de->d_reclen;
|
||||||
if (reclen > len)
|
if (reclen > len)
|
||||||
break;
|
break;
|
||||||
de->d_reclen = reclen;
|
de->d_reclen = tswap16(reclen);
|
||||||
tswap64s(&de->d_ino);
|
tswap64s(&de->d_ino);
|
||||||
tswap64s(&de->d_off);
|
tswap64s(&de->d_off);
|
||||||
de = (struct dirent64 *)((char *)de + reclen);
|
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;
|
unsigned int nfds = arg2;
|
||||||
int timeout = arg3;
|
int timeout = arg3;
|
||||||
struct pollfd *pfd;
|
struct pollfd *pfd;
|
||||||
int i;
|
unsigned int i;
|
||||||
|
|
||||||
pfd = alloca(sizeof(struct pollfd) * nfds);
|
pfd = alloca(sizeof(struct pollfd) * nfds);
|
||||||
for(i = 0; i < nfds; i++) {
|
for(i = 0; i < nfds; i++) {
|
||||||
pfd->fd = tswap32(target_pfd->fd);
|
pfd[i].fd = tswap32(target_pfd[i].fd);
|
||||||
pfd->events = tswap16(target_pfd->events);
|
pfd[i].events = tswap16(target_pfd[i].events);
|
||||||
}
|
}
|
||||||
ret = get_errno(poll(pfd, nfds, timeout));
|
ret = get_errno(poll(pfd, nfds, timeout));
|
||||||
if (!is_error(ret)) {
|
if (!is_error(ret)) {
|
||||||
for(i = 0; i < nfds; i++) {
|
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));
|
ret = get_errno(getsid(arg1));
|
||||||
break;
|
break;
|
||||||
case TARGET_NR_fdatasync:
|
case TARGET_NR_fdatasync:
|
||||||
goto unimplemented;
|
ret = get_errno(fdatasync(arg1));
|
||||||
|
break;
|
||||||
case TARGET_NR__sysctl:
|
case TARGET_NR__sysctl:
|
||||||
goto unimplemented;
|
goto unimplemented;
|
||||||
case TARGET_NR_sched_setparam:
|
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:
|
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:
|
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:
|
case TARGET_NR_sched_getscheduler:
|
||||||
goto unimplemented;
|
ret = get_errno(sched_getscheduler(arg1));
|
||||||
|
break;
|
||||||
case TARGET_NR_sched_yield:
|
case TARGET_NR_sched_yield:
|
||||||
ret = get_errno(sched_yield());
|
ret = get_errno(sched_yield());
|
||||||
break;
|
break;
|
||||||
case TARGET_NR_sched_get_priority_max:
|
case TARGET_NR_sched_get_priority_max:
|
||||||
|
ret = get_errno(sched_get_priority_max(arg1));
|
||||||
|
break;
|
||||||
case TARGET_NR_sched_get_priority_min:
|
case TARGET_NR_sched_get_priority_min:
|
||||||
|
ret = get_errno(sched_get_priority_min(arg1));
|
||||||
|
break;
|
||||||
case TARGET_NR_sched_rr_get_interval:
|
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:
|
case TARGET_NR_nanosleep:
|
||||||
{
|
{
|
||||||
struct target_timespec *target_req = (void *)arg1;
|
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;
|
break;
|
||||||
case TARGET_NR_vm86:
|
|
||||||
case TARGET_NR_query_module:
|
case TARGET_NR_query_module:
|
||||||
|
goto unimplemented;
|
||||||
case TARGET_NR_nfsservctl:
|
case TARGET_NR_nfsservctl:
|
||||||
|
goto unimplemented;
|
||||||
case TARGET_NR_prctl:
|
case TARGET_NR_prctl:
|
||||||
|
goto unimplemented;
|
||||||
case TARGET_NR_pread:
|
case TARGET_NR_pread:
|
||||||
|
goto unimplemented;
|
||||||
case TARGET_NR_pwrite:
|
case TARGET_NR_pwrite:
|
||||||
goto unimplemented;
|
goto unimplemented;
|
||||||
case TARGET_NR_chown:
|
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));
|
ret = get_errno(sys_getcwd1((char *)arg1, arg2));
|
||||||
break;
|
break;
|
||||||
case TARGET_NR_capget:
|
case TARGET_NR_capget:
|
||||||
|
goto unimplemented;
|
||||||
case TARGET_NR_capset:
|
case TARGET_NR_capset:
|
||||||
|
goto unimplemented;
|
||||||
case TARGET_NR_sigaltstack:
|
case TARGET_NR_sigaltstack:
|
||||||
|
goto unimplemented;
|
||||||
case TARGET_NR_sendfile:
|
case TARGET_NR_sendfile:
|
||||||
|
goto unimplemented;
|
||||||
case TARGET_NR_getpmsg:
|
case TARGET_NR_getpmsg:
|
||||||
|
goto unimplemented;
|
||||||
case TARGET_NR_putpmsg:
|
case TARGET_NR_putpmsg:
|
||||||
|
goto unimplemented;
|
||||||
case TARGET_NR_vfork:
|
case TARGET_NR_vfork:
|
||||||
ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD, 0));
|
ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD, 0));
|
||||||
break;
|
break;
|
||||||
case TARGET_NR_ugetrlimit:
|
case TARGET_NR_ugetrlimit:
|
||||||
|
goto unimplemented;
|
||||||
case TARGET_NR_truncate64:
|
case TARGET_NR_truncate64:
|
||||||
|
goto unimplemented;
|
||||||
case TARGET_NR_ftruncate64:
|
case TARGET_NR_ftruncate64:
|
||||||
goto unimplemented;
|
goto unimplemented;
|
||||||
case TARGET_NR_stat64:
|
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_size = tswapl(st.st_size);
|
||||||
target_st->st_blksize = tswapl(st.st_blksize);
|
target_st->st_blksize = tswapl(st.st_blksize);
|
||||||
target_st->st_blocks = tswapl(st.st_blocks);
|
target_st->st_blocks = tswapl(st.st_blocks);
|
||||||
target_st->st_atime = tswapl(st.st_atime);
|
target_st->target_st_atime = tswapl(st.st_atime);
|
||||||
target_st->st_mtime = tswapl(st.st_mtime);
|
target_st->target_st_mtime = tswapl(st.st_mtime);
|
||||||
target_st->st_ctime = tswapl(st.st_ctime);
|
target_st->target_st_ctime = tswapl(st.st_ctime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1919,6 +2340,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
|
|||||||
ret = get_errno(gettid());
|
ret = get_errno(gettid());
|
||||||
break;
|
break;
|
||||||
case TARGET_NR_readahead:
|
case TARGET_NR_readahead:
|
||||||
|
goto unimplemented;
|
||||||
case TARGET_NR_setxattr:
|
case TARGET_NR_setxattr:
|
||||||
case TARGET_NR_lsetxattr:
|
case TARGET_NR_lsetxattr:
|
||||||
case TARGET_NR_fsetxattr:
|
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_removexattr:
|
||||||
case TARGET_NR_lremovexattr:
|
case TARGET_NR_lremovexattr:
|
||||||
case TARGET_NR_fremovexattr:
|
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:
|
default:
|
||||||
unimplemented:
|
unimplemented:
|
||||||
gemu_log("gemu: Unsupported syscall: %d\n", num);
|
gemu_log("qemu: Unsupported syscall: %d\n", num);
|
||||||
|
unimplemented_nowarn:
|
||||||
ret = -ENOSYS;
|
ret = -ENOSYS;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -19,6 +19,11 @@
|
|||||||
#define SOCKOP_sendmsg 16
|
#define SOCKOP_sendmsg 16
|
||||||
#define SOCKOP_recvmsg 17
|
#define SOCKOP_recvmsg 17
|
||||||
|
|
||||||
|
struct target_sockaddr {
|
||||||
|
uint16_t sa_family;
|
||||||
|
uint8_t sa_data[14];
|
||||||
|
};
|
||||||
|
|
||||||
struct target_timeval {
|
struct target_timeval {
|
||||||
target_long tv_sec;
|
target_long tv_sec;
|
||||||
target_long tv_usec;
|
target_long tv_usec;
|
||||||
@@ -49,6 +54,43 @@ struct target_msghdr {
|
|||||||
unsigned int msg_flags;
|
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_rusage {
|
||||||
struct target_timeval ru_utime; /* user time used */
|
struct target_timeval ru_utime; /* user time used */
|
||||||
struct target_timeval ru_stime; /* system 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;
|
A0 += PARAM1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OPPROTO op_addl_A0_AL(void)
|
||||||
|
{
|
||||||
|
A0 += (EAX & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
void OPPROTO op_andl_A0_ffff(void)
|
void OPPROTO op_andl_A0_ffff(void)
|
||||||
{
|
{
|
||||||
A0 = A0 & 0xffff;
|
A0 = A0 & 0xffff;
|
||||||
@@ -602,13 +607,51 @@ void OPPROTO op_into(void)
|
|||||||
int eflags;
|
int eflags;
|
||||||
eflags = cc_table[CC_OP].compute_all();
|
eflags = cc_table[CC_OP].compute_all();
|
||||||
if (eflags & CC_O) {
|
if (eflags & CC_O) {
|
||||||
EIP = PARAM1;
|
|
||||||
raise_exception(EXCP04_INTO);
|
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 */
|
/* string ops */
|
||||||
|
|
||||||
#define ldul ldl
|
#define ldul ldl
|
||||||
@@ -788,7 +831,8 @@ void op_addw_ESP_im(void)
|
|||||||
#ifndef __i386__
|
#ifndef __i386__
|
||||||
uint64_t emu_time;
|
uint64_t emu_time;
|
||||||
#endif
|
#endif
|
||||||
void op_rdtsc(void)
|
|
||||||
|
void OPPROTO op_rdtsc(void)
|
||||||
{
|
{
|
||||||
uint64_t val;
|
uint64_t val;
|
||||||
#ifdef __i386__
|
#ifdef __i386__
|
||||||
@@ -801,6 +845,51 @@ void op_rdtsc(void)
|
|||||||
EDX = val >> 32;
|
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 */
|
/* bcd */
|
||||||
|
|
||||||
/* XXX: exception */
|
/* XXX: exception */
|
||||||
@@ -933,6 +1022,7 @@ void OPPROTO op_das(void)
|
|||||||
|
|
||||||
/* segment handling */
|
/* segment handling */
|
||||||
|
|
||||||
|
/* XXX: use static VM86 information */
|
||||||
void load_seg(int seg_reg, int selector)
|
void load_seg(int seg_reg, int selector)
|
||||||
{
|
{
|
||||||
SegmentCache *sc;
|
SegmentCache *sc;
|
||||||
@@ -943,7 +1033,7 @@ void load_seg(int seg_reg, int selector)
|
|||||||
|
|
||||||
env->segs[seg_reg] = selector;
|
env->segs[seg_reg] = selector;
|
||||||
sc = &env->seg_cache[seg_reg];
|
sc = &env->seg_cache[seg_reg];
|
||||||
if (env->vm86) {
|
if (env->eflags & VM_MASK) {
|
||||||
sc->base = (void *)(selector << 4);
|
sc->base = (void *)(selector << 4);
|
||||||
sc->limit = 0xffff;
|
sc->limit = 0xffff;
|
||||||
sc->seg_32bit = 0;
|
sc->seg_32bit = 0;
|
||||||
@@ -980,6 +1070,11 @@ void OPPROTO op_movl_T0_seg(void)
|
|||||||
T0 = env->segs[PARAM1];
|
T0 = env->segs[PARAM1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OPPROTO op_movl_A0_seg(void)
|
||||||
|
{
|
||||||
|
A0 = *(unsigned long *)((char *)env + PARAM1);
|
||||||
|
}
|
||||||
|
|
||||||
void OPPROTO op_addl_A0_seg(void)
|
void OPPROTO op_addl_A0_seg(void)
|
||||||
{
|
{
|
||||||
A0 += *(unsigned long *)((char *)env + PARAM1);
|
A0 += *(unsigned long *)((char *)env + PARAM1);
|
||||||
@@ -1139,10 +1234,16 @@ void OPPROTO op_set_cc_op(void)
|
|||||||
CC_OP = PARAM1;
|
CC_OP = PARAM1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define FL_UPDATE_MASK (TF_MASK | AC_MASK | ID_MASK)
|
||||||
|
|
||||||
void OPPROTO op_movl_eflags_T0(void)
|
void OPPROTO op_movl_eflags_T0(void)
|
||||||
{
|
{
|
||||||
CC_SRC = T0;
|
int eflags;
|
||||||
DF = 1 - (2 * ((T0 >> 10) & 1));
|
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 */
|
/* XXX: compute only O flag */
|
||||||
@@ -1150,13 +1251,16 @@ void OPPROTO op_movb_eflags_T0(void)
|
|||||||
{
|
{
|
||||||
int of;
|
int of;
|
||||||
of = cc_table[CC_OP].compute_all() & CC_O;
|
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)
|
void OPPROTO op_movl_T0_eflags(void)
|
||||||
{
|
{
|
||||||
T0 = cc_table[CC_OP].compute_all();
|
int eflags;
|
||||||
T0 |= (DF & DIRECTION_FLAG);
|
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)
|
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)
|
void OPPROTO glue(op_movl_A0,REGNAME)(void)
|
||||||
{
|
{
|
||||||
A0 = REG;
|
A0 = REG;
|
||||||
|
272
ops_template.h
272
ops_template.h
@@ -4,21 +4,20 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2003 Fabrice Bellard
|
* Copyright (c) 2003 Fabrice Bellard
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This library is free software; you can redistribute it and/or
|
||||||
* it under the terms of the GNU General Public License as published by
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
* License as published by the Free Software Foundation; either
|
||||||
* (at your option) any later version.
|
* version 2 of the License, or (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This library is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
* GNU General Public License for more details.
|
* Lesser General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* along with this program; if not, write to the Free Software
|
* License along with this library; if not, write to the Free Software
|
||||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define DATA_BITS (1 << (3 + SHIFT))
|
#define DATA_BITS (1 << (3 + SHIFT))
|
||||||
#define SHIFT_MASK (DATA_BITS - 1)
|
#define SHIFT_MASK (DATA_BITS - 1)
|
||||||
#define SIGN_MASK (1 << (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
|
#endif
|
||||||
|
|
||||||
/* string operations */
|
/* 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)
|
#define STRING_SUFFIX _fast
|
||||||
{
|
#define SI_ADDR (void *)ESI
|
||||||
int v;
|
#define DI_ADDR (void *)EDI
|
||||||
v = glue(ldu, SUFFIX)((void *)ESI);
|
#define INC_SI() ESI += inc
|
||||||
glue(st, SUFFIX)((void *)EDI, v);
|
#define INC_DI() EDI += inc
|
||||||
ESI += (DF << SHIFT);
|
#define CX ECX
|
||||||
EDI += (DF << SHIFT);
|
#define DEC_CX() ECX--
|
||||||
}
|
#include "op_string.h"
|
||||||
|
|
||||||
void OPPROTO glue(op_rep_movs, SUFFIX)(void)
|
#define STRING_SUFFIX _a32
|
||||||
{
|
#define SI_ADDR (uint8_t *)A0 + ESI
|
||||||
int v, inc;
|
#define DI_ADDR env->seg_cache[R_ES].base + EDI
|
||||||
inc = (DF << SHIFT);
|
#define INC_SI() ESI += inc
|
||||||
while (ECX != 0) {
|
#define INC_DI() EDI += inc
|
||||||
v = glue(ldu, SUFFIX)((void *)ESI);
|
#define CX ECX
|
||||||
glue(st, SUFFIX)((void *)EDI, v);
|
#define DEC_CX() ECX--
|
||||||
ESI += inc;
|
#include "op_string.h"
|
||||||
EDI += inc;
|
|
||||||
ECX--;
|
|
||||||
}
|
|
||||||
FORCE_RET();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OPPROTO glue(op_stos, SUFFIX)(void)
|
#define STRING_SUFFIX _a16
|
||||||
{
|
#define SI_ADDR (uint8_t *)A0 + (ESI & 0xffff)
|
||||||
glue(st, SUFFIX)((void *)EDI, EAX);
|
#define DI_ADDR env->seg_cache[R_ES].base + (EDI & 0xffff)
|
||||||
EDI += (DF << SHIFT);
|
#define INC_SI() ESI = (ESI & ~0xffff) | ((ESI + inc) & 0xffff)
|
||||||
}
|
#define INC_DI() EDI = (EDI & ~0xffff) | ((EDI + inc) & 0xffff)
|
||||||
|
#define CX (ECX & 0xffff)
|
||||||
void OPPROTO glue(op_rep_stos, SUFFIX)(void)
|
#define DEC_CX() ECX = (ECX & ~0xffff) | ((ECX - 1) & 0xffff)
|
||||||
{
|
#include "op_string.h"
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* port I/O */
|
/* 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)
|
void OPPROTO glue(glue(op_out, SUFFIX), _T0_T1)(void)
|
||||||
{
|
{
|
||||||
glue(cpu_x86_out, SUFFIX)(T0 & 0xffff, T1 & DATA_MASK);
|
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_removexattr 235
|
||||||
#define TARGET_NR_lremovexattr 236
|
#define TARGET_NR_lremovexattr 236
|
||||||
#define TARGET_NR_fremovexattr 237
|
#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_BLOCK 0 /* for blocking signals */
|
||||||
#define TARGET_SIG_UNBLOCK 1 /* for unblocking signals */
|
#define TARGET_SIG_UNBLOCK 1 /* for unblocking signals */
|
||||||
@@ -255,11 +285,11 @@ struct target_stat {
|
|||||||
target_ulong st_size;
|
target_ulong st_size;
|
||||||
target_ulong st_blksize;
|
target_ulong st_blksize;
|
||||||
target_ulong st_blocks;
|
target_ulong st_blocks;
|
||||||
target_ulong st_atime;
|
target_ulong target_st_atime;
|
||||||
target_ulong __unused1;
|
target_ulong __unused1;
|
||||||
target_ulong st_mtime;
|
target_ulong target_st_mtime;
|
||||||
target_ulong __unused2;
|
target_ulong __unused2;
|
||||||
target_ulong st_ctime;
|
target_ulong target_st_ctime;
|
||||||
target_ulong __unused3;
|
target_ulong __unused3;
|
||||||
target_ulong __unused4;
|
target_ulong __unused4;
|
||||||
target_ulong __unused5;
|
target_ulong __unused5;
|
||||||
@@ -290,13 +320,13 @@ struct target_stat64 {
|
|||||||
target_ulong st_blocks; /* Number 512-byte blocks allocated. */
|
target_ulong st_blocks; /* Number 512-byte blocks allocated. */
|
||||||
target_ulong __pad4; /* future possible st_blocks high bits */
|
target_ulong __pad4; /* future possible st_blocks high bits */
|
||||||
|
|
||||||
target_ulong st_atime;
|
target_ulong target_st_atime;
|
||||||
target_ulong __pad5;
|
target_ulong __pad5;
|
||||||
|
|
||||||
target_ulong st_mtime;
|
target_ulong target_st_mtime;
|
||||||
target_ulong __pad6;
|
target_ulong __pad6;
|
||||||
|
|
||||||
target_ulong st_ctime;
|
target_ulong target_st_ctime;
|
||||||
target_ulong __pad7; /* will be high 32 bits of ctime someday */
|
target_ulong __pad7; /* will be high 32 bits of ctime someday */
|
||||||
|
|
||||||
unsigned long long st_ino;
|
unsigned long long st_ino;
|
||||||
@@ -714,6 +744,10 @@ struct target_termios {
|
|||||||
#define TARGET_LDT_ENTRIES 8192
|
#define TARGET_LDT_ENTRIES 8192
|
||||||
#define TARGET_LDT_ENTRY_SIZE 8
|
#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 {
|
struct target_modify_ldt_ldt_s {
|
||||||
unsigned int entry_number;
|
unsigned int entry_number;
|
||||||
target_ulong base_addr;
|
target_ulong base_addr;
|
||||||
@@ -721,6 +755,182 @@ struct target_modify_ldt_ldt_s {
|
|||||||
unsigned int flags;
|
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) */
|
/* soundcard defines (XXX: move them to generic file syscall_defs.h) */
|
||||||
|
|
||||||
#define TARGET_SNDCTL_COPR_HALT 0xc0144307
|
#define TARGET_SNDCTL_COPR_HALT 0xc0144307
|
||||||
|
@@ -4,7 +4,7 @@ CFLAGS=-Wall -O2 -g
|
|||||||
LDFLAGS=
|
LDFLAGS=
|
||||||
|
|
||||||
ifeq ($(ARCH),i386)
|
ifeq ($(ARCH),i386)
|
||||||
TESTS=testclone testsig testthread sha1-i386 test-i386
|
TESTS=testclone testsig testthread sha1-i386 test-i386 runcom
|
||||||
endif
|
endif
|
||||||
TESTS+=sha1
|
TESTS+=sha1
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ testthread: testthread.c
|
|||||||
# i386 emulation test (test various opcodes) */
|
# i386 emulation test (test various opcodes) */
|
||||||
test-i386: test-i386.c test-i386-code16.S \
|
test-i386: test-i386.c test-i386-code16.S \
|
||||||
test-i386.h test-i386-shift.h test-i386-muldiv.h
|
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
|
test: test-i386
|
||||||
ifeq ($(ARCH),i386)
|
ifeq ($(ARCH),i386)
|
||||||
@@ -48,5 +48,9 @@ speed: sha1 sha1-i386
|
|||||||
time ./sha1
|
time ./sha1
|
||||||
time $(QEMU) ./sha1-i386
|
time $(QEMU) ./sha1-i386
|
||||||
|
|
||||||
|
# vm86 test
|
||||||
|
runcom: runcom.c
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *~ *.o $(TESTS)
|
rm -f *~ *.o $(TESTS)
|
||||||
|
@@ -714,6 +714,10 @@ void test_segs(void)
|
|||||||
long long ldt_table[3];
|
long long ldt_table[3];
|
||||||
int res, res2;
|
int res, res2;
|
||||||
char tmp;
|
char tmp;
|
||||||
|
struct {
|
||||||
|
uint32_t offset;
|
||||||
|
uint16_t seg;
|
||||||
|
} __attribute__((packed)) segoff;
|
||||||
|
|
||||||
ldt.entry_number = 1;
|
ldt.entry_number = 1;
|
||||||
ldt.base_addr = (unsigned long)&seg_data1;
|
ldt.base_addr = (unsigned long)&seg_data1;
|
||||||
@@ -772,6 +776,14 @@ void test_segs(void)
|
|||||||
: "r" (MK_SEL(1)), "r" (&tmp));
|
: "r" (MK_SEL(1)), "r" (&tmp));
|
||||||
printf("DS[1] = %02x\n", res);
|
printf("DS[1] = %02x\n", res);
|
||||||
printf("SS[tmp] = %02x\n", res2);
|
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 */
|
/* 16 bit code test */
|
||||||
@@ -812,6 +824,71 @@ void test_code16(void)
|
|||||||
printf("func3() = 0x%08x\n", res);
|
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;
|
static void *call_end __init_call = NULL;
|
||||||
|
|
||||||
@@ -831,6 +908,8 @@ int main(int argc, char **argv)
|
|||||||
test_floats();
|
test_floats();
|
||||||
test_bcd();
|
test_bcd();
|
||||||
test_xchg();
|
test_xchg();
|
||||||
|
test_string();
|
||||||
|
test_misc();
|
||||||
test_lea();
|
test_lea();
|
||||||
test_segs();
|
test_segs();
|
||||||
test_code16();
|
test_code16();
|
||||||
|
@@ -15,21 +15,34 @@ void alarm_handler(int sig)
|
|||||||
alarm(1);
|
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)
|
void dump_regs(struct ucontext *uc)
|
||||||
{
|
{
|
||||||
printf("EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n"
|
printf("EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n"
|
||||||
"ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n"
|
"ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n"
|
||||||
"EFL=%08x EIP=%08x\n",
|
"EFL=%08x EIP=%08x\n",
|
||||||
uc->uc_mcontext.gregs[EAX],
|
uc->uc_mcontext.gregs[REG_EAX],
|
||||||
uc->uc_mcontext.gregs[EBX],
|
uc->uc_mcontext.gregs[REG_EBX],
|
||||||
uc->uc_mcontext.gregs[ECX],
|
uc->uc_mcontext.gregs[REG_ECX],
|
||||||
uc->uc_mcontext.gregs[EDX],
|
uc->uc_mcontext.gregs[REG_EDX],
|
||||||
uc->uc_mcontext.gregs[ESI],
|
uc->uc_mcontext.gregs[REG_ESI],
|
||||||
uc->uc_mcontext.gregs[EDI],
|
uc->uc_mcontext.gregs[REG_EDI],
|
||||||
uc->uc_mcontext.gregs[EBP],
|
uc->uc_mcontext.gregs[REG_EBP],
|
||||||
uc->uc_mcontext.gregs[ESP],
|
uc->uc_mcontext.gregs[REG_ESP],
|
||||||
uc->uc_mcontext.gregs[EFL],
|
uc->uc_mcontext.gregs[REG_EFL],
|
||||||
uc->uc_mcontext.gregs[EIP]);
|
uc->uc_mcontext.gregs[REG_EIP]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sig_handler(int sig, siginfo_t *info, void *puc)
|
void sig_handler(int sig, siginfo_t *info, void *puc)
|
||||||
|
394
translate-i386.c
394
translate-i386.c
@@ -50,6 +50,12 @@ static inline void flush_icache_range(unsigned long start, unsigned long stop)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __s390__
|
||||||
|
static inline void flush_icache_range(unsigned long start, unsigned long stop)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __powerpc__
|
#ifdef __powerpc__
|
||||||
|
|
||||||
#define MIN_CACHE_LINE_SIZE 8 /* conservative value */
|
#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 FILE *logfile;
|
||||||
extern int loglevel;
|
extern int loglevel;
|
||||||
|
|
||||||
#define PREFIX_REPZ 1
|
#define PREFIX_REPZ 0x01
|
||||||
#define PREFIX_REPNZ 2
|
#define PREFIX_REPNZ 0x02
|
||||||
#define PREFIX_LOCK 4
|
#define PREFIX_LOCK 0x04
|
||||||
#define PREFIX_CS 8
|
#define PREFIX_DATA 0x08
|
||||||
#define PREFIX_SS 0x10
|
#define PREFIX_ADR 0x10
|
||||||
#define PREFIX_DS 0x20
|
#define PREFIX_FWAIT 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
|
|
||||||
|
|
||||||
typedef struct DisasContext {
|
typedef struct DisasContext {
|
||||||
/* current insn context */
|
/* current insn context */
|
||||||
|
int override; /* -1 if no override */
|
||||||
int prefix;
|
int prefix;
|
||||||
int aflag, dflag;
|
int aflag, dflag;
|
||||||
uint8_t *pc; /* pc = eip + cs_base */
|
uint8_t *pc; /* pc = eip + cs_base */
|
||||||
@@ -103,6 +104,7 @@ typedef struct DisasContext {
|
|||||||
int cc_op; /* current CC operation */
|
int cc_op; /* current CC operation */
|
||||||
int addseg; /* non zero if either DS/ES/SS have a non zero base */
|
int addseg; /* non zero if either DS/ES/SS have a non zero base */
|
||||||
int f_st; /* currently unused */
|
int f_st; /* currently unused */
|
||||||
|
int vm86; /* vm86 mode */
|
||||||
} DisasContext;
|
} DisasContext;
|
||||||
|
|
||||||
/* i386 arith/logic operations */
|
/* i386 arith/logic operations */
|
||||||
@@ -130,7 +132,7 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
#define DEF(s) INDEX_op_ ## s,
|
#define DEF(s, n) INDEX_op_ ## s,
|
||||||
#include "opc-i386.h"
|
#include "opc-i386.h"
|
||||||
#undef DEF
|
#undef DEF
|
||||||
NB_OPS,
|
NB_OPS,
|
||||||
@@ -556,76 +558,100 @@ static GenOpFunc *gen_op_st_T0_A0[3] = {
|
|||||||
gen_op_stl_T0_A0,
|
gen_op_stl_T0_A0,
|
||||||
};
|
};
|
||||||
|
|
||||||
static GenOpFunc *gen_op_movs[6] = {
|
/* the _a32 and _a16 string operations use A0 as the base register. */
|
||||||
gen_op_movsb,
|
|
||||||
gen_op_movsw,
|
#define STRINGOP(x) \
|
||||||
gen_op_movsl,
|
gen_op_ ## x ## b_fast, \
|
||||||
gen_op_rep_movsb,
|
gen_op_ ## x ## w_fast, \
|
||||||
gen_op_rep_movsw,
|
gen_op_ ## x ## l_fast, \
|
||||||
gen_op_rep_movsl,
|
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] = {
|
static GenOpFunc *gen_op_stos[9 * 2] = {
|
||||||
gen_op_stosb,
|
STRINGOP(stos)
|
||||||
gen_op_stosw,
|
STRINGOP(rep_stos)
|
||||||
gen_op_stosl,
|
|
||||||
gen_op_rep_stosb,
|
|
||||||
gen_op_rep_stosw,
|
|
||||||
gen_op_rep_stosl,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static GenOpFunc *gen_op_lods[6] = {
|
static GenOpFunc *gen_op_lods[9 * 2] = {
|
||||||
gen_op_lodsb,
|
STRINGOP(lods)
|
||||||
gen_op_lodsw,
|
STRINGOP(rep_lods)
|
||||||
gen_op_lodsl,
|
|
||||||
gen_op_rep_lodsb,
|
|
||||||
gen_op_rep_lodsw,
|
|
||||||
gen_op_rep_lodsl,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static GenOpFunc *gen_op_scas[9] = {
|
static GenOpFunc *gen_op_scas[9 * 3] = {
|
||||||
gen_op_scasb,
|
STRINGOP(scas)
|
||||||
gen_op_scasw,
|
STRINGOP(repz_scas)
|
||||||
gen_op_scasl,
|
STRINGOP(repnz_scas)
|
||||||
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_cmps[9] = {
|
static GenOpFunc *gen_op_cmps[9 * 3] = {
|
||||||
gen_op_cmpsb,
|
STRINGOP(cmps)
|
||||||
gen_op_cmpsw,
|
STRINGOP(repz_cmps)
|
||||||
gen_op_cmpsl,
|
STRINGOP(repnz_cmps)
|
||||||
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_ins[6] = {
|
static GenOpFunc *gen_op_ins[9 * 2] = {
|
||||||
gen_op_insb,
|
STRINGOP(ins)
|
||||||
gen_op_insw,
|
STRINGOP(rep_ins)
|
||||||
gen_op_insl,
|
|
||||||
gen_op_rep_insb,
|
|
||||||
gen_op_rep_insw,
|
|
||||||
gen_op_rep_insl,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static GenOpFunc *gen_op_outs[6] = {
|
static GenOpFunc *gen_op_outs[9 * 2] = {
|
||||||
gen_op_outsb,
|
STRINGOP(outs)
|
||||||
gen_op_outsw,
|
STRINGOP(rep_outs)
|
||||||
gen_op_outsl,
|
|
||||||
gen_op_rep_outsb,
|
|
||||||
gen_op_rep_outsw,
|
|
||||||
gen_op_rep_outsl,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
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] = {
|
static GenOpFunc *gen_op_in[3] = {
|
||||||
gen_op_inb_T0_T1,
|
gen_op_inb_T0_T1,
|
||||||
gen_op_inw_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 opreg;
|
||||||
int mod, rm, code, override, must_add_seg;
|
int mod, rm, code, override, must_add_seg;
|
||||||
|
|
||||||
/* XXX: add a generation time variable to tell if base == 0 in DS/ES/SS */
|
override = s->override;
|
||||||
override = -1;
|
|
||||||
must_add_seg = s->addseg;
|
must_add_seg = s->addseg;
|
||||||
if (s->prefix & (PREFIX_CS | PREFIX_SS | PREFIX_DS |
|
if (override >= 0)
|
||||||
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;
|
|
||||||
must_add_seg = 1;
|
must_add_seg = 1;
|
||||||
}
|
|
||||||
|
|
||||||
mod = (modrm >> 6) & 3;
|
mod = (modrm >> 6) & 3;
|
||||||
rm = modrm & 7;
|
rm = modrm & 7;
|
||||||
|
|
||||||
@@ -1343,7 +1353,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
|||||||
prefixes = 0;
|
prefixes = 0;
|
||||||
aflag = s->code32;
|
aflag = s->code32;
|
||||||
dflag = s->code32;
|
dflag = s->code32;
|
||||||
// cur_pc = s->pc; /* for insn generation */
|
s->override = -1;
|
||||||
next_byte:
|
next_byte:
|
||||||
b = ldub(s->pc);
|
b = ldub(s->pc);
|
||||||
s->pc++;
|
s->pc++;
|
||||||
@@ -1359,22 +1369,22 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
|||||||
prefixes |= PREFIX_LOCK;
|
prefixes |= PREFIX_LOCK;
|
||||||
goto next_byte;
|
goto next_byte;
|
||||||
case 0x2e:
|
case 0x2e:
|
||||||
prefixes |= PREFIX_CS;
|
s->override = R_CS;
|
||||||
goto next_byte;
|
goto next_byte;
|
||||||
case 0x36:
|
case 0x36:
|
||||||
prefixes |= PREFIX_SS;
|
s->override = R_SS;
|
||||||
goto next_byte;
|
goto next_byte;
|
||||||
case 0x3e:
|
case 0x3e:
|
||||||
prefixes |= PREFIX_DS;
|
s->override = R_DS;
|
||||||
goto next_byte;
|
goto next_byte;
|
||||||
case 0x26:
|
case 0x26:
|
||||||
prefixes |= PREFIX_ES;
|
s->override = R_ES;
|
||||||
goto next_byte;
|
goto next_byte;
|
||||||
case 0x64:
|
case 0x64:
|
||||||
prefixes |= PREFIX_FS;
|
s->override = R_FS;
|
||||||
goto next_byte;
|
goto next_byte;
|
||||||
case 0x65:
|
case 0x65:
|
||||||
prefixes |= PREFIX_GS;
|
s->override = R_GS;
|
||||||
goto next_byte;
|
goto next_byte;
|
||||||
case 0x66:
|
case 0x66:
|
||||||
prefixes |= PREFIX_DATA;
|
prefixes |= PREFIX_DATA;
|
||||||
@@ -1830,6 +1840,17 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
|||||||
}
|
}
|
||||||
s->cc_op = CC_OP_SUBB + ot;
|
s->cc_op = CC_OP_SUBB + ot;
|
||||||
break;
|
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 */
|
/* push/pop */
|
||||||
@@ -2027,8 +2048,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
|||||||
modrm = ldub(s->pc++);
|
modrm = ldub(s->pc++);
|
||||||
reg = (modrm >> 3) & 7;
|
reg = (modrm >> 3) & 7;
|
||||||
/* we must ensure that no segment is added */
|
/* we must ensure that no segment is added */
|
||||||
s->prefix &= ~(PREFIX_CS | PREFIX_SS | PREFIX_DS |
|
s->override = -1;
|
||||||
PREFIX_ES | PREFIX_FS | PREFIX_GS);
|
|
||||||
val = s->addseg;
|
val = s->addseg;
|
||||||
s->addseg = 0;
|
s->addseg = 0;
|
||||||
gen_lea_modrm(s, modrm, ®_addr, &offset_addr);
|
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);
|
offset_addr = insn_get(s, OT_WORD);
|
||||||
gen_op_movl_A0_im(offset_addr);
|
gen_op_movl_A0_im(offset_addr);
|
||||||
/* handle override */
|
/* handle override */
|
||||||
/* XXX: factorize that */
|
|
||||||
{
|
{
|
||||||
int override, must_add_seg;
|
int override, must_add_seg;
|
||||||
override = R_DS;
|
|
||||||
must_add_seg = s->addseg;
|
must_add_seg = s->addseg;
|
||||||
if (s->prefix & (PREFIX_CS | PREFIX_SS | PREFIX_DS |
|
if (s->override >= 0) {
|
||||||
PREFIX_ES | PREFIX_FS | PREFIX_GS)) {
|
override = s->override;
|
||||||
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;
|
|
||||||
must_add_seg = 1;
|
must_add_seg = 1;
|
||||||
|
} else {
|
||||||
|
override = R_DS;
|
||||||
}
|
}
|
||||||
if (must_add_seg) {
|
if (must_add_seg) {
|
||||||
gen_op_addl_A0_seg(offsetof(CPUX86State,seg_cache[override].base));
|
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]();
|
gen_op_st_T0_A0[ot]();
|
||||||
}
|
}
|
||||||
break;
|
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 */
|
case 0xb0 ... 0xb7: /* mov R, Ib */
|
||||||
val = insn_get(s, OT_BYTE);
|
val = insn_get(s, OT_BYTE);
|
||||||
gen_op_movl_T0_im(val);
|
gen_op_movl_T0_im(val);
|
||||||
@@ -2121,8 +2151,13 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
|||||||
} else {
|
} else {
|
||||||
gen_lea_modrm(s, modrm, ®_addr, &offset_addr);
|
gen_lea_modrm(s, modrm, ®_addr, &offset_addr);
|
||||||
gen_op_mov_TN_reg[ot][0][reg]();
|
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_ld_T1_A0[ot]();
|
||||||
gen_op_st_T0_A0[ot]();
|
gen_op_st_T0_A0[ot]();
|
||||||
|
if (!(prefixes & PREFIX_LOCK))
|
||||||
|
gen_op_unlock();
|
||||||
gen_op_mov_reg_T1[ot][reg]();
|
gen_op_mov_reg_T1[ot][reg]();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -2147,6 +2182,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
|||||||
mod = (modrm >> 6) & 3;
|
mod = (modrm >> 6) & 3;
|
||||||
if (mod == 3)
|
if (mod == 3)
|
||||||
goto illegal_op;
|
goto illegal_op;
|
||||||
|
gen_lea_modrm(s, modrm, ®_addr, &offset_addr);
|
||||||
gen_op_ld_T1_A0[ot]();
|
gen_op_ld_T1_A0[ot]();
|
||||||
gen_op_addl_A0_im(1 << (ot - OT_WORD + 1));
|
gen_op_addl_A0_im(1 << (ot - OT_WORD + 1));
|
||||||
/* load the segment first to handle exceptions properly */
|
/* load the segment first to handle exceptions properly */
|
||||||
@@ -2620,16 +2656,18 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
|||||||
break;
|
break;
|
||||||
/************************/
|
/************************/
|
||||||
/* string ops */
|
/* string ops */
|
||||||
|
|
||||||
case 0xa4: /* movsS */
|
case 0xa4: /* movsS */
|
||||||
case 0xa5:
|
case 0xa5:
|
||||||
if ((b & 1) == 0)
|
if ((b & 1) == 0)
|
||||||
ot = OT_BYTE;
|
ot = OT_BYTE;
|
||||||
else
|
else
|
||||||
ot = dflag ? OT_LONG : OT_WORD;
|
ot = dflag ? OT_LONG : OT_WORD;
|
||||||
|
|
||||||
if (prefixes & PREFIX_REPZ) {
|
if (prefixes & PREFIX_REPZ) {
|
||||||
gen_op_movs[3 + ot]();
|
gen_string_ds(s, ot, gen_op_movs + 9);
|
||||||
} else {
|
} else {
|
||||||
gen_op_movs[ot]();
|
gen_string_ds(s, ot, gen_op_movs);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -2639,10 +2677,11 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
|||||||
ot = OT_BYTE;
|
ot = OT_BYTE;
|
||||||
else
|
else
|
||||||
ot = dflag ? OT_LONG : OT_WORD;
|
ot = dflag ? OT_LONG : OT_WORD;
|
||||||
|
|
||||||
if (prefixes & PREFIX_REPZ) {
|
if (prefixes & PREFIX_REPZ) {
|
||||||
gen_op_stos[3 + ot]();
|
gen_string_es(s, ot, gen_op_stos + 9);
|
||||||
} else {
|
} else {
|
||||||
gen_op_stos[ot]();
|
gen_string_es(s, ot, gen_op_stos);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0xac: /* lodsS */
|
case 0xac: /* lodsS */
|
||||||
@@ -2652,9 +2691,9 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
|||||||
else
|
else
|
||||||
ot = dflag ? OT_LONG : OT_WORD;
|
ot = dflag ? OT_LONG : OT_WORD;
|
||||||
if (prefixes & PREFIX_REPZ) {
|
if (prefixes & PREFIX_REPZ) {
|
||||||
gen_op_lods[3 + ot]();
|
gen_string_ds(s, ot, gen_op_lods + 9);
|
||||||
} else {
|
} else {
|
||||||
gen_op_lods[ot]();
|
gen_string_ds(s, ot, gen_op_lods);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0xae: /* scasS */
|
case 0xae: /* scasS */
|
||||||
@@ -2666,15 +2705,15 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
|||||||
if (prefixes & PREFIX_REPNZ) {
|
if (prefixes & PREFIX_REPNZ) {
|
||||||
if (s->cc_op != CC_OP_DYNAMIC)
|
if (s->cc_op != CC_OP_DYNAMIC)
|
||||||
gen_op_set_cc_op(s->cc_op);
|
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 */
|
s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */
|
||||||
} else if (prefixes & PREFIX_REPZ) {
|
} else if (prefixes & PREFIX_REPZ) {
|
||||||
if (s->cc_op != CC_OP_DYNAMIC)
|
if (s->cc_op != CC_OP_DYNAMIC)
|
||||||
gen_op_set_cc_op(s->cc_op);
|
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 */
|
s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */
|
||||||
} else {
|
} else {
|
||||||
gen_op_scas[ot]();
|
gen_string_es(s, ot, gen_op_scas);
|
||||||
s->cc_op = CC_OP_SUBB + ot;
|
s->cc_op = CC_OP_SUBB + ot;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -2688,21 +2727,18 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
|||||||
if (prefixes & PREFIX_REPNZ) {
|
if (prefixes & PREFIX_REPNZ) {
|
||||||
if (s->cc_op != CC_OP_DYNAMIC)
|
if (s->cc_op != CC_OP_DYNAMIC)
|
||||||
gen_op_set_cc_op(s->cc_op);
|
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 */
|
s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */
|
||||||
} else if (prefixes & PREFIX_REPZ) {
|
} else if (prefixes & PREFIX_REPZ) {
|
||||||
if (s->cc_op != CC_OP_DYNAMIC)
|
if (s->cc_op != CC_OP_DYNAMIC)
|
||||||
gen_op_set_cc_op(s->cc_op);
|
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 */
|
s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */
|
||||||
} else {
|
} else {
|
||||||
gen_op_cmps[ot]();
|
gen_string_ds(s, ot, gen_op_cmps);
|
||||||
s->cc_op = CC_OP_SUBB + ot;
|
s->cc_op = CC_OP_SUBB + ot;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/************************/
|
|
||||||
/* port I/O */
|
|
||||||
case 0x6c: /* insS */
|
case 0x6c: /* insS */
|
||||||
case 0x6d:
|
case 0x6d:
|
||||||
if ((b & 1) == 0)
|
if ((b & 1) == 0)
|
||||||
@@ -2710,9 +2746,9 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
|||||||
else
|
else
|
||||||
ot = dflag ? OT_LONG : OT_WORD;
|
ot = dflag ? OT_LONG : OT_WORD;
|
||||||
if (prefixes & PREFIX_REPZ) {
|
if (prefixes & PREFIX_REPZ) {
|
||||||
gen_op_ins[3 + ot]();
|
gen_string_es(s, ot, gen_op_ins + 9);
|
||||||
} else {
|
} else {
|
||||||
gen_op_ins[ot]();
|
gen_string_es(s, ot, gen_op_ins);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x6e: /* outsS */
|
case 0x6e: /* outsS */
|
||||||
@@ -2722,11 +2758,14 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
|||||||
else
|
else
|
||||||
ot = dflag ? OT_LONG : OT_WORD;
|
ot = dflag ? OT_LONG : OT_WORD;
|
||||||
if (prefixes & PREFIX_REPZ) {
|
if (prefixes & PREFIX_REPZ) {
|
||||||
gen_op_outs[3 + ot]();
|
gen_string_ds(s, ot, gen_op_outs + 9);
|
||||||
} else {
|
} else {
|
||||||
gen_op_outs[ot]();
|
gen_string_ds(s, ot, gen_op_outs);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/************************/
|
||||||
|
/* port I/O */
|
||||||
case 0xe4:
|
case 0xe4:
|
||||||
case 0xe5:
|
case 0xe5:
|
||||||
if ((b & 1) == 0)
|
if ((b & 1) == 0)
|
||||||
@@ -3112,14 +3151,27 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
|||||||
case 0xcd: /* int N */
|
case 0xcd: /* int N */
|
||||||
val = ldub(s->pc++);
|
val = ldub(s->pc++);
|
||||||
/* XXX: currently we ignore the interrupt number */
|
/* 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;
|
s->is_jmp = 1;
|
||||||
break;
|
break;
|
||||||
case 0xce: /* into */
|
case 0xce: /* into */
|
||||||
if (s->cc_op != CC_OP_DYNAMIC)
|
if (s->cc_op != CC_OP_DYNAMIC)
|
||||||
gen_op_set_cc_op(s->cc_op);
|
gen_op_set_cc_op(s->cc_op);
|
||||||
gen_op_into((long)pc_start, (long)s->pc);
|
gen_op_into();
|
||||||
s->is_jmp = 1;
|
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;
|
break;
|
||||||
case 0x1c8 ... 0x1cf: /* bswap reg */
|
case 0x1c8 ... 0x1cf: /* bswap reg */
|
||||||
reg = b & 7;
|
reg = b & 7;
|
||||||
@@ -3150,11 +3202,9 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
|
|||||||
case 0x131: /* rdtsc */
|
case 0x131: /* rdtsc */
|
||||||
gen_op_rdtsc();
|
gen_op_rdtsc();
|
||||||
break;
|
break;
|
||||||
#if 0
|
|
||||||
case 0x1a2: /* cpuid */
|
case 0x1a2: /* cpuid */
|
||||||
gen_insn0(OP_ASM);
|
gen_op_cpuid();
|
||||||
break;
|
break;
|
||||||
#endif
|
|
||||||
default:
|
default:
|
||||||
goto illegal_op;
|
goto illegal_op;
|
||||||
}
|
}
|
||||||
@@ -3361,28 +3411,30 @@ static uint16_t opc_write_flags[NB_OPS] = {
|
|||||||
[INDEX_op_bsrw_T0_cc] = CC_OSZAPC,
|
[INDEX_op_bsrw_T0_cc] = CC_OSZAPC,
|
||||||
[INDEX_op_bsrl_T0_cc] = CC_OSZAPC,
|
[INDEX_op_bsrl_T0_cc] = CC_OSZAPC,
|
||||||
|
|
||||||
[INDEX_op_scasb] = CC_OSZAPC,
|
#undef STRINGOP
|
||||||
[INDEX_op_scasw] = CC_OSZAPC,
|
#define STRINGOP(x) \
|
||||||
[INDEX_op_scasl] = CC_OSZAPC,
|
[INDEX_op_ ## x ## b_fast] = CC_OSZAPC, \
|
||||||
[INDEX_op_repz_scasb] = CC_OSZAPC,
|
[INDEX_op_ ## x ## w_fast] = CC_OSZAPC, \
|
||||||
[INDEX_op_repz_scasw] = CC_OSZAPC,
|
[INDEX_op_ ## x ## l_fast] = CC_OSZAPC, \
|
||||||
[INDEX_op_repz_scasl] = CC_OSZAPC,
|
[INDEX_op_ ## x ## b_a32] = CC_OSZAPC, \
|
||||||
[INDEX_op_repnz_scasb] = CC_OSZAPC,
|
[INDEX_op_ ## x ## w_a32] = CC_OSZAPC, \
|
||||||
[INDEX_op_repnz_scasw] = CC_OSZAPC,
|
[INDEX_op_ ## x ## l_a32] = CC_OSZAPC, \
|
||||||
[INDEX_op_repnz_scasl] = 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,
|
STRINGOP(scas)
|
||||||
[INDEX_op_cmpsw] = CC_OSZAPC,
|
STRINGOP(repz_scas)
|
||||||
[INDEX_op_cmpsl] = CC_OSZAPC,
|
STRINGOP(repnz_scas)
|
||||||
[INDEX_op_repz_cmpsb] = CC_OSZAPC,
|
STRINGOP(cmps)
|
||||||
[INDEX_op_repz_cmpsw] = CC_OSZAPC,
|
STRINGOP(repz_cmps)
|
||||||
[INDEX_op_repz_cmpsl] = CC_OSZAPC,
|
STRINGOP(repnz_cmps)
|
||||||
[INDEX_op_repnz_cmpsb] = CC_OSZAPC,
|
|
||||||
[INDEX_op_repnz_cmpsw] = CC_OSZAPC,
|
|
||||||
[INDEX_op_repnz_cmpsl] = CC_OSZAPC,
|
|
||||||
|
|
||||||
|
[INDEX_op_cmpxchgb_T0_T1_EAX_cc] = CC_OSZAPC,
|
||||||
[INDEX_op_cmpxchgw_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_cmpxchgl_T0_T1_EAX_cc] = CC_OSZAPC,
|
||||||
|
|
||||||
|
[INDEX_op_cmpxchg8b] = CC_Z,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* simpler form of an operation if no flags need to be generated */
|
/* 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
|
#ifdef DEBUG_DISAS
|
||||||
static const char *op_str[] = {
|
static const char *op_str[] = {
|
||||||
#define DEF(s) #s,
|
#define DEF(s, n) #s,
|
||||||
#include "opc-i386.h"
|
#include "opc-i386.h"
|
||||||
#undef DEF
|
#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;
|
const uint16_t *opc_ptr;
|
||||||
int c;
|
const uint32_t *opparam_ptr;
|
||||||
|
int c, n, i;
|
||||||
|
|
||||||
opc_ptr = opc_buf;
|
opc_ptr = opc_buf;
|
||||||
|
opparam_ptr = opparam_buf;
|
||||||
for(;;) {
|
for(;;) {
|
||||||
c = *opc_ptr++;
|
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)
|
if (c == INDEX_op_end)
|
||||||
break;
|
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->ss32 = (flags >> GEN_FLAG_SS32_SHIFT) & 1;
|
||||||
dc->addseg = (flags >> GEN_FLAG_ADDSEG_SHIFT) & 1;
|
dc->addseg = (flags >> GEN_FLAG_ADDSEG_SHIFT) & 1;
|
||||||
dc->f_st = (flags >> GEN_FLAG_ST_SHIFT) & 7;
|
dc->f_st = (flags >> GEN_FLAG_ST_SHIFT) & 7;
|
||||||
|
dc->vm86 = (flags >> GEN_FLAG_VM_SHIFT) & 1;
|
||||||
dc->cc_op = CC_OP_DYNAMIC;
|
dc->cc_op = CC_OP_DYNAMIC;
|
||||||
dc->cs_base = cs_base;
|
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, "\n");
|
||||||
|
|
||||||
fprintf(logfile, "OP:\n");
|
fprintf(logfile, "OP:\n");
|
||||||
dump_ops(gen_opc_buf);
|
dump_ops(gen_opc_buf, gen_opparam_buf);
|
||||||
fprintf(logfile, "\n");
|
fprintf(logfile, "\n");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -3583,7 +3651,7 @@ int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size,
|
|||||||
#ifdef DEBUG_DISAS
|
#ifdef DEBUG_DISAS
|
||||||
if (loglevel) {
|
if (loglevel) {
|
||||||
fprintf(logfile, "AFTER FLAGS OPT:\n");
|
fprintf(logfile, "AFTER FLAGS OPT:\n");
|
||||||
dump_ops(gen_opc_buf);
|
dump_ops(gen_opc_buf, gen_opparam_buf);
|
||||||
fprintf(logfile, "\n");
|
fprintf(logfile, "\n");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -3645,8 +3713,8 @@ CPUX86State *cpu_x86_init(void)
|
|||||||
for(i = 0;i < 8; i++)
|
for(i = 0;i < 8; i++)
|
||||||
env->fptags[i] = 1;
|
env->fptags[i] = 1;
|
||||||
env->fpuc = 0x37f;
|
env->fpuc = 0x37f;
|
||||||
/* flags setup */
|
/* flags setup : we activate the IRQs by default as in user mode */
|
||||||
env->eflags = 0;
|
env->eflags = 0x2 | IF_MASK;
|
||||||
|
|
||||||
/* init various static tables */
|
/* init various static tables */
|
||||||
if (!inited) {
|
if (!inited) {
|
||||||
|
Reference in New Issue
Block a user