/* * DOS Virtual Machine * * Copyright 1998 Ove Kåven * * This code hasn't been completely cleaned up yet. */ #include #include #include #include #include #include #include #include #include #include #include "windows.h" #include "winbase.h" #include "winnt.h" #include "sig_context.h" #include "msdos.h" #include "miscemu.h" #include "debugger.h" #include "debug.h" #include "module.h" #include "task.h" #include "ldt.h" #include "dosexe.h" #include "dosmod.h" void (*ctx_debug_call)(int sig,CONTEXT*ctx)=NULL; BOOL32 (*instr_emu_call)(SIGCONTEXT*ctx)=NULL; #ifdef MZ_SUPPORTED #include #include static void DOSVM_Dump( LPDOSTASK lpDosTask, int fn, int sig, struct vm86plus_struct*VM86 ) { unsigned iofs; BYTE*inst; int x; switch (VM86_TYPE(fn)) { case VM86_SIGNAL: printf("Trapped signal %d\n",sig); break; case VM86_UNKNOWN: printf("Trapped unhandled GPF\n"); break; case VM86_INTx: printf("Trapped INT %02x\n",VM86_ARG(fn)); break; case VM86_STI: printf("Trapped STI\n"); break; case VM86_PICRETURN: printf("Trapped due to pending PIC request\n"); break; case VM86_TRAP: printf("Trapped debug request\n"); break; default: printf("Trapped unknown VM86 type %d arg %d\n",VM86_TYPE(fn),VM86_ARG(fn)); break; } #define REGS VM86->regs fprintf(stderr,"AX=%04lX CX=%04lX DX=%04lX BX=%04lX\n",REGS.eax,REGS.ecx,REGS.edx,REGS.ebx); fprintf(stderr,"SI=%04lX DI=%04lX SP=%04lX BP=%04lX\n",REGS.esi,REGS.edi,REGS.esp,REGS.ebp); fprintf(stderr,"CS=%04X DS=%04X ES=%04X SS=%04X\n",REGS.cs,REGS.ds,REGS.es,REGS.ss); fprintf(stderr,"IP=%04lX EFLAGS=%08lX\n",REGS.eip,REGS.eflags); iofs=((DWORD)REGS.cs<<4)+REGS.eip; #undef REGS inst=(BYTE*)lpDosTask->img+iofs; printf("Opcodes:"); for (x=0; x<8; x++) printf(" %02x",inst[x]); printf("\n"); } static int DOSVM_Int( int vect, PCONTEXT context, LPDOSTASK lpDosTask ) { extern UINT16 DPMI_wrap_seg; if (vect==0x31) { if (CS_reg(context)==DPMI_wrap_seg) { /* exit from real-mode wrapper */ return -1; } /* we could probably move some other dodgy stuff here too from dpmi.c */ } INT_RealModeInterrupt(vect,context); return 0; } static void DOSVM_SimulateInt( int vect, PCONTEXT context, LPDOSTASK lpDosTask ) { FARPROC16 handler=INT_GetRMHandler(vect); WORD*stack=(WORD*)(V86BASE(context)+(((DWORD)SS_reg(context))<<4)+SP_reg(context)); *(--stack)=FL_reg(context); *(--stack)=CS_reg(context); *(--stack)=IP_reg(context); SP_reg(context)-=6; CS_reg(context)=SELECTOROF(handler); IP_reg(context)=OFFSETOF(handler); } #define CV CP(eax,EAX); CP(ecx,ECX); CP(edx,EDX); CP(ebx,EBX); \ CP(esi,ESI); CP(edi,EDI); CP(esp,ESP); CP(ebp,EBP); \ CP(cs,CS); CP(ds,DS); CP(es,ES); \ CP(ss,SS); CP(fs,FS); CP(gs,GS); \ CP(eip,EIP); CP(eflags,EFL) static int DOSVM_Process( LPDOSTASK lpDosTask, int fn, int sig, struct vm86plus_struct*VM86 ) { SIGCONTEXT sigcontext; CONTEXT context; int ret=0; if (VM86_TYPE(fn)==VM86_UNKNOWN) { /* INSTR_EmulateInstruction needs a SIGCONTEXT, not a CONTEXT... */ #define CP(x,y) y##_sig(&sigcontext) = VM86->regs.x CV; #undef CP if (instr_emu_call) ret=instr_emu_call(&sigcontext); #define CP(x,y) VM86->regs.x = y##_sig(&sigcontext) CV; #undef CP if (ret) return 0; ret=0; } #define CP(x,y) y##_reg(&context) = VM86->regs.x CV; #undef CP (void*)V86BASE(&context)=lpDosTask->img; switch (VM86_TYPE(fn)) { case VM86_SIGNAL: TRACE(int,"DOS module caught signal %d\n",sig); if (sig==SIGALRM) { DOSVM_SimulateInt(8,&context,lpDosTask); } else if (sig==SIGHUP) { if (ctx_debug_call) ctx_debug_call(SIGTRAP,&context); } else if ((sig==SIGILL)||(sig==SIGSEGV)) { if (ctx_debug_call) ctx_debug_call(SIGILL,&context); } else { DOSVM_Dump(lpDosTask,fn,sig,VM86); ret=-1; } break; case VM86_UNKNOWN: /* unhandled GPF */ DOSVM_Dump(lpDosTask,fn,sig,VM86); if (ctx_debug_call) ctx_debug_call(SIGSEGV,&context); else ret=-1; break; case VM86_INTx: if (TRACE_ON(relay)) DPRINTF("Call DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn),context.Eax,context.SegCs,context.Eip); ret=DOSVM_Int(VM86_ARG(fn),&context,lpDosTask); if (TRACE_ON(relay)) DPRINTF("Ret DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn),context.Eax,context.SegCs,context.Eip); break; case VM86_STI: break; case VM86_PICRETURN: printf("Trapped due to pending PIC request\n"); break; case VM86_TRAP: if (ctx_debug_call) ctx_debug_call(SIGTRAP,&context); break; default: DOSVM_Dump(lpDosTask,fn,sig,VM86); ret=-1; } #define CP(x,y) VM86->regs.x = y##_reg(&context) CV; #undef CP return ret; } int DOSVM_Enter( PCONTEXT context ) { TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() ); NE_MODULE *pModule = NE_GetPtr( pTask->hModule ); LPDOSTASK lpDosTask; struct vm86plus_struct VM86; int stat,len,sig; fd_set readfds,exceptfds; GlobalUnlock16( GetCurrentTask() ); if (!pModule) { ERR(module,"No task is currently active!\n"); return -1; } if (!(lpDosTask=pModule->lpDosTask)) { /* MZ_CreateProcess or MZ_AllocDPMITask should have been called first */ ERR(module,"dosmod has not been initialized!"); return -1; } if (context) { #define CP(x,y) VM86.regs.x = y##_reg(context) CV; #undef CP } else { /* initial setup */ /* allocate standard DOS handles */ FILE_InitProcessDosHandles(); /* registers */ memset(&VM86,0,sizeof(VM86)); VM86.regs.cs=lpDosTask->init_cs; VM86.regs.eip=lpDosTask->init_ip; VM86.regs.ss=lpDosTask->init_ss; VM86.regs.esp=lpDosTask->init_sp; VM86.regs.ds=lpDosTask->psp_seg; VM86.regs.es=lpDosTask->psp_seg; /* hmm, what else do we need? */ } /* main exchange loop */ do { stat = VM86_ENTER; errno = 0; /* transmit VM86 structure to dosmod task */ if (write(lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) { ERR(module,"dosmod sync lost, errno=%d\n",errno); return -1; } if (write(lpDosTask->write_pipe,&VM86,sizeof(VM86))!=sizeof(VM86)) { ERR(module,"dosmod sync lost, errno=%d\n",errno); return -1; } /* wait for response, with async events enabled */ FD_ZERO(&readfds); FD_ZERO(&exceptfds); SIGNAL_MaskAsyncEvents(FALSE); do { FD_SET(lpDosTask->read_pipe,&readfds); FD_SET(lpDosTask->read_pipe,&exceptfds); select(lpDosTask->read_pipe+1,&readfds,NULL,&exceptfds,NULL); } while (!(FD_ISSET(lpDosTask->read_pipe,&readfds)|| FD_ISSET(lpDosTask->read_pipe,&exceptfds))); SIGNAL_MaskAsyncEvents(TRUE); /* read response (with async events disabled to avoid some strange problems) */ do { if ((len=read(lpDosTask->read_pipe,&stat,sizeof(stat)))!=sizeof(stat)) { if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) { WARN(module,"rereading dosmod return code due to errno=%d, result=%d\n",errno,len); continue; } ERR(module,"dosmod sync lost reading return code, errno=%d, result=%d\n",errno,len); return -1; } } while (0); TRACE(module,"dosmod return code=%d\n",stat); do { if ((len=read(lpDosTask->read_pipe,&VM86,sizeof(VM86)))!=sizeof(VM86)) { if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) { WARN(module,"rereading dosmod VM86 structure due to errno=%d, result=%d\n",errno,len); continue; } ERR(module,"dosmod sync lost reading VM86 structure, errno=%d, result=%d\n",errno,len); return -1; } } while (0); if ((stat&0xff)==DOSMOD_SIGNAL) { do { if ((len=read(lpDosTask->read_pipe,&sig,sizeof(sig)))!=sizeof(sig)) { if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) { WARN(module,"rereading dosmod signal due to errno=%d, result=%d\n",errno,len); continue; } ERR(module,"dosmod sync lost reading signal, errno=%d, result=%d\n",errno,len); return -1; } } while (0); } else sig=0; /* got response */ } while (DOSVM_Process(lpDosTask,stat,sig,&VM86)>=0); if (context) { #define CP(x,y) y##_reg(context) = VM86.regs.x CV; #undef CP } return 0; } void DOSVM_SetTimer( unsigned ticks ) { TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() ); NE_MODULE *pModule = NE_GetPtr( pTask->hModule ); int stat=DOSMOD_SET_TIMER; struct timeval tim; GlobalUnlock16( GetCurrentTask() ); if (pModule&&pModule->lpDosTask) { /* the PC clocks ticks at 1193180 Hz */ tim.tv_sec=0; tim.tv_usec=((unsigned long long)ticks*1000000)/1193180; /* sanity check */ if (!tim.tv_usec) tim.tv_usec=1; if (write(pModule->lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) { ERR(module,"dosmod sync lost, errno=%d\n",errno); return; } if (write(pModule->lpDosTask->write_pipe,&tim,sizeof(tim))!=sizeof(tim)) { ERR(module,"dosmod sync lost, errno=%d\n",errno); return; } /* there's no return */ } } unsigned DOSVM_GetTimer( void ) { TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() ); NE_MODULE *pModule = NE_GetPtr( pTask->hModule ); int stat=DOSMOD_GET_TIMER; struct timeval tim; GlobalUnlock16( GetCurrentTask() ); if (pModule&&pModule->lpDosTask) { if (write(pModule->lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) { ERR(module,"dosmod sync lost, errno=%d\n",errno); return 0; } /* read response */ if (read(pModule->lpDosTask->read_pipe,&tim,sizeof(tim))!=sizeof(tim)) { ERR(module,"dosmod sync lost, errno=%d\n",errno); return 0; } return ((unsigned long long)tim.tv_usec*1193180)/1000000; } return 0; } void MZ_Tick( WORD handle ) { /* find the DOS task that has the right system_timer handle... */ /* should usually be the current, so let's just be lazy... */ TDB *pTask = (TDB*)GlobalLock16( GetCurrentTask() ); NE_MODULE *pModule = pTask ? NE_GetPtr( pTask->hModule ) : NULL; LPDOSTASK lpDosTask = pModule ? pModule->lpDosTask : NULL; GlobalUnlock16( GetCurrentTask() ); if (lpDosTask&&(lpDosTask->system_timer==handle)) { /* BIOS timer tick */ (*((DWORD*)(((BYTE*)(lpDosTask->img))+0x46c)))++; } } #else /* !MZ_SUPPORTED */ int DOSVM_Enter( PCONTEXT context ) { ERR(module,"DOS realmode not supported on this architecture!\n"); return -1; } void MZ_Tick( WORD handle ) {} void DOSVM_SetTimer( unsigned ticks ) {} unsigned DOSVM_GetTimer( void ) { return 0; } #endif