| /* | |
| * 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 "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" | |
| void (*ctx_debug_call)(int sig,CONTEXT*ctx)=NULL; | |
| BOOL32 (*instr_emu_call)(SIGCONTEXT*ctx)=NULL; | |
| #ifdef MZ_SUPPORTED | |
| #include <sys/mman.h> | |
| #include <sys/vm86.h> | |
| static void DOSVM_Dump( LPDOSTASK lpDosTask, int fn, | |
| struct vm86plus_struct*VM86 ) | |
| { | |
| unsigned iofs; | |
| BYTE*inst; | |
| int x; | |
| switch (VM86_TYPE(fn)) { | |
| case VM86_SIGNAL: | |
| printf("Trapped signal\n"); 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 ) | |
| { | |
| if (vect==0x31) { | |
| if (CS_reg(context)==lpDosTask->dpmi_sel) { | |
| if (IP_reg(context)>=lpDosTask->wrap_ofs) { | |
| /* 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; | |
| } | |
| #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, | |
| 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: | |
| DOSVM_Dump(lpDosTask,fn,VM86); | |
| ret=-1; break; | |
| case VM86_UNKNOWN: /* unhandled GPF */ | |
| DOSVM_Dump(lpDosTask,fn,VM86); | |
| if (ctx_debug_call) ctx_debug_call(SIGSEGV,&context); else ret=-1; | |
| break; | |
| case VM86_INTx: | |
| TRACE(int,"DOS EXE calls INT %02x with AX=%04lx\n",VM86_ARG(fn),context.Eax); | |
| ret=DOSVM_Int(VM86_ARG(fn),&context,lpDosTask); 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,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; | |
| fd_set readfds,exceptfds; | |
| GlobalUnlock16( GetCurrentTask() ); | |
| if (!pModule) { | |
| ERR(module,"No task is currently active!\n"); | |
| return -1; | |
| } | |
| if (!pModule->lpDosTask) { | |
| /* no VM86 (dosmod) task is currently running, start one */ | |
| if ((lpDosTask = calloc(1, sizeof(DOSTASK))) == NULL) | |
| return 0; | |
| lpDosTask->hModule=pModule->self; | |
| stat=MZ_InitMemory(lpDosTask,pModule); | |
| if (stat>=32) stat=MZ_InitTask(lpDosTask); | |
| if (stat<32) { | |
| free(lpDosTask); | |
| return -1; | |
| } | |
| pModule->lpDosTask = lpDosTask; | |
| pModule->dos_image = lpDosTask->img; | |
| /* Note: we're leaving it running after this, in case we need it again, | |
| as this minimizes the overhead of starting it up every time... | |
| it will be killed automatically when the current task terminates */ | |
| } else lpDosTask=pModule->lpDosTask; | |
| if (context) { | |
| #define CP(x,y) VM86.regs.x = y##_reg(context) | |
| CV; | |
| #undef CP | |
| } else { | |
| /* initial setup */ | |
| 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; | |
| /* 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); | |
| /* got response */ | |
| } while (DOSVM_Process(lpDosTask,stat,&VM86)>=0); | |
| if (context) { | |
| #define CP(x,y) y##_reg(context) = VM86.regs.x | |
| CV; | |
| #undef CP | |
| } | |
| 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 ) {} | |
| #endif |