Commit bc8a22cc307ebd9a2577c8fffcb90000724f72f3

Authored by bellard
1 parent f631ef9b

better vm86 support


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@69 c046a42c-6fe2-441c-8c8c-71466251a162
Changelog
  1 +version 0.1.4:
  2 +
  3 + - more accurate VM86 emulation (can launch small DOS 16 bit
  4 + executables in wine).
  5 + - fixed push/pop fs/gs
  6 + - added iret instruction.
  7 +
  8 +version 0.1.3:
  9 +
  10 + - S390 support (Ulrich Weigand)
  11 + - glibc 2.3.x compile fix (Ulrich Weigand)
  12 + - socketcall endian fix (Ulrich Weigand)
  13 + - struct sockaddr endian fix (Ulrich Weigand)
  14 + - sendmsg/recvmsg endian fix (Ulrich Weigand)
  15 + - execve endian fix (Ulrich Weigand)
  16 + - fdset endian fix (Ulrich Weigand)
  17 + - partial setsockopt syscall support (Ulrich Weigand)
  18 + - more accurate pushf/popf emulation
  19 + - first partial vm86() syscall support (can be used with runcom example).
  20 + - added bound, cmpxchg8b, cpuid instructions
  21 + - added 16 bit addressing support/override for string operations
  22 + - poll() fix
  23 +
  24 +version 0.1.2:
  25 +
  26 + - compile fixes
  27 + - xlat instruction
  28 + - xchg instruction memory lock
  29 + - added simple vm86 example (not working with QEMU yet). The 54 byte
  30 + DOS executable 'pi_10.com' program was released by Bertram
  31 + Felgenhauer (more information at http://www.boo.net/~jasonp/pipage.html).
  32 +
1 33 version 0.1.1:
2 34  
3 35 - glibc 2.2 compilation fixes
4 36 - added -s and -L options
5 37 - binary distribution of x86 glibc and wine
  38 + - big endian fixes in ELF loader and getdents.
6 39  
7 40 version 0.1:
8 41  
... ...
  1 +- fix thread locks
  2 +- fix thread stack liberation
  3 +- fix x86 stack allocation
1 4 - optimize translated cache chaining (DLL PLT-like system)
2 5 - more syscalls (in particular all 64 bit ones, IPCs, fix 64 bit
3 6 issues, fix 16 bit uid issues)
4 7 - finish signal handing (fp87 state, more siginfo conversions)
5 8 - verify thread support (clone() and various locks)
6   -- vm86 syscall support
7 9 - overrides/16bit for string ops
8 10 - make it self runnable (use same trick as ld.so : include its own relocator and libc)
9   -- improved 16 bit support
10 11 - fix FPU exceptions (in particular: gen_op_fpush not before mem load)
... ...
cpu-i386.h
... ... @@ -68,24 +68,24 @@
68 68 #define VIP_MASK 0x00100000
69 69 #define ID_MASK 0x00200000
70 70  
71   -#define EXCP00_DIVZ 1
72   -#define EXCP01_SSTP 2
73   -#define EXCP02_NMI 3
74   -#define EXCP03_INT3 4
75   -#define EXCP04_INTO 5
76   -#define EXCP05_BOUND 6
77   -#define EXCP06_ILLOP 7
78   -#define EXCP07_PREX 8
79   -#define EXCP08_DBLE 9
80   -#define EXCP09_XERR 10
81   -#define EXCP0A_TSS 11
82   -#define EXCP0B_NOSEG 12
83   -#define EXCP0C_STACK 13
84   -#define EXCP0D_GPF 14
85   -#define EXCP0E_PAGE 15
86   -#define EXCP10_COPR 17
87   -#define EXCP11_ALGN 18
88   -#define EXCP12_MCHK 19
  71 +#define EXCP00_DIVZ 0
  72 +#define EXCP01_SSTP 1
  73 +#define EXCP02_NMI 2
  74 +#define EXCP03_INT3 3
  75 +#define EXCP04_INTO 4
  76 +#define EXCP05_BOUND 5
  77 +#define EXCP06_ILLOP 6
  78 +#define EXCP07_PREX 7
  79 +#define EXCP08_DBLE 8
  80 +#define EXCP09_XERR 9
  81 +#define EXCP0A_TSS 10
  82 +#define EXCP0B_NOSEG 11
  83 +#define EXCP0C_STACK 12
  84 +#define EXCP0D_GPF 13
  85 +#define EXCP0E_PAGE 14
  86 +#define EXCP10_COPR 16
  87 +#define EXCP11_ALGN 17
  88 +#define EXCP12_MCHK 18
89 89  
90 90 #define EXCP_INTERRUPT 256 /* async interruption */
91 91  
... ...
linux-user/main.c
... ... @@ -106,77 +106,172 @@ uint64_t gdt_table[6];
106 106  
107 107 //#define DEBUG_VM86
108 108  
  109 +static inline int is_revectored(int nr, struct target_revectored_struct *bitmap)
  110 +{
  111 + return (tswap32(bitmap->__map[nr >> 5]) >> (nr & 0x1f)) & 1;
  112 +}
  113 +
  114 +static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg)
  115 +{
  116 + return (uint8_t *)((seg << 4) + (reg & 0xffff));
  117 +}
  118 +
  119 +static inline void pushw(CPUX86State *env, int val)
  120 +{
  121 + env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) |
  122 + ((env->regs[R_ESP] - 2) & 0xffff);
  123 + *(uint16_t *)seg_to_linear(env->segs[R_SS], env->regs[R_ESP]) = val;
  124 +}
  125 +
  126 +static inline unsigned int get_vflags(CPUX86State *env)
  127 +{
  128 + unsigned int eflags;
  129 + eflags = env->eflags & ~(VM_MASK | RF_MASK | IF_MASK);
  130 + if (eflags & VIF_MASK)
  131 + eflags |= IF_MASK;
  132 + return eflags;
  133 +}
  134 +
  135 +void save_v86_state(CPUX86State *env)
  136 +{
  137 + TaskState *ts = env->opaque;
  138 +#ifdef DEBUG_VM86
  139 + printf("save_v86_state\n");
  140 +#endif
  141 +
  142 + /* put the VM86 registers in the userspace register structure */
  143 + ts->target_v86->regs.eax = tswap32(env->regs[R_EAX]);
  144 + ts->target_v86->regs.ebx = tswap32(env->regs[R_EBX]);
  145 + ts->target_v86->regs.ecx = tswap32(env->regs[R_ECX]);
  146 + ts->target_v86->regs.edx = tswap32(env->regs[R_EDX]);
  147 + ts->target_v86->regs.esi = tswap32(env->regs[R_ESI]);
  148 + ts->target_v86->regs.edi = tswap32(env->regs[R_EDI]);
  149 + ts->target_v86->regs.ebp = tswap32(env->regs[R_EBP]);
  150 + ts->target_v86->regs.esp = tswap32(env->regs[R_ESP]);
  151 + ts->target_v86->regs.eip = tswap32(env->eip);
  152 + ts->target_v86->regs.cs = tswap16(env->segs[R_CS]);
  153 + ts->target_v86->regs.ss = tswap16(env->segs[R_SS]);
  154 + ts->target_v86->regs.ds = tswap16(env->segs[R_DS]);
  155 + ts->target_v86->regs.es = tswap16(env->segs[R_ES]);
  156 + ts->target_v86->regs.fs = tswap16(env->segs[R_FS]);
  157 + ts->target_v86->regs.gs = tswap16(env->segs[R_GS]);
  158 + ts->target_v86->regs.eflags = tswap32(env->eflags);
  159 +
  160 + /* restore 32 bit registers */
  161 + env->regs[R_EAX] = ts->vm86_saved_regs.eax;
  162 + env->regs[R_EBX] = ts->vm86_saved_regs.ebx;
  163 + env->regs[R_ECX] = ts->vm86_saved_regs.ecx;
  164 + env->regs[R_EDX] = ts->vm86_saved_regs.edx;
  165 + env->regs[R_ESI] = ts->vm86_saved_regs.esi;
  166 + env->regs[R_EDI] = ts->vm86_saved_regs.edi;
  167 + env->regs[R_EBP] = ts->vm86_saved_regs.ebp;
  168 + env->regs[R_ESP] = ts->vm86_saved_regs.esp;
  169 + env->eflags = ts->vm86_saved_regs.eflags;
  170 + env->eip = ts->vm86_saved_regs.eip;
  171 +
  172 + cpu_x86_load_seg(env, R_CS, ts->vm86_saved_regs.cs);
  173 + cpu_x86_load_seg(env, R_SS, ts->vm86_saved_regs.ss);
  174 + cpu_x86_load_seg(env, R_DS, ts->vm86_saved_regs.ds);
  175 + cpu_x86_load_seg(env, R_ES, ts->vm86_saved_regs.es);
  176 + cpu_x86_load_seg(env, R_FS, ts->vm86_saved_regs.fs);
  177 + cpu_x86_load_seg(env, R_GS, ts->vm86_saved_regs.gs);
  178 +}
  179 +
  180 +/* return from vm86 mode to 32 bit. The vm86() syscall will return
  181 + 'retval' */
  182 +static inline void return_to_32bit(CPUX86State *env, int retval)
  183 +{
  184 +#ifdef DEBUG_VM86
  185 + printf("return_to_32bit: ret=0x%x\n", retval);
  186 +#endif
  187 + save_v86_state(env);
  188 + env->regs[R_EAX] = retval;
  189 +}
  190 +
  191 +/* handle VM86 interrupt (NOTE: the CPU core currently does not
  192 + support TSS interrupt revectoring, so this code is always executed) */
  193 +static void do_int(CPUX86State *env, int intno)
  194 +{
  195 + TaskState *ts = env->opaque;
  196 + uint32_t *int_ptr, segoffs;
  197 +
  198 + if (env->segs[R_CS] == TARGET_BIOSSEG)
  199 + goto cannot_handle; /* XXX: I am not sure this is really useful */
  200 + if (is_revectored(intno, &ts->target_v86->int_revectored))
  201 + goto cannot_handle;
  202 + if (intno == 0x21 && is_revectored((env->regs[R_EAX] >> 8) & 0xff,
  203 + &ts->target_v86->int21_revectored))
  204 + goto cannot_handle;
  205 + int_ptr = (uint32_t *)(intno << 2);
  206 + segoffs = tswap32(*int_ptr);
  207 + if ((segoffs >> 16) == TARGET_BIOSSEG)
  208 + goto cannot_handle;
  209 +#ifdef DEBUG_VM86
  210 + printf("VM86: emulating int 0x%x. CS:IP=%04x:%04x\n",
  211 + intno, segoffs >> 16, segoffs & 0xffff);
  212 +#endif
  213 + /* save old state */
  214 + pushw(env, get_vflags(env));
  215 + pushw(env, env->segs[R_CS]);
  216 + pushw(env, env->eip);
  217 + /* goto interrupt handler */
  218 + env->eip = segoffs & 0xffff;
  219 + cpu_x86_load_seg(env, R_CS, segoffs >> 16);
  220 + env->eflags &= ~(VIF_MASK | TF_MASK);
  221 + return;
  222 + cannot_handle:
  223 +#ifdef DEBUG_VM86
  224 + printf("VM86: return to 32 bits int 0x%x\n", intno);
  225 +#endif
  226 + return_to_32bit(env, TARGET_VM86_INTx | (intno << 8));
  227 +}
  228 +
109 229 void cpu_loop(struct CPUX86State *env)
110 230 {
111   - int err;
  231 + int trapnr;
112 232 uint8_t *pc;
113 233 target_siginfo_t info;
114 234  
115 235 for(;;) {
116   - err = cpu_x86_exec(env);
  236 + trapnr = cpu_x86_exec(env);
117 237 pc = env->seg_cache[R_CS].base + env->eip;
118   - switch(err) {
  238 + switch(trapnr) {
119 239 case EXCP0D_GPF:
120 240 if (env->eflags & VM_MASK) {
121   - TaskState *ts;
122   - int ret;
123 241 #ifdef DEBUG_VM86
124   - printf("VM86 exception %04x:%08x %02x\n",
125   - env->segs[R_CS], env->eip, pc[0]);
  242 + printf("VM86 exception %04x:%08x %02x %02x\n",
  243 + env->segs[R_CS], env->eip, pc[0], pc[1]);
126 244 #endif
127 245 /* VM86 mode */
128   - ts = env->opaque;
129   -
130   - /* XXX: add all cases */
131 246 switch(pc[0]) {
132 247 case 0xcd: /* int */
133 248 env->eip += 2;
134   - ret = TARGET_VM86_INTx | (pc[1] << 8);
  249 + do_int(env, pc[1]);
  250 + break;
  251 + case 0x66:
  252 + switch(pc[1]) {
  253 + case 0xfb: /* sti */
  254 + case 0x9d: /* popf */
  255 + case 0xcf: /* iret */
  256 + env->eip += 2;
  257 + return_to_32bit(env, TARGET_VM86_STI);
  258 + break;
  259 + default:
  260 + goto vm86_gpf;
  261 + }
  262 + break;
  263 + case 0xfb: /* sti */
  264 + case 0x9d: /* popf */
  265 + case 0xcf: /* iret */
  266 + env->eip++;
  267 + return_to_32bit(env, TARGET_VM86_STI);
135 268 break;
136 269 default:
  270 + vm86_gpf:
137 271 /* real VM86 GPF exception */
138   - ret = TARGET_VM86_UNKNOWN;
  272 + return_to_32bit(env, TARGET_VM86_UNKNOWN);
139 273 break;
140 274 }
141   -#ifdef DEBUG_VM86
142   - printf("ret=0x%x\n", ret);
143   -#endif
144   - /* put the VM86 registers in the userspace register structure */
145   - ts->target_v86->regs.eax = tswap32(env->regs[R_EAX]);
146   - ts->target_v86->regs.ebx = tswap32(env->regs[R_EBX]);
147   - ts->target_v86->regs.ecx = tswap32(env->regs[R_ECX]);
148   - ts->target_v86->regs.edx = tswap32(env->regs[R_EDX]);
149   - ts->target_v86->regs.esi = tswap32(env->regs[R_ESI]);
150   - ts->target_v86->regs.edi = tswap32(env->regs[R_EDI]);
151   - ts->target_v86->regs.ebp = tswap32(env->regs[R_EBP]);
152   - ts->target_v86->regs.esp = tswap32(env->regs[R_ESP]);
153   - ts->target_v86->regs.eip = tswap32(env->eip);
154   - ts->target_v86->regs.cs = tswap16(env->segs[R_CS]);
155   - ts->target_v86->regs.ss = tswap16(env->segs[R_SS]);
156   - ts->target_v86->regs.ds = tswap16(env->segs[R_DS]);
157   - ts->target_v86->regs.es = tswap16(env->segs[R_ES]);
158   - ts->target_v86->regs.fs = tswap16(env->segs[R_FS]);
159   - ts->target_v86->regs.gs = tswap16(env->segs[R_GS]);
160   -
161   - /* restore 32 bit registers */
162   - env->regs[R_EBX] = ts->vm86_saved_regs.ebx;
163   - env->regs[R_ECX] = ts->vm86_saved_regs.ecx;
164   - env->regs[R_EDX] = ts->vm86_saved_regs.edx;
165   - env->regs[R_ESI] = ts->vm86_saved_regs.esi;
166   - env->regs[R_EDI] = ts->vm86_saved_regs.edi;
167   - env->regs[R_EBP] = ts->vm86_saved_regs.ebp;
168   - env->regs[R_ESP] = ts->vm86_saved_regs.esp;
169   - env->eflags = ts->vm86_saved_regs.eflags;
170   - env->eip = ts->vm86_saved_regs.eip;
171   -
172   - cpu_x86_load_seg(env, R_CS, ts->vm86_saved_regs.cs);
173   - cpu_x86_load_seg(env, R_SS, ts->vm86_saved_regs.ss);
174   - cpu_x86_load_seg(env, R_DS, ts->vm86_saved_regs.ds);
175   - cpu_x86_load_seg(env, R_ES, ts->vm86_saved_regs.es);
176   - cpu_x86_load_seg(env, R_FS, ts->vm86_saved_regs.fs);
177   - cpu_x86_load_seg(env, R_GS, ts->vm86_saved_regs.gs);
178   -
179   - env->regs[R_EAX] = ret;
180 275 } else {
181 276 if (pc[0] == 0xcd && pc[1] == 0x80) {
182 277 /* syscall */
... ... @@ -200,20 +295,28 @@ void cpu_loop(struct CPUX86State *env)
200 295 }
201 296 break;
202 297 case EXCP00_DIVZ:
203   - /* division by zero */
204   - info.si_signo = SIGFPE;
205   - info.si_errno = 0;
206   - info.si_code = TARGET_FPE_INTDIV;
207   - info._sifields._sigfault._addr = env->eip;
208   - queue_signal(info.si_signo, &info);
  298 + if (env->eflags & VM_MASK) {
  299 + do_int(env, trapnr);
  300 + } else {
  301 + /* division by zero */
  302 + info.si_signo = SIGFPE;
  303 + info.si_errno = 0;
  304 + info.si_code = TARGET_FPE_INTDIV;
  305 + info._sifields._sigfault._addr = env->eip;
  306 + queue_signal(info.si_signo, &info);
  307 + }
209 308 break;
210 309 case EXCP04_INTO:
211 310 case EXCP05_BOUND:
212   - info.si_signo = SIGSEGV;
213   - info.si_errno = 0;
214   - info.si_code = 0;
215   - info._sifields._sigfault._addr = 0;
216   - queue_signal(info.si_signo, &info);
  311 + if (env->eflags & VM_MASK) {
  312 + do_int(env, trapnr);
  313 + } else {
  314 + info.si_signo = SIGSEGV;
  315 + info.si_errno = 0;
  316 + info.si_code = 0;
  317 + info._sifields._sigfault._addr = 0;
  318 + queue_signal(info.si_signo, &info);
  319 + }
217 320 break;
218 321 case EXCP06_ILLOP:
219 322 info.si_signo = SIGILL;
... ... @@ -226,8 +329,8 @@ void cpu_loop(struct CPUX86State *env)
226 329 /* just indicate that signals should be handled asap */
227 330 break;
228 331 default:
229   - fprintf(stderr, "0x%08lx: Unknown exception CPU %d, aborting\n",
230   - (long)pc, err);
  332 + fprintf(stderr, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n",
  333 + (long)pc, trapnr);
231 334 abort();
232 335 }
233 336 process_pending_signals(env);
... ...
linux-user/qemu.h
... ... @@ -74,5 +74,6 @@ void cpu_loop(CPUX86State *env);
74 74 void process_pending_signals(void *cpu_env);
75 75 void signal_init(void);
76 76 int queue_signal(int sig, target_siginfo_t *info);
  77 +void save_v86_state(CPUX86State *env);
77 78  
78 79 #endif
... ...
linux-user/signal.c
... ... @@ -198,7 +198,7 @@ void __attribute((noreturn)) force_sig(int sig)
198 198 {
199 199 int host_sig;
200 200 host_sig = target_to_host_signal(sig);
201   - fprintf(stderr, "gemu: uncaught target signal %d (%s) - exiting\n",
  201 + fprintf(stderr, "qemu: uncaught target signal %d (%s) - exiting\n",
202 202 sig, strsignal(host_sig));
203 203 #if 1
204 204 _exit(-host_sig);
... ... @@ -223,7 +223,7 @@ int queue_signal(int sig, target_siginfo_t *info)
223 223 target_ulong handler;
224 224  
225 225 #if defined(DEBUG_SIGNAL)
226   - fprintf(stderr, "queue_sigal: sig=%d\n",
  226 + fprintf(stderr, "queue_signal: sig=%d\n",
227 227 sig);
228 228 #endif
229 229 k = &sigact_table[sig - 1];
... ... @@ -317,7 +317,7 @@ static void host_signal_handler(int host_signum, siginfo_t *info,
317 317 if (sig < 1 || sig > TARGET_NSIG)
318 318 return;
319 319 #if defined(DEBUG_SIGNAL)
320   - fprintf(stderr, "gemu: got signal %d\n", sig);
  320 + fprintf(stderr, "qemu: got signal %d\n", sig);
321 321 dump_regs(puc);
322 322 #endif
323 323 host_to_target_siginfo_noswap(&tinfo, info);
... ... @@ -538,7 +538,6 @@ setup_sigcontext(struct target_sigcontext *sc, struct target_fpstate *fpstate,
538 538 /* non-iBCS2 extensions.. */
539 539 err |= __put_user(mask, &sc->oldmask);
540 540 err |= __put_user(/*current->thread.cr2*/ 0, &sc->cr2);
541   -
542 541 return err;
543 542 }
544 543  
... ... @@ -859,7 +858,7 @@ void process_pending_signals(void *cpu_env)
859 858  
860 859 handle_signal:
861 860 #ifdef DEBUG_SIGNAL
862   - fprintf(stderr, "gemu: process signal %d\n", sig);
  861 + fprintf(stderr, "qemu: process signal %d\n", sig);
863 862 #endif
864 863 /* dequeue signal */
865 864 q = k->first;
... ... @@ -893,6 +892,14 @@ void process_pending_signals(void *cpu_env)
893 892 end of the signal execution (see do_sigreturn) */
894 893 host_to_target_sigset(&target_old_set, &old_set);
895 894  
  895 + /* if the CPU is in VM86 mode, we restore the 32 bit values */
  896 +#ifdef TARGET_I386
  897 + {
  898 + CPUX86State *env = cpu_env;
  899 + if (env->eflags & VM_MASK)
  900 + save_v86_state(env);
  901 + }
  902 +#endif
896 903 /* prepare the stack frame of the virtual CPU */
897 904 if (k->sa.sa_flags & TARGET_SA_SIGINFO)
898 905 setup_rt_frame(sig, k, &q->info, &target_old_set, cpu_env);
... ...
syscall-i386.h
... ... @@ -755,6 +755,11 @@ struct target_modify_ldt_ldt_s {
755 755 unsigned int flags;
756 756 };
757 757  
  758 +
  759 +/* vm86 defines */
  760 +
  761 +#define TARGET_BIOSSEG 0x0f000
  762 +
758 763 #define TARGET_VM86_SIGNAL 0 /* return due to signal */
759 764 #define TARGET_VM86_UNKNOWN 1 /* unhandled GP fault - IO-instruction or similar */
760 765 #define TARGET_VM86_INTx 2 /* int3/int x instruction (ARG = x) */
... ...