Fixed a race condition on RPC worker thread creation, and a typo.
[wine] / dlls / winedos / interrupts.c
1 /*
2  * Interrupt emulation
3  *
4  * Copyright 2002 Jukka Heinonen
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "dosexe.h"
22 #include "wine/debug.h"
23 #include "wine/winbase16.h"
24
25 #ifdef HAVE_SYS_VM86_H
26 # include <sys/vm86.h>
27 #endif
28
29 #ifndef IF_MASK
30 #define IF_MASK 0x00000200
31 #endif
32
33 #ifndef VIF_MASK
34 #define VIF_MASK 0x00080000
35 #endif
36
37
38 WINE_DEFAULT_DEBUG_CHANNEL(int);
39 WINE_DECLARE_DEBUG_CHANNEL(relay);
40
41
42 static FARPROC16     DOSVM_Vectors16[256];
43 static FARPROC48     DOSVM_Vectors48[256];
44 static const INTPROC DOSVM_VectorsBuiltin[] =
45 {
46   /* 00 */ 0,                  0,                  0,                  0,
47   /* 04 */ 0,                  0,                  0,                  0,
48   /* 08 */ 0,                  DOSVM_Int09Handler, 0,                  0,
49   /* 0C */ 0,                  0,                  0,                  0,
50   /* 10 */ DOSVM_Int10Handler, DOSVM_Int11Handler, DOSVM_Int12Handler, DOSVM_Int13Handler,
51   /* 14 */ 0,                  DOSVM_Int15Handler, DOSVM_Int16Handler, DOSVM_Int17Handler,
52   /* 18 */ 0,                  0,                  DOSVM_Int1aHandler, 0,
53   /* 1C */ 0,                  0,                  0,                  0,
54   /* 20 */ DOSVM_Int20Handler, DOSVM_Int21Handler, 0,                  0,
55   /* 24 */ 0,                  DOSVM_Int25Handler, DOSVM_Int26Handler, 0,
56   /* 28 */ 0,                  DOSVM_Int29Handler, DOSVM_Int2aHandler, 0,
57   /* 2C */ 0,                  0,                  0,                  DOSVM_Int2fHandler,
58   /* 30 */ 0,                  DOSVM_Int31Handler, 0,                  DOSVM_Int33Handler,
59   /* 34 */ DOSVM_Int34Handler, DOSVM_Int35Handler, DOSVM_Int36Handler, DOSVM_Int37Handler,
60   /* 38 */ DOSVM_Int38Handler, DOSVM_Int39Handler, DOSVM_Int3aHandler, DOSVM_Int3bHandler,
61   /* 3C */ DOSVM_Int3cHandler, DOSVM_Int3dHandler, DOSVM_Int3eHandler, 0,
62   /* 40 */ 0,                  DOSVM_Int41Handler, 0,                  0,
63   /* 44 */ 0,                  0,                  0,                  0,
64   /* 48 */ 0,                  0,                  0,                  DOSVM_Int4bHandler,
65   /* 4C */ 0,                  0,                  0,                  0,
66   /* 50 */ 0,                  0,                  0,                  0,
67   /* 54 */ 0,                  0,                  0,                  0,
68   /* 58 */ 0,                  0,                  0,                  0,
69   /* 5C */ DOSVM_Int5cHandler, 0,                  0,                  0,
70   /* 60 */ 0,                  0,                  0,                  0,
71   /* 64 */ 0,                  0,                  0,                  DOSVM_Int67Handler
72 };
73
74
75 /*
76  * Sizes of real mode and protected mode interrupt stubs.
77  */
78 #define DOSVM_STUB_RM   4
79 #define DOSVM_STUB_PM16 5
80 #define DOSVM_STUB_PM48 6
81
82
83 /**********************************************************************
84  *         DOSVM_DefaultHandler
85  *
86  * Default interrupt handler. This will be used to emulate all
87  * interrupts that don't have their own interrupt handler.
88  */
89 void WINAPI DOSVM_DefaultHandler( CONTEXT86 *context )
90 {
91 }
92
93
94 /**********************************************************************
95  *         DOSVM_GetBuiltinHandler
96  *
97  * Return Wine interrupt handler procedure for a given interrupt.
98  */
99 static INTPROC DOSVM_GetBuiltinHandler( BYTE intnum )
100 {
101     if (intnum < sizeof(DOSVM_VectorsBuiltin)/sizeof(INTPROC)) {
102         INTPROC proc = DOSVM_VectorsBuiltin[intnum];
103         if (proc)
104             return proc;
105     }
106
107     WARN("int%x not implemented, returning dummy handler\n", intnum );
108     return DOSVM_DefaultHandler;
109 }
110
111
112 /**********************************************************************
113  *          DOSVM_IntProcRelay
114  *
115  * Simple DOSRELAY that interprets its argument as INTPROC and calls it.
116  */
117 static void DOSVM_IntProcRelay( CONTEXT86 *context, LPVOID data )
118 {
119     INTPROC proc = (INTPROC)data;
120     proc(context);
121 }
122
123
124 /**********************************************************************
125  *          DOSVM_PushFlags
126  *
127  * This routine is used to make default int25 and int26 handlers leave the 
128  * original eflags into stack. In order to do this, stack is manipulated
129  * so that it actually contains two copies of eflags, one of which is
130  * popped during return from interrupt handler.
131  */
132 static void DOSVM_PushFlags( CONTEXT86 *context, BOOL islong, BOOL isstub )
133 {
134     if (islong)
135     {
136         DWORD *stack = CTX_SEG_OFF_TO_LIN(context, 
137                                           context->SegSs, 
138                                           context->Esp);
139         context->Esp += -4; /* One item will be added to stack. */
140
141         if (isstub)
142         {
143             DWORD ip = stack[0];
144             DWORD cs = stack[1];
145             stack += 2; /* Pop ip and cs. */
146             *(--stack) = context->EFlags;
147             *(--stack) = cs;
148             *(--stack) = ip;
149         }
150         else
151             *(--stack) = context->EFlags;            
152     }
153     else
154     {
155         WORD *stack = CTX_SEG_OFF_TO_LIN(context, 
156                                          context->SegSs, 
157                                          context->Esp);
158         ADD_LOWORD( context->Esp, -2 ); /* One item will be added to stack. */
159
160         if (isstub)
161         {
162             WORD ip = stack[0];
163             WORD cs = stack[1];
164             stack += 2; /* Pop ip and cs. */
165             *(--stack) = LOWORD(context->EFlags);
166             *(--stack) = cs;
167             *(--stack) = ip;
168         }
169         else
170             *(--stack) = LOWORD(context->EFlags);
171     }
172 }
173
174
175 /**********************************************************************
176  *         DOSVM_EmulateInterruptPM
177  *
178  * Emulate software interrupt in 16-bit or 32-bit protected mode.
179  * Called from signal handler when intXX opcode is executed. 
180  *
181  * Pushes interrupt frame to stack and changes instruction 
182  * pointer to interrupt handler.
183  */
184 void WINAPI DOSVM_EmulateInterruptPM( CONTEXT86 *context, BYTE intnum ) 
185 {
186     if (TRACE_ON(relay)) 
187     {
188         DPRINTF( "Call DOS int 0x%02x ret=%04lx:%08lx\n",
189                  intnum, context->SegCs, context->Eip );
190         DPRINTF( "  eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx\n",
191                  context->Eax, context->Ebx, context->Ecx, context->Edx );
192         DPRINTF( "  esi=%08lx edi=%08lx ebp=%08lx esp=%08lx \n",
193                  context->Esi, context->Edi, context->Ebp, context->Esp );
194         DPRINTF( "  ds=%04lx es=%04lx fs=%04lx gs=%04lx flags=%08lx\n",
195                  context->SegDs, context->SegEs,
196                  context->SegFs, context->SegGs, context->EFlags );
197     }
198
199     if (context->SegCs == DOSVM_dpmi_segments->dpmi_sel)
200     {
201         DOSVM_BuildCallFrame( context, 
202                               DOSVM_IntProcRelay,
203                               DOSVM_RawModeSwitchHandler );
204     }
205     else if (context->SegCs == DOSVM_dpmi_segments->relay_code_sel)
206     {
207         /*
208          * This must not be called using DOSVM_BuildCallFrame.
209          */
210         DOSVM_RelayHandler( context );
211     }
212     else if (context->SegCs == DOSVM_dpmi_segments->int48_sel)
213     {
214         if (intnum != context->Eip / DOSVM_STUB_PM48)
215             WARN( "interrupt stub has been modified "
216                   "(interrupt is %02x, interrupt stub is %02lx)\n",
217                   intnum, context->Eip/DOSVM_STUB_PM48 );
218
219         TRACE( "builtin interrupt %02x has been branched to\n", intnum );
220
221         if (intnum == 0x25 || intnum == 0x26)
222             DOSVM_PushFlags( context, TRUE, TRUE );
223
224         DOSVM_BuildCallFrame( context, 
225                               DOSVM_IntProcRelay,
226                               DOSVM_GetBuiltinHandler(intnum) );
227     }
228     else if (context->SegCs == DOSVM_dpmi_segments->int16_sel)
229     {
230         if (intnum != context->Eip / DOSVM_STUB_PM16)
231             WARN( "interrupt stub has been modified "
232                   "(interrupt is %02x, interrupt stub is %02lx)\n",
233                   intnum, context->Eip/DOSVM_STUB_PM16 );
234
235         TRACE( "builtin interrupt %02x has been branched to\n", intnum );
236
237         if (intnum == 0x25 || intnum == 0x26)
238             DOSVM_PushFlags( context, FALSE, TRUE );
239
240         DOSVM_BuildCallFrame( context, 
241                               DOSVM_IntProcRelay, 
242                               DOSVM_GetBuiltinHandler(intnum) );
243     }
244     else
245     {
246         DOSVM_HardwareInterruptPM( context, intnum );
247     }
248 }
249
250
251 /**********************************************************************
252  *         DOSVM_HardwareInterruptPM
253  *
254  * Emulate call to interrupt handler in 16-bit or 32-bit protected mode.
255  *
256  * Pushes interrupt frame to stack and changes instruction 
257  * pointer to interrupt handler.
258  */
259 void DOSVM_HardwareInterruptPM( CONTEXT86 *context, BYTE intnum ) 
260 {
261     if(DOSVM_IsDos32())
262     {
263         FARPROC48 addr = DOSVM_GetPMHandler48( intnum );
264         
265         if (addr.selector == DOSVM_dpmi_segments->int48_sel)
266         {
267             TRACE( "builtin interrupt %02lx has been invoked "
268                    "(through vector %02x)\n", 
269                    addr.offset / DOSVM_STUB_PM48, intnum );
270
271             if (intnum == 0x25 || intnum == 0x26)
272                 DOSVM_PushFlags( context, TRUE, FALSE );
273
274             DOSVM_BuildCallFrame( context,
275                                   DOSVM_IntProcRelay,
276                                   DOSVM_GetBuiltinHandler(
277                                       addr.offset/DOSVM_STUB_PM48 ) );
278         }
279         else
280         {
281             DWORD *stack = CTX_SEG_OFF_TO_LIN(context, 
282                                               context->SegSs, 
283                                               context->Esp);
284             
285             TRACE( "invoking hooked interrupt %02x at %04x:%08lx\n",
286                    intnum, addr.selector, addr.offset );
287             
288             /* Push the flags and return address on the stack */
289             *(--stack) = context->EFlags;
290             *(--stack) = context->SegCs;
291             *(--stack) = context->Eip;
292             context->Esp += -12;
293
294             /* Jump to the interrupt handler */
295             context->SegCs  = addr.selector;
296             context->Eip = addr.offset;
297         }
298     }
299     else
300     {
301         FARPROC16 addr = DOSVM_GetPMHandler16( intnum );
302
303         if (SELECTOROF(addr) == DOSVM_dpmi_segments->int16_sel)
304         {
305             TRACE( "builtin interrupt %02x has been invoked "
306                    "(through vector %02x)\n", 
307                    OFFSETOF(addr)/DOSVM_STUB_PM16, intnum );
308
309             if (intnum == 0x25 || intnum == 0x26)
310                 DOSVM_PushFlags( context, FALSE, FALSE );
311             
312             DOSVM_BuildCallFrame( context, 
313                                   DOSVM_IntProcRelay,
314                                   DOSVM_GetBuiltinHandler(
315                                       OFFSETOF(addr)/DOSVM_STUB_PM16 ) );
316         }
317         else
318         {
319             WORD *stack = CTX_SEG_OFF_TO_LIN(context, 
320                                              context->SegSs, 
321                                              context->Esp);
322             
323             TRACE( "invoking hooked interrupt %02x at %04x:%04x\n", 
324                    intnum, SELECTOROF(addr), OFFSETOF(addr) );
325
326             /* Push the flags and return address on the stack */
327             *(--stack) = LOWORD(context->EFlags);
328             *(--stack) = context->SegCs;
329             *(--stack) = LOWORD(context->Eip);
330             ADD_LOWORD( context->Esp, -6 );
331
332             /* Jump to the interrupt handler */
333             context->SegCs =  HIWORD(addr);
334             context->Eip = LOWORD(addr);
335         }
336     }
337 }
338
339
340 /**********************************************************************
341  *         DOSVM_EmulateInterruptRM
342  *
343  * Emulate software interrupt in real mode.
344  * Called from VM86 emulation when intXX opcode is executed. 
345  *
346  * Either calls directly builtin handler or pushes interrupt frame to 
347  * stack and changes instruction pointer to interrupt handler.
348  *
349  * Returns FALSE if this interrupt was caused by return 
350  * from real mode wrapper.
351  */
352 BOOL WINAPI DOSVM_EmulateInterruptRM( CONTEXT86 *context, BYTE intnum ) 
353 {
354     if (TRACE_ON(relay)) 
355     {
356         DPRINTF( "Call DOS int 0x%02x ret=%04lx:%08lx\n",
357                  intnum, context->SegCs, context->Eip );
358         DPRINTF( "  eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx\n",
359                  context->Eax, context->Ebx, context->Ecx, context->Edx );
360         DPRINTF( "  esi=%08lx edi=%08lx ebp=%08lx esp=%08lx \n",
361                  context->Esi, context->Edi, context->Ebp, context->Esp );
362         DPRINTF( "  ds=%04lx es=%04lx fs=%04lx gs=%04lx flags=%08lx\n",
363                  context->SegDs, context->SegEs,
364                  context->SegFs, context->SegGs, context->EFlags );
365     }
366
367     /* check for our real-mode hooks */
368     if (intnum == 0x31)
369     {
370         /* is this exit from real-mode wrapper */
371         if (context->SegCs == DOSVM_dpmi_segments->wrap_seg)
372             return FALSE;
373
374         if (DOSVM_CheckWrappers( context ))
375             return TRUE;
376     }
377
378     /* check if the call is from our fake BIOS interrupt stubs */
379     if (context->SegCs==0xf000)
380     {
381         if (intnum != context->Eip / DOSVM_STUB_RM)
382             WARN( "interrupt stub has been modified "
383                   "(interrupt is %02x, interrupt stub is %02lx)\n",
384                   intnum, context->Eip/DOSVM_STUB_RM );
385
386         TRACE( "builtin interrupt %02x has been branched to\n", intnum );
387         
388         DOSVM_CallBuiltinHandler( context, intnum );
389     }
390     else
391     {
392         DOSVM_HardwareInterruptRM( context, intnum );
393     }
394
395     return TRUE;
396 }
397
398
399 /**********************************************************************
400  *         DOSVM_HardwareInterruptRM
401  *
402  * Emulate call to interrupt handler in real mode.
403  *
404  * Either calls directly builtin handler or pushes interrupt frame to 
405  * stack and changes instruction pointer to interrupt handler.
406  */
407 void DOSVM_HardwareInterruptRM( CONTEXT86 *context, BYTE intnum ) 
408 {
409      FARPROC16 handler = DOSVM_GetRMHandler( intnum );
410
411      /* check if the call goes to an unhooked interrupt */
412      if (SELECTOROF(handler) == 0xf000) 
413      {
414          /* if so, call it directly */
415          TRACE( "builtin interrupt %02x has been invoked "
416                 "(through vector %02x)\n", 
417                 OFFSETOF(handler)/DOSVM_STUB_RM, intnum );
418          DOSVM_CallBuiltinHandler( context, OFFSETOF(handler)/DOSVM_STUB_RM );
419      }
420      else 
421      {
422          /* the interrupt is hooked, simulate interrupt in DOS space */ 
423          WORD* stack = PTR_REAL_TO_LIN( context->SegSs, context->Esp );
424          WORD  flag  = LOWORD( context->EFlags );
425
426          TRACE( "invoking hooked interrupt %02x at %04x:%04x\n", 
427                 intnum, SELECTOROF(handler), OFFSETOF(handler) );
428
429          /* Copy virtual interrupt flag to pushed interrupt flag. */
430          if (context->EFlags & VIF_MASK)
431              flag |= IF_MASK;
432          else 
433              flag &= ~IF_MASK;
434
435          *(--stack) = flag;
436          *(--stack) = context->SegCs;
437          *(--stack) = LOWORD( context->Eip );
438          context->Esp -= 6;
439          context->SegCs = SELECTOROF( handler );
440          context->Eip   = OFFSETOF( handler );
441
442          /* Clear virtual interrupt flag. */
443          context->EFlags &= ~VIF_MASK;
444      }
445 }
446
447
448 /**********************************************************************
449  *          DOSVM_GetRMHandler
450  *
451  * Return the real mode interrupt vector for a given interrupt.
452  */
453 FARPROC16 DOSVM_GetRMHandler( BYTE intnum )
454 {
455   return ((FARPROC16*)0)[intnum];
456 }
457
458
459 /**********************************************************************
460  *          DOSVM_SetRMHandler
461  *
462  * Set the real mode interrupt handler for a given interrupt.
463  */
464 void DOSVM_SetRMHandler( BYTE intnum, FARPROC16 handler )
465 {
466   TRACE("Set real mode interrupt vector %02x <- %04x:%04x\n",
467        intnum, HIWORD(handler), LOWORD(handler) );
468   ((FARPROC16*)0)[intnum] = handler;
469 }
470
471
472 /**********************************************************************
473  *          DOSVM_GetPMHandler16
474  *
475  * Return the protected mode interrupt vector for a given interrupt.
476  */
477 FARPROC16 DOSVM_GetPMHandler16( BYTE intnum )
478 {
479     if (!DOSVM_Vectors16[intnum])
480     {
481         FARPROC16 proc = (FARPROC16)MAKESEGPTR( DOSVM_dpmi_segments->int16_sel,
482                                                 DOSVM_STUB_PM16 * intnum );
483         DOSVM_Vectors16[intnum] = proc;
484     }
485     return DOSVM_Vectors16[intnum];
486 }
487
488
489 /**********************************************************************
490  *          DOSVM_SetPMHandler16
491  *
492  * Set the protected mode interrupt handler for a given interrupt.
493  */
494 void DOSVM_SetPMHandler16( BYTE intnum, FARPROC16 handler )
495 {
496   TRACE("Set protected mode interrupt vector %02x <- %04x:%04x\n",
497        intnum, HIWORD(handler), LOWORD(handler) );
498   DOSVM_Vectors16[intnum] = handler;
499 }
500
501
502 /**********************************************************************
503  *         DOSVM_GetPMHandler48
504  *
505  * Return the protected mode interrupt vector for a given interrupt.
506  * Used to get 48-bit pointer for 32-bit interrupt handlers in DPMI32.
507  */
508 FARPROC48 DOSVM_GetPMHandler48( BYTE intnum )
509 {
510   if (!DOSVM_Vectors48[intnum].selector)
511   {
512     DOSVM_Vectors48[intnum].selector = DOSVM_dpmi_segments->int48_sel;
513     DOSVM_Vectors48[intnum].offset = DOSVM_STUB_PM48 * intnum;
514   }
515   return DOSVM_Vectors48[intnum];
516 }
517
518
519 /**********************************************************************
520  *         DOSVM_SetPMHandler48
521  *
522  * Set the protected mode interrupt handler for a given interrupt.
523  * Used to set 48-bit pointer for 32-bit interrupt handlers in DPMI32.
524  */
525 void DOSVM_SetPMHandler48( BYTE intnum, FARPROC48 handler )
526 {
527   TRACE("Set 32-bit protected mode interrupt vector %02x <- %04x:%08lx\n",
528        intnum, handler.selector, handler.offset );
529   DOSVM_Vectors48[intnum] = handler;
530 }
531
532
533 /**********************************************************************
534  *         DOSVM_CallBuiltinHandler
535  *
536  * Execute Wine interrupt handler procedure.
537  */
538 void WINAPI DOSVM_CallBuiltinHandler( CONTEXT86 *context, BYTE intnum ) 
539 {
540   INTPROC proc = DOSVM_GetBuiltinHandler( intnum );
541   proc( context );
542 }