Commit bc8a22cc authored by Fabrice Bellard's avatar Fabrice Bellard
Browse files

better vm86 support


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@69 c046a42c-6fe2-441c-8c8c-71466251a162
parent f631ef9b
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
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:

+3 −2
Original line number Diff line number Diff line
- 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)
+18 −18
Original line number Diff line number Diff line
@@ -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 */

+167 −64
Original line number Diff line number Diff line
@@ -106,41 +106,39 @@ uint64_t gdt_table[6];

//#define DEBUG_VM86

void cpu_loop(struct CPUX86State *env)
static inline int is_revectored(int nr, struct target_revectored_struct *bitmap)
{
    int err;
    uint8_t *pc;
    target_siginfo_t info;
    return (tswap32(bitmap->__map[nr >> 5]) >> (nr & 0x1f)) & 1;
}

    for(;;) {
        err = cpu_x86_exec(env);
        pc = env->seg_cache[R_CS].base + env->eip;
        switch(err) {
        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;
static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg)
{
    return (uint8_t *)((seg << 4) + (reg & 0xffff));
}

                /* 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;
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("ret=0x%x\n", ret);
    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]);
@@ -157,8 +155,10 @@ void cpu_loop(struct CPUX86State *env)
    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;
@@ -175,8 +175,103 @@ void cpu_loop(struct CPUX86State *env)
    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 trapnr;
    uint8_t *pc;
    target_siginfo_t info;

                env->regs[R_EAX] = ret;
    for(;;) {
        trapnr = cpu_x86_exec(env);
        pc = env->seg_cache[R_CS].base + env->eip;
        switch(trapnr) {
        case EXCP0D_GPF:
            if (env->eflags & VM_MASK) {
#ifdef DEBUG_VM86
                printf("VM86 exception %04x:%08x %02x %02x\n",
                       env->segs[R_CS], env->eip, pc[0], pc[1]);
#endif
                /* VM86 mode */
                switch(pc[0]) {
                case 0xcd: /* int */
                    env->eip += 2;
                    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 */
                    return_to_32bit(env, TARGET_VM86_UNKNOWN);
                    break;
                }
            } else {
                if (pc[0] == 0xcd && pc[1] == 0x80) {
                    /* syscall */
@@ -200,20 +295,28 @@ void cpu_loop(struct CPUX86State *env)
            }
            break;
        case EXCP00_DIVZ:
            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:
            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);
+1 −0
Original line number Diff line number Diff line
@@ -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
Loading