From bc8a22cc307ebd9a2577c8fffcb90000724f72f3 Mon Sep 17 00:00:00 2001 From: bellard Date: Sun, 30 Mar 2003 21:02:40 +0000 Subject: [PATCH] better vm86 support git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@69 c046a42c-6fe2-441c-8c8c-71466251a162 --- Changelog | 33 ++++++++ TODO | 5 +- cpu-i386.h | 36 ++++---- linux-user/main.c | 231 +++++++++++++++++++++++++++++++++++++--------------- linux-user/qemu.h | 1 + linux-user/signal.c | 17 ++-- syscall-i386.h | 5 ++ 7 files changed, 239 insertions(+), 89 deletions(-) diff --git a/Changelog b/Changelog index 574ae5e..2a76e09 100644 --- a/Changelog +++ b/Changelog @@ -1,8 +1,41 @@ +version 0.1.4: + + - more accurate VM86 emulation (can launch small DOS 16 bit + executables in wine). + - fixed push/pop fs/gs + - added iret instruction. + +version 0.1.3: + + - S390 support (Ulrich Weigand) + - glibc 2.3.x compile fix (Ulrich Weigand) + - socketcall endian fix (Ulrich Weigand) + - struct sockaddr endian fix (Ulrich Weigand) + - sendmsg/recvmsg endian fix (Ulrich Weigand) + - execve endian fix (Ulrich Weigand) + - fdset endian fix (Ulrich Weigand) + - partial setsockopt syscall support (Ulrich Weigand) + - more accurate pushf/popf emulation + - first partial vm86() syscall support (can be used with runcom example). + - added bound, cmpxchg8b, cpuid instructions + - added 16 bit addressing support/override for string operations + - poll() fix + +version 0.1.2: + + - compile fixes + - xlat instruction + - xchg instruction memory lock + - added simple vm86 example (not working with QEMU yet). The 54 byte + DOS executable 'pi_10.com' program was released by Bertram + Felgenhauer (more information at http://www.boo.net/~jasonp/pipage.html). + version 0.1.1: - glibc 2.2 compilation fixes - added -s and -L options - binary distribution of x86 glibc and wine + - big endian fixes in ELF loader and getdents. version 0.1: diff --git a/TODO b/TODO index 6616d62..9f26651 100644 --- a/TODO +++ b/TODO @@ -1,10 +1,11 @@ +- fix thread locks +- fix thread stack liberation +- fix x86 stack allocation - optimize translated cache chaining (DLL PLT-like system) - more syscalls (in particular all 64 bit ones, IPCs, fix 64 bit issues, fix 16 bit uid issues) - finish signal handing (fp87 state, more siginfo conversions) - verify thread support (clone() and various locks) -- vm86 syscall support - overrides/16bit for string ops - make it self runnable (use same trick as ld.so : include its own relocator and libc) -- improved 16 bit support - fix FPU exceptions (in particular: gen_op_fpush not before mem load) diff --git a/cpu-i386.h b/cpu-i386.h index 4eeb6be..76db7a6 100644 --- a/cpu-i386.h +++ b/cpu-i386.h @@ -68,24 +68,24 @@ #define VIP_MASK 0x00100000 #define ID_MASK 0x00200000 -#define EXCP00_DIVZ 1 -#define EXCP01_SSTP 2 -#define EXCP02_NMI 3 -#define EXCP03_INT3 4 -#define EXCP04_INTO 5 -#define EXCP05_BOUND 6 -#define EXCP06_ILLOP 7 -#define EXCP07_PREX 8 -#define EXCP08_DBLE 9 -#define EXCP09_XERR 10 -#define EXCP0A_TSS 11 -#define EXCP0B_NOSEG 12 -#define EXCP0C_STACK 13 -#define EXCP0D_GPF 14 -#define EXCP0E_PAGE 15 -#define EXCP10_COPR 17 -#define EXCP11_ALGN 18 -#define EXCP12_MCHK 19 +#define EXCP00_DIVZ 0 +#define EXCP01_SSTP 1 +#define EXCP02_NMI 2 +#define EXCP03_INT3 3 +#define EXCP04_INTO 4 +#define EXCP05_BOUND 5 +#define EXCP06_ILLOP 6 +#define EXCP07_PREX 7 +#define EXCP08_DBLE 8 +#define EXCP09_XERR 9 +#define EXCP0A_TSS 10 +#define EXCP0B_NOSEG 11 +#define EXCP0C_STACK 12 +#define EXCP0D_GPF 13 +#define EXCP0E_PAGE 14 +#define EXCP10_COPR 16 +#define EXCP11_ALGN 17 +#define EXCP12_MCHK 18 #define EXCP_INTERRUPT 256 /* async interruption */ diff --git a/linux-user/main.c b/linux-user/main.c index c12ef0f..5acfdde 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -106,77 +106,172 @@ uint64_t gdt_table[6]; //#define DEBUG_VM86 +static inline int is_revectored(int nr, struct target_revectored_struct *bitmap) +{ + return (tswap32(bitmap->__map[nr >> 5]) >> (nr & 0x1f)) & 1; +} + +static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg) +{ + return (uint8_t *)((seg << 4) + (reg & 0xffff)); +} + +static inline void pushw(CPUX86State *env, int val) +{ + env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) | + ((env->regs[R_ESP] - 2) & 0xffff); + *(uint16_t *)seg_to_linear(env->segs[R_SS], env->regs[R_ESP]) = val; +} + +static inline unsigned int get_vflags(CPUX86State *env) +{ + unsigned int eflags; + eflags = env->eflags & ~(VM_MASK | RF_MASK | IF_MASK); + if (eflags & VIF_MASK) + eflags |= IF_MASK; + return eflags; +} + +void save_v86_state(CPUX86State *env) +{ + TaskState *ts = env->opaque; +#ifdef DEBUG_VM86 + printf("save_v86_state\n"); +#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]); + ts->target_v86->regs.eflags = tswap32(env->eflags); + + /* restore 32 bit registers */ + env->regs[R_EAX] = ts->vm86_saved_regs.eax; + 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); +} + +/* return from vm86 mode to 32 bit. The vm86() syscall will return + 'retval' */ +static inline void return_to_32bit(CPUX86State *env, int retval) +{ +#ifdef DEBUG_VM86 + printf("return_to_32bit: ret=0x%x\n", retval); +#endif + save_v86_state(env); + env->regs[R_EAX] = retval; +} + +/* handle VM86 interrupt (NOTE: the CPU core currently does not + support TSS interrupt revectoring, so this code is always executed) */ +static void do_int(CPUX86State *env, int intno) +{ + TaskState *ts = env->opaque; + uint32_t *int_ptr, segoffs; + + if (env->segs[R_CS] == TARGET_BIOSSEG) + goto cannot_handle; /* XXX: I am not sure this is really useful */ + if (is_revectored(intno, &ts->target_v86->int_revectored)) + goto cannot_handle; + if (intno == 0x21 && is_revectored((env->regs[R_EAX] >> 8) & 0xff, + &ts->target_v86->int21_revectored)) + goto cannot_handle; + int_ptr = (uint32_t *)(intno << 2); + segoffs = tswap32(*int_ptr); + if ((segoffs >> 16) == TARGET_BIOSSEG) + goto cannot_handle; +#ifdef DEBUG_VM86 + printf("VM86: emulating int 0x%x. CS:IP=%04x:%04x\n", + intno, segoffs >> 16, segoffs & 0xffff); +#endif + /* save old state */ + pushw(env, get_vflags(env)); + pushw(env, env->segs[R_CS]); + pushw(env, env->eip); + /* goto interrupt handler */ + env->eip = segoffs & 0xffff; + cpu_x86_load_seg(env, R_CS, segoffs >> 16); + env->eflags &= ~(VIF_MASK | TF_MASK); + return; + cannot_handle: +#ifdef DEBUG_VM86 + printf("VM86: return to 32 bits int 0x%x\n", intno); +#endif + return_to_32bit(env, TARGET_VM86_INTx | (intno << 8)); +} + void cpu_loop(struct CPUX86State *env) { - int err; + int trapnr; uint8_t *pc; target_siginfo_t info; for(;;) { - err = cpu_x86_exec(env); + trapnr = cpu_x86_exec(env); pc = env->seg_cache[R_CS].base + env->eip; - switch(err) { + switch(trapnr) { 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]); + printf("VM86 exception %04x:%08x %02x %02x\n", + env->segs[R_CS], env->eip, pc[0], pc[1]); #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); + do_int(env, pc[1]); + break; + case 0x66: + switch(pc[1]) { + case 0xfb: /* sti */ + case 0x9d: /* popf */ + case 0xcf: /* iret */ + env->eip += 2; + return_to_32bit(env, TARGET_VM86_STI); + break; + default: + goto vm86_gpf; + } + break; + case 0xfb: /* sti */ + case 0x9d: /* popf */ + case 0xcf: /* iret */ + env->eip++; + return_to_32bit(env, TARGET_VM86_STI); break; default: + vm86_gpf: /* real VM86 GPF exception */ - ret = TARGET_VM86_UNKNOWN; + return_to_32bit(env, 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) { /* syscall */ @@ -200,20 +295,28 @@ void cpu_loop(struct CPUX86State *env) } break; case EXCP00_DIVZ: - /* division by zero */ - info.si_signo = SIGFPE; - info.si_errno = 0; - info.si_code = TARGET_FPE_INTDIV; - info._sifields._sigfault._addr = env->eip; - queue_signal(info.si_signo, &info); + if (env->eflags & VM_MASK) { + do_int(env, trapnr); + } else { + /* division by zero */ + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_code = TARGET_FPE_INTDIV; + info._sifields._sigfault._addr = env->eip; + queue_signal(info.si_signo, &info); + } break; case EXCP04_INTO: case EXCP05_BOUND: - info.si_signo = SIGSEGV; - info.si_errno = 0; - info.si_code = 0; - info._sifields._sigfault._addr = 0; - queue_signal(info.si_signo, &info); + if (env->eflags & VM_MASK) { + do_int(env, trapnr); + } else { + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_code = 0; + info._sifields._sigfault._addr = 0; + queue_signal(info.si_signo, &info); + } break; case EXCP06_ILLOP: info.si_signo = SIGILL; @@ -226,8 +329,8 @@ void cpu_loop(struct CPUX86State *env) /* just indicate that signals should be handled asap */ break; default: - fprintf(stderr, "0x%08lx: Unknown exception CPU %d, aborting\n", - (long)pc, err); + fprintf(stderr, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n", + (long)pc, trapnr); abort(); } process_pending_signals(env); diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 54b38e7..882c3c0 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -74,5 +74,6 @@ void cpu_loop(CPUX86State *env); void process_pending_signals(void *cpu_env); void signal_init(void); int queue_signal(int sig, target_siginfo_t *info); +void save_v86_state(CPUX86State *env); #endif diff --git a/linux-user/signal.c b/linux-user/signal.c index 04779c8..6a81b11 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -198,7 +198,7 @@ void __attribute((noreturn)) force_sig(int sig) { int host_sig; host_sig = target_to_host_signal(sig); - fprintf(stderr, "gemu: uncaught target signal %d (%s) - exiting\n", + fprintf(stderr, "qemu: uncaught target signal %d (%s) - exiting\n", sig, strsignal(host_sig)); #if 1 _exit(-host_sig); @@ -223,7 +223,7 @@ int queue_signal(int sig, target_siginfo_t *info) target_ulong handler; #if defined(DEBUG_SIGNAL) - fprintf(stderr, "queue_sigal: sig=%d\n", + fprintf(stderr, "queue_signal: sig=%d\n", sig); #endif k = &sigact_table[sig - 1]; @@ -317,7 +317,7 @@ static void host_signal_handler(int host_signum, siginfo_t *info, if (sig < 1 || sig > TARGET_NSIG) return; #if defined(DEBUG_SIGNAL) - fprintf(stderr, "gemu: got signal %d\n", sig); + fprintf(stderr, "qemu: got signal %d\n", sig); dump_regs(puc); #endif host_to_target_siginfo_noswap(&tinfo, info); @@ -538,7 +538,6 @@ setup_sigcontext(struct target_sigcontext *sc, struct target_fpstate *fpstate, /* non-iBCS2 extensions.. */ err |= __put_user(mask, &sc->oldmask); err |= __put_user(/*current->thread.cr2*/ 0, &sc->cr2); - return err; } @@ -859,7 +858,7 @@ void process_pending_signals(void *cpu_env) handle_signal: #ifdef DEBUG_SIGNAL - fprintf(stderr, "gemu: process signal %d\n", sig); + fprintf(stderr, "qemu: process signal %d\n", sig); #endif /* dequeue signal */ q = k->first; @@ -893,6 +892,14 @@ void process_pending_signals(void *cpu_env) end of the signal execution (see do_sigreturn) */ host_to_target_sigset(&target_old_set, &old_set); + /* if the CPU is in VM86 mode, we restore the 32 bit values */ +#ifdef TARGET_I386 + { + CPUX86State *env = cpu_env; + if (env->eflags & VM_MASK) + save_v86_state(env); + } +#endif /* prepare the stack frame of the virtual CPU */ if (k->sa.sa_flags & TARGET_SA_SIGINFO) setup_rt_frame(sig, k, &q->info, &target_old_set, cpu_env); diff --git a/syscall-i386.h b/syscall-i386.h index 847404f..0fb57ce 100644 --- a/syscall-i386.h +++ b/syscall-i386.h @@ -755,6 +755,11 @@ struct target_modify_ldt_ldt_s { unsigned int flags; }; + +/* vm86 defines */ + +#define TARGET_BIOSSEG 0x0f000 + #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) */ -- 2.7.4