Fixed typo.
[wine] / loader / dos / dosvm.c
1 /*
2  * DOS Virtual Machine
3  *
4  * Copyright 1998 Ove Kåven
5  *
6  * This code hasn't been completely cleaned up yet.
7  */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <signal.h>
15 #include <unistd.h>
16 #include <sys/time.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19
20 #include "wine/winbase16.h"
21 #include "winnt.h"
22 #include "sig_context.h"
23 #include "msdos.h"
24 #include "file.h"
25 #include "miscemu.h"
26 #include "debugger.h"
27 #include "module.h"
28 #include "task.h"
29 #include "ldt.h"
30 #include "dosexe.h"
31 #include "dosmod.h"
32 #include "debug.h"
33
34 #ifdef MZ_SUPPORTED
35
36 #include <sys/mman.h>
37 #include <sys/vm86.h>
38
39 static void DOSVM_Dump( LPDOSTASK lpDosTask, int fn, int sig,
40                         struct vm86plus_struct*VM86 )
41 {
42  unsigned iofs;
43  BYTE*inst;
44  int x;
45
46  switch (VM86_TYPE(fn)) {
47   case VM86_SIGNAL:
48    printf("Trapped signal %d\n",sig); break;
49   case VM86_UNKNOWN:
50    printf("Trapped unhandled GPF\n"); break;
51   case VM86_INTx:
52    printf("Trapped INT %02x\n",VM86_ARG(fn)); break;
53   case VM86_STI:
54    printf("Trapped STI\n"); break;
55   case VM86_PICRETURN:
56    printf("Trapped due to pending PIC request\n"); break;
57   case VM86_TRAP:
58    printf("Trapped debug request\n"); break;
59   default:
60    printf("Trapped unknown VM86 type %d arg %d\n",VM86_TYPE(fn),VM86_ARG(fn)); break;
61  }
62 #define REGS VM86->regs
63  fprintf(stderr,"AX=%04lX CX=%04lX DX=%04lX BX=%04lX\n",REGS.eax,REGS.ecx,REGS.edx,REGS.ebx);
64  fprintf(stderr,"SI=%04lX DI=%04lX SP=%04lX BP=%04lX\n",REGS.esi,REGS.edi,REGS.esp,REGS.ebp);
65  fprintf(stderr,"CS=%04X DS=%04X ES=%04X SS=%04X\n",REGS.cs,REGS.ds,REGS.es,REGS.ss);
66  fprintf(stderr,"IP=%04lX EFLAGS=%08lX\n",REGS.eip,REGS.eflags);
67
68  iofs=((DWORD)REGS.cs<<4)+REGS.eip;
69 #undef REGS
70  inst=(BYTE*)lpDosTask->img+iofs;
71  printf("Opcodes:");
72  for (x=0; x<8; x++) printf(" %02x",inst[x]);
73  printf("\n");
74 }
75
76 static int DOSVM_Int( int vect, PCONTEXT context, LPDOSTASK lpDosTask )
77 {
78  extern UINT16 DPMI_wrap_seg;
79
80  if (vect==0x31) {
81   if (CS_reg(context)==DPMI_wrap_seg) {
82    /* exit from real-mode wrapper */
83    return -1;
84   }
85   /* we could probably move some other dodgy stuff here too from dpmi.c */
86  }
87  INT_RealModeInterrupt(vect,context);
88  return 0;
89 }
90
91 static void DOSVM_SimulateInt( int vect, PCONTEXT context, LPDOSTASK lpDosTask )
92 {
93  FARPROC16 handler=INT_GetRMHandler(vect);
94  WORD*stack=(WORD*)(V86BASE(context)+(((DWORD)SS_reg(context))<<4)+SP_reg(context));
95
96  *(--stack)=FL_reg(context);
97  *(--stack)=CS_reg(context);
98  *(--stack)=IP_reg(context);
99  SP_reg(context)-=6;
100  CS_reg(context)=SELECTOROF(handler);
101  IP_reg(context)=OFFSETOF(handler);
102 }
103
104 #define CV CP(eax,EAX); CP(ecx,ECX); CP(edx,EDX); CP(ebx,EBX); \
105            CP(esi,ESI); CP(edi,EDI); CP(esp,ESP); CP(ebp,EBP); \
106            CP(cs,CS); CP(ds,DS); CP(es,ES); \
107            CP(ss,SS); CP(fs,FS); CP(gs,GS); \
108            CP(eip,EIP); CP(eflags,EFL)
109
110 static int DOSVM_Process( LPDOSTASK lpDosTask, int fn, int sig,
111                           struct vm86plus_struct*VM86 )
112 {
113  SIGCONTEXT sigcontext;
114  CONTEXT context;
115  int ret=0;
116
117  if (VM86_TYPE(fn)==VM86_UNKNOWN) {
118   /* INSTR_EmulateInstruction needs a SIGCONTEXT, not a CONTEXT... */
119 #define CP(x,y) y##_sig(&sigcontext) = VM86->regs.x
120   CV;
121 #undef CP
122   if (fnINSTR_EmulateInstruction) ret=fnINSTR_EmulateInstruction(&sigcontext);
123 #define CP(x,y) VM86->regs.x = y##_sig(&sigcontext)
124   CV;
125 #undef CP
126   if (ret) return 0;
127   ret=0;
128  }
129 #define CP(x,y) y##_reg(&context) = VM86->regs.x
130  CV;
131 #undef CP
132  (void*)V86BASE(&context)=lpDosTask->img;
133
134  switch (VM86_TYPE(fn)) {
135   case VM86_SIGNAL:
136    TRACE(int,"DOS module caught signal %d\n",sig);
137    if (sig==SIGALRM) {
138     DOSVM_SimulateInt(8,&context,lpDosTask);
139    } else
140    if (sig==SIGHUP) {
141     if (ctx_debug_call) ctx_debug_call(SIGTRAP,&context);
142    } else
143    if ((sig==SIGILL)||(sig==SIGSEGV)) {
144     if (ctx_debug_call) ctx_debug_call(SIGILL,&context);
145    } else {
146     DOSVM_Dump(lpDosTask,fn,sig,VM86);
147     ret=-1;
148    }
149    break;
150   case VM86_UNKNOWN: /* unhandled GPF */
151    DOSVM_Dump(lpDosTask,fn,sig,VM86);
152    if (ctx_debug_call) ctx_debug_call(SIGSEGV,&context); else ret=-1;
153    break;
154   case VM86_INTx:
155    if (TRACE_ON(relay))
156     DPRINTF("Call DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn),context.Eax,context.SegCs,context.Eip);
157    ret=DOSVM_Int(VM86_ARG(fn),&context,lpDosTask);
158    if (TRACE_ON(relay))
159     DPRINTF("Ret  DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn),context.Eax,context.SegCs,context.Eip);
160    break;
161   case VM86_STI:
162    break;
163   case VM86_PICRETURN:
164    printf("Trapped due to pending PIC request\n"); break;
165   case VM86_TRAP:
166    if (ctx_debug_call) ctx_debug_call(SIGTRAP,&context);
167    break;
168   default:
169    DOSVM_Dump(lpDosTask,fn,sig,VM86);
170    ret=-1;
171  }
172
173 #define CP(x,y) VM86->regs.x = y##_reg(&context)
174  CV;
175 #undef CP
176  return ret;
177 }
178
179 int DOSVM_Enter( PCONTEXT context )
180 {
181  TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
182  NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
183  LPDOSTASK lpDosTask;
184  struct vm86plus_struct VM86;
185  int stat,len,sig;
186  fd_set readfds,exceptfds;
187
188  GlobalUnlock16( GetCurrentTask() );
189  if (!pModule) {
190   ERR(module,"No task is currently active!\n");
191   return -1;
192  }
193  if (!(lpDosTask=pModule->lpDosTask)) {
194   /* MZ_CreateProcess or MZ_AllocDPMITask should have been called first */
195   ERR(module,"dosmod has not been initialized!");
196   return -1;
197  }
198
199  if (context) {
200 #define CP(x,y) VM86.regs.x = y##_reg(context)
201   CV;
202 #undef CP
203  } else {
204 /* initial setup */
205   /* allocate standard DOS handles */
206   FILE_InitProcessDosHandles(); 
207   /* registers */
208   memset(&VM86,0,sizeof(VM86));
209   VM86.regs.cs=lpDosTask->init_cs;
210   VM86.regs.eip=lpDosTask->init_ip;
211   VM86.regs.ss=lpDosTask->init_ss;
212   VM86.regs.esp=lpDosTask->init_sp;
213   VM86.regs.ds=lpDosTask->psp_seg;
214   VM86.regs.es=lpDosTask->psp_seg;
215   /* hmm, what else do we need? */
216  }
217
218  /* main exchange loop */
219  do {
220   stat = VM86_ENTER;
221   errno = 0;
222   /* transmit VM86 structure to dosmod task */
223   if (write(lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
224    ERR(module,"dosmod sync lost, errno=%d\n",errno);
225    return -1;
226   }
227   if (write(lpDosTask->write_pipe,&VM86,sizeof(VM86))!=sizeof(VM86)) {
228    ERR(module,"dosmod sync lost, errno=%d\n",errno);
229    return -1;
230   }
231   /* wait for response, with async events enabled */
232   FD_ZERO(&readfds);
233   FD_ZERO(&exceptfds);
234   SIGNAL_MaskAsyncEvents(FALSE);
235   do {
236    FD_SET(lpDosTask->read_pipe,&readfds);
237    FD_SET(lpDosTask->read_pipe,&exceptfds);
238    select(lpDosTask->read_pipe+1,&readfds,NULL,&exceptfds,NULL);
239   } while (!(FD_ISSET(lpDosTask->read_pipe,&readfds)||
240              FD_ISSET(lpDosTask->read_pipe,&exceptfds)));
241   SIGNAL_MaskAsyncEvents(TRUE);
242   /* read response (with async events disabled to avoid some strange problems) */
243   do {
244    if ((len=read(lpDosTask->read_pipe,&stat,sizeof(stat)))!=sizeof(stat)) {
245     if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
246      WARN(module,"rereading dosmod return code due to errno=%d, result=%d\n",errno,len);
247      continue;
248     }
249     ERR(module,"dosmod sync lost reading return code, errno=%d, result=%d\n",errno,len);
250     return -1;
251    }
252   } while (0);
253   TRACE(module,"dosmod return code=%d\n",stat);
254   do {
255    if ((len=read(lpDosTask->read_pipe,&VM86,sizeof(VM86)))!=sizeof(VM86)) {
256     if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
257      WARN(module,"rereading dosmod VM86 structure due to errno=%d, result=%d\n",errno,len);
258      continue;
259     }
260     ERR(module,"dosmod sync lost reading VM86 structure, errno=%d, result=%d\n",errno,len);
261     return -1;
262    }
263   } while (0);
264   if ((stat&0xff)==DOSMOD_SIGNAL) {
265    do {
266     if ((len=read(lpDosTask->read_pipe,&sig,sizeof(sig)))!=sizeof(sig)) {
267      if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
268       WARN(module,"rereading dosmod signal due to errno=%d, result=%d\n",errno,len);
269       continue;
270      }
271      ERR(module,"dosmod sync lost reading signal, errno=%d, result=%d\n",errno,len);
272      return -1;
273     }
274    } while (0);
275   } else sig=0;
276   /* got response */
277  } while (DOSVM_Process(lpDosTask,stat,sig,&VM86)>=0);
278
279  if (context) {
280 #define CP(x,y) y##_reg(context) = VM86.regs.x
281   CV;
282 #undef CP
283  }
284  return 0;
285 }
286
287 void DOSVM_SetTimer( unsigned ticks )
288 {
289  TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
290  NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
291  int stat=DOSMOD_SET_TIMER;
292  struct timeval tim;
293
294  GlobalUnlock16( GetCurrentTask() );
295  if (pModule&&pModule->lpDosTask) {
296   /* the PC clocks ticks at 1193180 Hz */
297   tim.tv_sec=0;
298   tim.tv_usec=((unsigned long long)ticks*1000000)/1193180;
299   /* sanity check */
300   if (!tim.tv_usec) tim.tv_usec=1;
301
302   if (write(pModule->lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
303    ERR(module,"dosmod sync lost, errno=%d\n",errno);
304    return;
305   }
306   if (write(pModule->lpDosTask->write_pipe,&tim,sizeof(tim))!=sizeof(tim)) {
307    ERR(module,"dosmod sync lost, errno=%d\n",errno);
308    return;
309   }
310   /* there's no return */
311  }
312 }
313
314 unsigned DOSVM_GetTimer( void )
315 {
316  TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
317  NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
318  int stat=DOSMOD_GET_TIMER;
319  struct timeval tim;
320
321  GlobalUnlock16( GetCurrentTask() );
322  if (pModule&&pModule->lpDosTask) {
323   if (write(pModule->lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
324    ERR(module,"dosmod sync lost, errno=%d\n",errno);
325    return 0;
326   }
327   /* read response */
328   if (read(pModule->lpDosTask->read_pipe,&tim,sizeof(tim))!=sizeof(tim)) {
329    ERR(module,"dosmod sync lost, errno=%d\n",errno);
330    return 0;
331   }
332   return ((unsigned long long)tim.tv_usec*1193180)/1000000;
333  }
334  return 0;
335 }
336
337 void MZ_Tick( WORD handle )
338 {
339  /* find the DOS task that has the right system_timer handle... */
340  /* should usually be the current, so let's just be lazy... */
341  TDB *pTask = (TDB*)GlobalLock16( GetCurrentTask() );
342  NE_MODULE *pModule = pTask ? NE_GetPtr( pTask->hModule ) : NULL;
343  LPDOSTASK lpDosTask = pModule ? pModule->lpDosTask : NULL;
344
345  GlobalUnlock16( GetCurrentTask() );
346  if (lpDosTask&&(lpDosTask->system_timer==handle)) {
347   /* BIOS timer tick */
348   (*((DWORD*)(((BYTE*)(lpDosTask->img))+0x46c)))++;
349  }
350 }
351
352 #else /* !MZ_SUPPORTED */
353
354 int DOSVM_Enter( PCONTEXT context )
355 {
356  ERR(module,"DOS realmode not supported on this architecture!\n");
357  return -1;
358 }
359
360 void MZ_Tick( WORD handle ) {}
361 void DOSVM_SetTimer( unsigned ticks ) {}
362 unsigned DOSVM_GetTimer( void ) { return 0; }
363
364 #endif