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