vm86.c 15.2 KB
Newer Older
1 2
/*
 *  vm86 linux syscall support
3
 *
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
 *  Copyright (c) 2003 Fabrice Bellard
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#include "qemu.h"

//#define DEBUG_VM86

#define set_flags(X,new,mask) \
((X) = ((X) & ~(mask)) | ((new) & (mask)))

#define SAFE_MASK	(0xDD5)
#define RETURN_MASK	(0xDFF)

static inline int is_revectored(int nr, struct target_revectored_struct *bitmap)
{
39
    return (((uint8_t *)bitmap)[nr >> 3] >> (nr & 7)) & 1;
40 41 42 43
}

static inline void vm_putw(uint8_t *segptr, unsigned int reg16, unsigned int val)
{
B
bellard 已提交
44
    stw(segptr + (reg16 & 0xffff), val);
45 46 47 48
}

static inline void vm_putl(uint8_t *segptr, unsigned int reg16, unsigned int val)
{
B
bellard 已提交
49
    stl(segptr + (reg16 & 0xffff), val);
50 51 52 53
}

static inline unsigned int vm_getw(uint8_t *segptr, unsigned int reg16)
{
B
bellard 已提交
54
    return lduw(segptr + (reg16 & 0xffff));
55 56 57 58
}

static inline unsigned int vm_getl(uint8_t *segptr, unsigned int reg16)
{
B
bellard 已提交
59
    return ldl(segptr + (reg16 & 0xffff));
60 61 62 63 64
}

void save_v86_state(CPUX86State *env)
{
    TaskState *ts = env->opaque;
65
    struct target_vm86plus_struct * target_v86;
66

67
    lock_user_struct(target_v86, ts->target_v86, 0);
68
    /* put the VM86 registers in the userspace register structure */
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
    target_v86->regs.eax = tswap32(env->regs[R_EAX]);
    target_v86->regs.ebx = tswap32(env->regs[R_EBX]);
    target_v86->regs.ecx = tswap32(env->regs[R_ECX]);
    target_v86->regs.edx = tswap32(env->regs[R_EDX]);
    target_v86->regs.esi = tswap32(env->regs[R_ESI]);
    target_v86->regs.edi = tswap32(env->regs[R_EDI]);
    target_v86->regs.ebp = tswap32(env->regs[R_EBP]);
    target_v86->regs.esp = tswap32(env->regs[R_ESP]);
    target_v86->regs.eip = tswap32(env->eip);
    target_v86->regs.cs = tswap16(env->segs[R_CS].selector);
    target_v86->regs.ss = tswap16(env->segs[R_SS].selector);
    target_v86->regs.ds = tswap16(env->segs[R_DS].selector);
    target_v86->regs.es = tswap16(env->segs[R_ES].selector);
    target_v86->regs.fs = tswap16(env->segs[R_FS].selector);
    target_v86->regs.gs = tswap16(env->segs[R_GS].selector);
84
    set_flags(env->eflags, ts->v86flags, VIF_MASK | ts->v86mask);
85 86
    target_v86->regs.eflags = tswap32(env->eflags);
    unlock_user_struct(target_v86, ts->target_v86, 1);
87
#ifdef DEBUG_VM86
88
    fprintf(logfile, "save_v86_state: eflags=%08x cs:ip=%04x:%04x\n",
B
bellard 已提交
89
            env->eflags, env->segs[R_CS].selector, env->eip);
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
#endif

    /* 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
    fprintf(logfile, "return_to_32bit: ret=0x%x\n", retval);
#endif
    save_v86_state(env);
    env->regs[R_EAX] = retval;
}

static inline int set_IF(CPUX86State *env)
{
    TaskState *ts = env->opaque;
126

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
    ts->v86flags |= VIF_MASK;
    if (ts->v86flags & VIP_MASK) {
        return_to_32bit(env, TARGET_VM86_STI);
        return 1;
    }
    return 0;
}

static inline void clear_IF(CPUX86State *env)
{
    TaskState *ts = env->opaque;

    ts->v86flags &= ~VIF_MASK;
}

static inline void clear_TF(CPUX86State *env)
{
    env->eflags &= ~TF_MASK;
}

B
bellard 已提交
147 148 149 150 151
static inline void clear_AC(CPUX86State *env)
{
    env->eflags &= ~AC_MASK;
}

152 153 154 155 156 157 158 159
static inline int set_vflags_long(unsigned long eflags, CPUX86State *env)
{
    TaskState *ts = env->opaque;

    set_flags(ts->v86flags, eflags, ts->v86mask);
    set_flags(env->eflags, eflags, SAFE_MASK);
    if (eflags & IF_MASK)
        return set_IF(env);
B
bellard 已提交
160 161
    else
        clear_IF(env);
162 163 164 165 166 167 168 169 170 171 172
    return 0;
}

static inline int set_vflags_short(unsigned short flags, CPUX86State *env)
{
    TaskState *ts = env->opaque;

    set_flags(ts->v86flags, flags, ts->v86mask & 0xffff);
    set_flags(env->eflags, flags, SAFE_MASK);
    if (flags & IF_MASK)
        return set_IF(env);
B
bellard 已提交
173 174
    else
        clear_IF(env);
175 176 177 178 179 180 181 182 183 184 185
    return 0;
}

static inline unsigned int get_vflags(CPUX86State *env)
{
    TaskState *ts = env->opaque;
    unsigned int flags;

    flags = env->eflags & RETURN_MASK;
    if (ts->v86flags & VIF_MASK)
        flags |= IF_MASK;
B
bellard 已提交
186
    flags |= IOPL_MASK;
187 188 189 190 191 192 193
    return flags | (ts->v86flags & ts->v86mask);
}

#define ADD16(reg, val) reg = (reg & ~0xffff) | ((reg + (val)) & 0xffff)

/* handle VM86 interrupt (NOTE: the CPU core currently does not
   support TSS interrupt revectoring, so this code is always executed) */
B
bellard 已提交
194
static void do_int(CPUX86State *env, int intno)
195 196 197 198 199 200
{
    TaskState *ts = env->opaque;
    uint32_t *int_ptr, segoffs;
    uint8_t *ssp;
    unsigned int sp;

B
bellard 已提交
201
    if (env->segs[R_CS].selector == TARGET_BIOSSEG)
202
        goto cannot_handle;
203
    if (is_revectored(intno, &ts->vm86plus.int_revectored))
204
        goto cannot_handle;
205
    if (intno == 0x21 && is_revectored((env->regs[R_EAX] >> 8) & 0xff,
206
                                       &ts->vm86plus.int21_revectored))
207 208 209 210 211 212
        goto cannot_handle;
    int_ptr = (uint32_t *)(intno << 2);
    segoffs = tswap32(*int_ptr);
    if ((segoffs >> 16) == TARGET_BIOSSEG)
        goto cannot_handle;
#if defined(DEBUG_VM86)
213
    fprintf(logfile, "VM86: emulating int 0x%x. CS:IP=%04x:%04x\n",
214 215 216
            intno, segoffs >> 16, segoffs & 0xffff);
#endif
    /* save old state */
B
bellard 已提交
217
    ssp = (uint8_t *)(env->segs[R_SS].selector << 4);
218 219
    sp = env->regs[R_ESP] & 0xffff;
    vm_putw(ssp, sp - 2, get_vflags(env));
B
bellard 已提交
220
    vm_putw(ssp, sp - 4, env->segs[R_CS].selector);
221 222 223 224 225 226 227
    vm_putw(ssp, sp - 6, env->eip);
    ADD16(env->regs[R_ESP], -6);
    /* goto interrupt handler */
    env->eip = segoffs & 0xffff;
    cpu_x86_load_seg(env, R_CS, segoffs >> 16);
    clear_TF(env);
    clear_IF(env);
B
bellard 已提交
228
    clear_AC(env);
229 230 231 232 233 234 235 236
    return;
 cannot_handle:
#if defined(DEBUG_VM86)
    fprintf(logfile, "VM86: return to 32 bits int 0x%x\n", intno);
#endif
    return_to_32bit(env, TARGET_VM86_INTx | (intno << 8));
}

B
bellard 已提交
237 238 239 240 241 242 243 244 245
void handle_vm86_trap(CPUX86State *env, int trapno)
{
    if (trapno == 1 || trapno == 3) {
        return_to_32bit(env, TARGET_VM86_TRAP + (trapno << 8));
    } else {
        do_int(env, trapno);
    }
}

246 247 248 249
#define CHECK_IF_IN_TRAP() \
      if ((ts->vm86plus.vm86plus.flags & TARGET_vm86dbg_active) && \
          (ts->vm86plus.vm86plus.flags & TARGET_vm86dbg_TFpendig)) \
		newflags |= TF_MASK
250 251

#define VM86_FAULT_RETURN \
252
        if ((ts->vm86plus.vm86plus.flags & TARGET_force_return_for_pic) && \
253 254 255 256 257 258 259 260
            (ts->v86flags & (IF_MASK | VIF_MASK))) \
            return_to_32bit(env, TARGET_VM86_PICRETURN); \
        return

void handle_vm86_fault(CPUX86State *env)
{
    TaskState *ts = env->opaque;
    uint8_t *csp, *pc, *ssp;
261 262
    unsigned int ip, sp, newflags, newip, newcs, opcode, intno;
    int data32, pref_done;
263

B
bellard 已提交
264
    csp = (uint8_t *)(env->segs[R_CS].selector << 4);
265 266
    ip = env->eip & 0xffff;
    pc = csp + ip;
267

B
bellard 已提交
268
    ssp = (uint8_t *)(env->segs[R_SS].selector << 4);
269 270 271 272
    sp = env->regs[R_ESP] & 0xffff;

#if defined(DEBUG_VM86)
    fprintf(logfile, "VM86 exception %04x:%08x %02x %02x\n",
B
bellard 已提交
273
            env->segs[R_CS].selector, env->eip, pc[0], pc[1]);
274 275
#endif

276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
    data32 = 0;
    pref_done = 0;
    do {
        opcode = csp[ip];
        ADD16(ip, 1);
        switch (opcode) {
        case 0x66:      /* 32-bit data */     data32=1; break;
        case 0x67:      /* 32-bit address */  break;
        case 0x2e:      /* CS */              break;
        case 0x3e:      /* DS */              break;
        case 0x26:      /* ES */              break;
        case 0x36:      /* SS */              break;
        case 0x65:      /* GS */              break;
        case 0x64:      /* FS */              break;
        case 0xf2:      /* repnz */	      break;
        case 0xf3:      /* rep */             break;
        default: pref_done = 1;
        }
    } while (!pref_done);

296
    /* VM86 mode */
297 298 299
    switch(opcode) {
    case 0x9c: /* pushf */
        if (data32) {
300
            vm_putl(ssp, sp - 4, get_vflags(env));
301 302 303 304 305 306 307
            ADD16(env->regs[R_ESP], -4);
        } else {
            vm_putw(ssp, sp - 2, get_vflags(env));
            ADD16(env->regs[R_ESP], -2);
        }
        env->eip = ip;
        VM86_FAULT_RETURN;
308

309 310 311
    case 0x9d: /* popf */
        if (data32) {
            newflags = vm_getl(ssp, sp);
312
            ADD16(env->regs[R_ESP], 4);
313 314 315 316 317 318 319 320
        } else {
            newflags = vm_getw(ssp, sp);
            ADD16(env->regs[R_ESP], 2);
        }
        env->eip = ip;
        CHECK_IF_IN_TRAP();
        if (data32) {
            if (set_vflags_long(newflags, env))
321
                return;
322 323
        } else {
            if (set_vflags_short(newflags, env))
324 325 326 327 328
                return;
        }
        VM86_FAULT_RETURN;

    case 0xcd: /* int */
329 330 331 332
        intno = csp[ip];
        ADD16(ip, 1);
        env->eip = ip;
        if (ts->vm86plus.vm86plus.flags & TARGET_vm86dbg_active) {
333
            if ( (ts->vm86plus.vm86plus.vm86dbg_intxxtab[intno >> 3] >>
334 335 336 337 338 339
                  (intno &7)) & 1) {
                return_to_32bit(env, TARGET_VM86_INTx + (intno << 8));
                return;
            }
        }
        do_int(env, intno);
340 341 342
        break;

    case 0xcf: /* iret */
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
        if (data32) {
            newip = vm_getl(ssp, sp) & 0xffff;
            newcs = vm_getl(ssp, sp + 4) & 0xffff;
            newflags = vm_getl(ssp, sp + 8);
            ADD16(env->regs[R_ESP], 12);
        } else {
            newip = vm_getw(ssp, sp);
            newcs = vm_getw(ssp, sp + 2);
            newflags = vm_getw(ssp, sp + 4);
            ADD16(env->regs[R_ESP], 6);
        }
        env->eip = newip;
        cpu_x86_load_seg(env, R_CS, newcs);
        CHECK_IF_IN_TRAP();
        if (data32) {
            if (set_vflags_long(newflags, env))
                return;
        } else {
            if (set_vflags_short(newflags, env))
                return;
        }
364
        VM86_FAULT_RETURN;
365

366
    case 0xfa: /* cli */
367
        env->eip = ip;
368 369
        clear_IF(env);
        VM86_FAULT_RETURN;
370

371
    case 0xfb: /* sti */
372
        env->eip = ip;
373 374 375 376 377 378 379 380 381 382 383
        if (set_IF(env))
            return;
        VM86_FAULT_RETURN;

    default:
        /* real VM86 GPF exception */
        return_to_32bit(env, TARGET_VM86_UNKNOWN);
        break;
    }
}

384
int do_vm86(CPUX86State *env, long subfunction, abi_ulong vm86_addr)
385 386
{
    TaskState *ts = env->opaque;
387
    struct target_vm86plus_struct * target_v86;
388
    int ret;
389

390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
    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;
    }

    /* 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;
B
bellard 已提交
419 420 421 422 423 424
    ts->vm86_saved_regs.cs = env->segs[R_CS].selector;
    ts->vm86_saved_regs.ss = env->segs[R_SS].selector;
    ts->vm86_saved_regs.ds = env->segs[R_DS].selector;
    ts->vm86_saved_regs.es = env->segs[R_ES].selector;
    ts->vm86_saved_regs.fs = env->segs[R_FS].selector;
    ts->vm86_saved_regs.gs = env->segs[R_GS].selector;
425

426 427
    ts->target_v86 = vm86_addr;
    lock_user_struct(target_v86, vm86_addr, 1);
428 429
    /* build vm86 CPU state */
    ts->v86flags = tswap32(target_v86->regs.eflags);
430
    env->eflags = (env->eflags & ~SAFE_MASK) |
431
        (tswap32(target_v86->regs.eflags) & SAFE_MASK) | VM_MASK;
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447

    ts->vm86plus.cpu_type = tswapl(target_v86->cpu_type);
    switch (ts->vm86plus.cpu_type) {
    case TARGET_CPU_286:
        ts->v86mask = 0;
        break;
    case TARGET_CPU_386:
        ts->v86mask = NT_MASK | IOPL_MASK;
        break;
    case TARGET_CPU_486:
        ts->v86mask = AC_MASK | NT_MASK | IOPL_MASK;
        break;
    default:
        ts->v86mask = ID_MASK | AC_MASK | NT_MASK | IOPL_MASK;
        break;
    }
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464

    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 */
465
    memcpy(&ts->vm86plus.int_revectored,
466
           &target_v86->int_revectored, 32);
467
    memcpy(&ts->vm86plus.int21_revectored,
468 469
           &target_v86->int21_revectored, 32);
    ts->vm86plus.vm86plus.flags = tswapl(target_v86->vm86plus.flags);
470
    memcpy(&ts->vm86plus.vm86plus.vm86dbg_intxxtab,
471
           target_v86->vm86plus.vm86dbg_intxxtab, 32);
472
    unlock_user_struct(target_v86, vm86_addr, 0);
473

474
#ifdef DEBUG_VM86
475
    fprintf(logfile, "do_vm86: cs:ip=%04x:%04x\n",
B
bellard 已提交
476
            env->segs[R_CS].selector, env->eip);
477 478 479 480 481 482
#endif
    /* now the virtual CPU is ready for vm86 execution ! */
 out:
    return ret;
}