4 * Copyright 1998 Ove Kåven
6 * This code hasn't been completely cleaned up yet.
19 #include <sys/types.h>
22 #include "wine/winbase16.h"
23 #include "wine/exception.h"
36 #include "../../loader/dos/dosmod.h"
37 #include "stackframe.h"
38 #include "debugtools.h"
40 DECLARE_DEBUG_CHANNEL(int);
41 DECLARE_DEBUG_CHANNEL(module
);
42 DECLARE_DEBUG_CHANNEL(relay
);
46 #ifdef HAVE_SYS_VM86_H
47 # include <sys/vm86.h>
49 #ifdef HAVE_SYS_MMAN_H
50 # include <sys/mman.h>
53 #define IF_CLR(ctx) ((ctx)->EFlags &= ~VIF_MASK)
54 #define IF_SET(ctx) ((ctx)->EFlags |= VIF_MASK)
55 #define IF_ENABLED(ctx) ((ctx)->EFlags & VIF_MASK)
56 #define SET_PEND(ctx) ((ctx)->EFlags |= VIP_MASK)
57 #define CLR_PEND(ctx) ((ctx)->EFlags &= ~VIP_MASK)
58 #define IS_PEND(ctx) ((ctx)->EFlags & VIP_MASK)
62 typedef struct _DOSEVENT
{
64 void (*relay
)(CONTEXT86
*,void*);
66 struct _DOSEVENT
*next
;
67 } DOSEVENT
, *LPDOSEVENT
;
69 static struct _DOSEVENT
*pending_event
, *current_event
;
70 static int sig_sent
, entered
;
73 extern int read_pipe
, write_pipe
;
74 extern HANDLE hReadPipe
;
75 extern pid_t dosmod_pid
;
77 static void do_exception( int signal
, CONTEXT86
*context
)
80 if ((signal
== SIGTRAP
) || (signal
== SIGHUP
))
82 rec
.ExceptionCode
= EXCEPTION_BREAKPOINT
;
83 rec
.ExceptionFlags
= EXCEPTION_CONTINUABLE
;
87 rec
.ExceptionCode
= EXCEPTION_ILLEGAL_INSTRUCTION
; /* generic error */
88 rec
.ExceptionFlags
= EH_NONCONTINUABLE
;
90 rec
.ExceptionRecord
= NULL
;
91 rec
.ExceptionAddress
= (LPVOID
)context
->Eip
;
92 rec
.NumberParameters
= 0;
93 EXC_RtlRaiseException( &rec
, context
);
96 static void DOSVM_Dump( int fn
, int sig
, struct vm86plus_struct
*VM86
)
101 switch (VM86_TYPE(fn
)) {
103 printf("Trapped signal %d\n",sig
); break;
105 printf("Trapped unhandled GPF\n"); break;
107 printf("Trapped INT %02x\n",VM86_ARG(fn
)); break;
109 printf("Trapped STI\n"); break;
111 printf("Trapped due to pending PIC request\n"); break;
113 printf("Trapped debug request\n"); break;
115 printf("Trapped unknown VM86 type %d arg %d\n",VM86_TYPE(fn
),VM86_ARG(fn
)); break;
117 #define REGS VM86->regs
118 fprintf(stderr
,"AX=%04lX CX=%04lX DX=%04lX BX=%04lX\n",REGS
.eax
,REGS
.ecx
,REGS
.edx
,REGS
.ebx
);
119 fprintf(stderr
,"SI=%04lX DI=%04lX SP=%04lX BP=%04lX\n",REGS
.esi
,REGS
.edi
,REGS
.esp
,REGS
.ebp
);
120 fprintf(stderr
,"CS=%04X DS=%04X ES=%04X SS=%04X\n",REGS
.cs
,REGS
.ds
,REGS
.es
,REGS
.ss
);
121 fprintf(stderr
,"IP=%04lX EFLAGS=%08lX\n",REGS
.eip
,REGS
.eflags
);
123 inst
= PTR_REAL_TO_LIN( REGS
.cs
, REGS
.eip
);
126 for (x
=0; x
<8; x
++) printf(" %02x",inst
[x
]);
130 static int DOSVM_SimulateInt( int vect
, CONTEXT86
*context
, BOOL inwine
)
132 FARPROC16 handler
=INT_GetRMHandler(vect
);
134 /* check for our real-mode hooks */
136 if (context
->SegCs
==DOSMEM_wrap_seg
) {
137 /* exit from real-mode wrapper */
140 /* we could probably move some other dodgy stuff here too from dpmi.c */
142 /* check if the call is from our fake BIOS interrupt stubs */
143 if ((context
->SegCs
==0xf000) && !inwine
) {
144 if (vect
!= (context
->Eip
/4)) {
145 TRACE_(int)("something fishy going on here (interrupt stub is %02lx)\n", context
->Eip
/4);
147 TRACE_(int)("builtin interrupt %02x has been branched to\n", vect
);
148 INT_RealModeInterrupt(vect
, context
);
150 /* check if the call goes to an unhooked interrupt */
151 else if (SELECTOROF(handler
)==0xf000) {
152 /* if so, call it directly */
153 TRACE_(int)("builtin interrupt %02x has been invoked (through vector %02x)\n", OFFSETOF(handler
)/4, vect
);
154 INT_RealModeInterrupt(OFFSETOF(handler
)/4, context
);
156 /* the interrupt is hooked, simulate interrupt in DOS space */
158 WORD
*stack
= PTR_REAL_TO_LIN( context
->SegSs
, context
->Esp
);
159 WORD flag
=LOWORD(context
->EFlags
);
161 if (IF_ENABLED(context
)) flag
|=IF_MASK
;
165 *(--stack
)=context
->SegCs
;
166 *(--stack
)=LOWORD(context
->Eip
);
168 context
->SegCs
=SELECTOROF(handler
);
169 context
->Eip
=OFFSETOF(handler
);
175 #define SHOULD_PEND(x) \
176 (x && ((!current_event) || (x->priority < current_event->priority)))
178 static void DOSVM_SendQueuedEvent(CONTEXT86
*context
)
180 LPDOSEVENT event
= pending_event
;
182 if (SHOULD_PEND(event
)) {
183 /* remove from "pending" list */
184 pending_event
= event
->next
;
187 /* it's an IRQ, move it to "current" list */
188 event
->next
= current_event
;
189 current_event
= event
;
190 TRACE_(int)("dispatching IRQ %d\n",event
->irq
);
191 /* note that if DOSVM_SimulateInt calls an internal interrupt directly,
192 * current_event might be cleared (and event freed) in this very call! */
193 DOSVM_SimulateInt((event
->irq
<8)?(event
->irq
+8):(event
->irq
-8+0x70),context
,TRUE
);
196 TRACE_(int)("dispatching callback event\n");
197 (*event
->relay
)(context
,event
->data
);
201 if (!SHOULD_PEND(pending_event
)) {
202 TRACE_(int)("clearing Pending flag\n");
207 static void DOSVM_SendQueuedEvents(CONTEXT86
*context
)
209 /* we will send all queued events as long as interrupts are enabled,
210 * but IRQ events will disable interrupts again */
211 while (IS_PEND(context
) && IF_ENABLED(context
))
212 DOSVM_SendQueuedEvent(context
);
215 void WINAPI
DOSVM_QueueEvent( int irq
, int priority
, void (*relay
)(CONTEXT86
*,void*), void *data
)
217 LPDOSEVENT event
, cur
, prev
;
220 event
= malloc(sizeof(DOSEVENT
));
222 ERR_(int)("out of memory allocating event entry\n");
225 event
->irq
= irq
; event
->priority
= priority
;
226 event
->relay
= relay
; event
->data
= data
;
228 /* insert event into linked list, in order *after*
229 * all earlier events of higher or equal priority */
230 cur
= pending_event
; prev
= NULL
;
231 while (cur
&& cur
->priority
<=priority
) {
236 if (prev
) prev
->next
= event
;
237 else pending_event
= event
;
239 /* get dosmod's attention to the new event, if necessary */
241 TRACE_(int)("new event queued, signalling dosmod\n");
242 kill(dosmod_pid
,SIGUSR2
);
245 TRACE_(int)("new event queued\n");
248 /* DOS subsystem not running */
249 /* (this probably means that we're running a win16 app
250 * which uses DPMI to thunk down to DOS services) */
252 /* callback event, perform it with dummy context */
254 memset(&context
,0,sizeof(context
));
255 (*relay
)(&context
,data
);
257 ERR_(int)("IRQ without DOS task: should not happen");
262 #define CV do { CP(eax,Eax); CP(ecx,Ecx); CP(edx,Edx); CP(ebx,Ebx); \
263 CP(esi,Esi); CP(edi,Edi); CP(esp,Esp); CP(ebp,Ebp); \
264 CP(cs,SegCs); CP(ds,SegDs); CP(es,SegEs); \
265 CP(ss,SegSs); CP(fs,SegFs); CP(gs,SegGs); \
266 CP(eip,Eip); CP(eflags,EFlags); } while(0)
268 static int DOSVM_Process( int fn
, int sig
, struct vm86plus_struct
*VM86
)
273 #define CP(x,y) context.y = VM86->regs.x
276 if (VM86_TYPE(fn
)==VM86_UNKNOWN
) {
277 ret
=INSTR_EmulateInstruction(&context
);
278 #define CP(x,y) VM86->regs.x = context.y
285 if (VM86
->vm86plus
.force_return_for_pic
) {
289 /* linux doesn't preserve pending flag on return */
290 if (SHOULD_PEND(pending_event
)) {
295 switch (VM86_TYPE(fn
)) {
297 TRACE_(int)("DOS module caught signal %d\n",sig
);
298 if ((sig
==SIGALRM
) || (sig
==SIGUSR2
)) {
301 DOSVM_QueueEvent(0,DOS_PRIORITY_REALTIME
,NULL
,NULL
);
304 TRACE_(int)("setting Pending flag, interrupts are currently %s\n",
305 IF_ENABLED(&context
) ? "enabled" : "disabled");
307 DOSVM_SendQueuedEvents(&context
);
309 TRACE_(int)("no events are pending, clearing Pending flag\n");
314 else if ((sig
==SIGHUP
) || (sig
==SIGILL
) || (sig
==SIGSEGV
)) {
315 do_exception( sig
, &context
);
317 DOSVM_Dump(fn
,sig
,VM86
);
321 case VM86_UNKNOWN
: /* unhandled GPF */
322 DOSVM_Dump(fn
,sig
,VM86
);
323 do_exception( SIGSEGV
, &context
);
327 DPRINTF("Call DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn
),context
.Eax
,context
.SegCs
,context
.Eip
);
328 ret
=DOSVM_SimulateInt(VM86_ARG(fn
),&context
,FALSE
);
330 DPRINTF("Ret DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn
),context
.Eax
,context
.SegCs
,context
.Eip
);
334 /* case VM86_PICRETURN: */
335 TRACE_(int)("DOS task enabled interrupts %s events pending, sending events\n", IS_PEND(&context
)?"with":"without");
336 DOSVM_SendQueuedEvents(&context
);
339 do_exception( SIGTRAP
, &context
);
342 DOSVM_Dump(fn
,sig
,VM86
);
346 #define CP(x,y) VM86->regs.x = context.y
350 VM86
->vm86plus
.force_return_for_pic
= IS_PEND(&context
) ? 1 : 0;
356 static void DOSVM_ProcessConsole(void)
362 if (ReadConsoleInputA(GetStdHandle(STD_INPUT_HANDLE
),&msg
,1,&res
)) {
363 switch (msg
.EventType
) {
365 scan
= msg
.Event
.KeyEvent
.wVirtualScanCode
;
366 if (!msg
.Event
.KeyEvent
.bKeyDown
) scan
|= 0x80;
368 /* check whether extended bit is set,
369 * and if so, queue the extension prefix */
370 if (msg
.Event
.KeyEvent
.dwControlKeyState
& ENHANCED_KEY
) {
371 INT_Int09SendScan(0xE0,0);
373 INT_Int09SendScan(scan
,msg
.Event
.KeyEvent
.uChar
.AsciiChar
);
376 FIXME_(int)("unhandled console event: %d\n", msg
.EventType
);
381 static void DOSVM_ProcessMessage(MSG
*msg
)
385 TRACE_(int)("got message %04x, wparam=%08x, lparam=%08lx\n",msg
->message
,msg
->wParam
,msg
->lParam
);
386 if ((msg
->message
>=WM_MOUSEFIRST
)&&
387 (msg
->message
<=WM_MOUSELAST
)) {
388 INT_Int33Message(msg
->message
,msg
->wParam
,msg
->lParam
);
390 switch (msg
->message
) {
394 scan
|= (msg
->lParam
>> 16) & 0x7f;
396 /* check whether extended bit is set,
397 * and if so, queue the extension prefix */
398 if (msg
->lParam
& 0x1000000) {
399 /* FIXME: some keys (function keys) have
400 * extended bit set even when they shouldn't,
401 * should check for them */
402 INT_Int09SendScan(0xE0,0);
404 INT_Int09SendScan(scan
,0);
410 void WINAPI
DOSVM_Wait( int read_pipe
, HANDLE hObject
)
416 BOOL got_msg
= FALSE
;
418 objs
[0]=GetStdHandle(STD_INPUT_HANDLE
);
422 /* check for messages (waste time before the response check below) */
423 if (Callout
.PeekMessageA
)
425 while (Callout
.PeekMessageA(&msg
,0,0,0,PM_REMOVE
|PM_NOYIELD
)) {
427 DOSVM_ProcessMessage(&msg
);
428 /* we don't need a TranslateMessage here */
429 Callout
.DispatchMessageA(&msg
);
434 /* check for console input */
437 if (PeekConsoleInputA(objs
[0],&msg
,1,&num
) && num
) {
438 DOSVM_ProcessConsole();
442 if (read_pipe
== -1) {
446 struct timeval timeout
={0,0};
447 /* quick check for response from dosmod
448 * (faster than doing the full blocking wait, if data already available) */
449 FD_ZERO(&readfds
); FD_SET(read_pipe
,&readfds
);
450 if (select(read_pipe
+1,&readfds
,NULL
,NULL
,&timeout
)>0)
453 /* nothing yet, block while waiting for something to do */
454 if (Callout
.MsgWaitForMultipleObjects
)
455 waitret
= Callout
.MsgWaitForMultipleObjects(objc
,objs
,FALSE
,INFINITE
,QS_ALLINPUT
);
457 waitret
= WaitForMultipleObjects(objc
,objs
,FALSE
,INFINITE
);
459 if (waitret
==(DWORD
)-1) {
460 ERR_(module
)("dosvm wait error=%ld\n",GetLastError());
462 if ((read_pipe
!= -1) && hObject
) {
463 if (waitret
==(WAIT_OBJECT_0
+1)) break;
465 if (waitret
==WAIT_OBJECT_0
) {
466 DOSVM_ProcessConsole();
471 int WINAPI
DOSVM_Enter( CONTEXT86
*context
)
473 struct vm86plus_struct VM86
;
476 memset(&VM86
, 0, sizeof(VM86
));
477 #define CP(x,y) VM86.regs.x = context->y
480 if (VM86
.regs
.eflags
& IF_MASK
)
481 VM86
.regs
.eflags
|= VIF_MASK
;
483 /* main exchange loop */
486 TRACE_(module
)("thread is: %lx\n",GetCurrentThreadId());
489 /* transmit VM86 structure to dosmod task */
490 if (write(write_pipe
,&stat
,sizeof(stat
))!=sizeof(stat
)) {
491 ERR_(module
)("dosmod sync lost, errno=%d, fd=%d, pid=%d\n",errno
,write_pipe
,getpid());
494 if (write(write_pipe
,&VM86
,sizeof(VM86
))!=sizeof(VM86
)) {
495 ERR_(module
)("dosmod sync lost, errno=%d\n",errno
);
498 /* wait for response, doing other things in the meantime */
499 DOSVM_Wait(read_pipe
, hReadPipe
);
502 if ((len
=read(read_pipe
,&stat
,sizeof(stat
)))==sizeof(stat
)) break;
503 if (((errno
==EINTR
)||(errno
==EAGAIN
))&&(len
<=0)) {
504 WARN_(module
)("rereading dosmod return code due to errno=%d, result=%d\n",errno
,len
);
507 ERR_(module
)("dosmod sync lost reading return code, errno=%d, result=%d\n",errno
,len
);
510 TRACE_(module
)("dosmod return code=%d\n",stat
);
511 if (stat
==DOSMOD_LEFTIDLE
) {
515 if ((len
=read(read_pipe
,&VM86
,sizeof(VM86
)))==sizeof(VM86
)) break;
516 if (((errno
==EINTR
)||(errno
==EAGAIN
))&&(len
<=0)) {
517 WARN_(module
)("rereading dosmod VM86 structure due to errno=%d, result=%d\n",errno
,len
);
520 ERR_(module
)("dosmod sync lost reading VM86 structure, errno=%d, result=%d\n",errno
,len
);
523 if ((stat
&0xff)==DOSMOD_SIGNAL
) {
525 if ((len
=read(read_pipe
,&sig
,sizeof(sig
)))==sizeof(sig
)) break;
526 if (((errno
==EINTR
)||(errno
==EAGAIN
))&&(len
<=0)) {
527 WARN_(module
)("rereading dosmod signal due to errno=%d, result=%d\n",errno
,len
);
530 ERR_(module
)("dosmod sync lost reading signal, errno=%d, result=%d\n",errno
,len
);
535 } while (DOSVM_Process(stat
,sig
,&VM86
)>=0);
538 #define CP(x,y) context->y = VM86.regs.x
544 void WINAPI
DOSVM_PIC_ioport_out( WORD port
, BYTE val
)
548 if ((port
==0x20) && (val
==0x20)) {
550 /* EOI (End Of Interrupt) */
551 TRACE_(int)("received EOI for current IRQ, clearing\n");
552 event
= current_event
;
553 current_event
= event
->next
;
555 (*event
->relay
)(NULL
,event
->data
);
560 /* another event is pending, which we should probably
561 * be able to process now, so tell dosmod about it */
562 TRACE_(int)("another event pending, signalling dosmod\n");
563 kill(dosmod_pid
,SIGUSR2
);
567 WARN_(int)("EOI without active IRQ\n");
570 FIXME_(int)("unrecognized PIC command %02x\n",val
);
574 void WINAPI
DOSVM_SetTimer( unsigned ticks
)
576 int stat
=DOSMOD_SET_TIMER
;
580 /* the PC clocks ticks at 1193180 Hz */
582 tim
.tv_usec
=MulDiv(ticks
,1000000,1193180);
584 if (!tim
.tv_usec
) tim
.tv_usec
=1;
586 if (write(write_pipe
,&stat
,sizeof(stat
))!=sizeof(stat
)) {
587 ERR_(module
)("dosmod sync lost, errno=%d\n",errno
);
590 if (write(write_pipe
,&tim
,sizeof(tim
))!=sizeof(tim
)) {
591 ERR_(module
)("dosmod sync lost, errno=%d\n",errno
);
594 /* there's no return */
598 unsigned WINAPI
DOSVM_GetTimer( void )
600 int stat
=DOSMOD_GET_TIMER
;
604 if (write(write_pipe
,&stat
,sizeof(stat
))!=sizeof(stat
)) {
605 ERR_(module
)("dosmod sync lost, errno=%d\n",errno
);
610 if (read(read_pipe
,&tim
,sizeof(tim
))==sizeof(tim
)) break;
611 if ((errno
==EINTR
)||(errno
==EAGAIN
)) continue;
612 ERR_(module
)("dosmod sync lost, errno=%d\n",errno
);
615 return MulDiv(tim
.tv_usec
,1193180,1000000);
620 #else /* !MZ_SUPPORTED */
622 int WINAPI
DOSVM_Enter( CONTEXT86
*context
)
624 ERR_(module
)("DOS realmode not supported on this architecture!\n");
628 void WINAPI
DOSVM_Wait( int read_pipe
, HANDLE hObject
) {}
629 void WINAPI
DOSVM_PIC_ioport_out( WORD port
, BYTE val
) {}
630 void WINAPI
DOSVM_SetTimer( unsigned ticks
) {}
631 unsigned WINAPI
DOSVM_GetTimer( void ) { return 0; }
632 void WINAPI
DOSVM_QueueEvent( int irq
, int priority
, void (*relay
)(CONTEXT86
*,void*), void *data
)
635 /* callback event, perform it with dummy context */
637 memset(&context
,0,sizeof(context
));
638 (*relay
)(&context
,data
);
640 ERR_(int)("IRQ without DOS task: should not happen");