| /* | |
| * DOS Virtual Machine | |
| * | |
| * Copyright 1998 Ove Kåven | |
| * | |
| * This code hasn't been completely cleaned up yet. | |
| */ | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <errno.h> | |
| #include <fcntl.h> | |
| #include <signal.h> | |
| #include <unistd.h> | |
| #include <sys/time.h> | |
| #include <sys/types.h> | |
| #include <sys/stat.h> | |
| #include "wine/winbase16.h" | |
| #include "winnt.h" | |
| #include "sig_context.h" | |
| #include "msdos.h" | |
| #include "file.h" | |
| #include "miscemu.h" | |
| #include "debugger.h" | |
| #include "module.h" | |
| #include "task.h" | |
| #include "ldt.h" | |
| #include "dosexe.h" | |
| #include "dosmod.h" | |
| #include "debug.h" | |
| #ifdef MZ_SUPPORTED | |
| #include <sys/mman.h> | |
| #include <sys/vm86.h> | |
| 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 (fnINSTR_EmulateInstruction) ret=fnINSTR_EmulateInstruction(&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 |