Added some new dosmod communication possibilities, including
activating a dosmod-specific setitimer(), and the capture of some
common signals, which is returned to Wine (previously a SIGILL
would just cause a "dosmod sync lost" without explanation, now it
invokes the Wine debugger). Invokes int 08 upon receipt of dosmod
SIGALRM, thus simulating the DOS timer. Made DOS interrupt calls
be reported by -debugmsg +relay instead of -debugmsg +int. And
fixed non-i386 compilation of dosmod. (Anything else?!)
diff --git a/loader/dos/dosmod.c b/loader/dos/dosmod.c
index 6514f08..75ac48f 100644
--- a/loader/dos/dosmod.c
+++ b/loader/dos/dosmod.c
@@ -4,7 +4,7 @@
* Copyright 1998 Ove Kåven
*/
-#ifdef linux
+#if defined(linux) && defined(__i386__)
/* apparently ELF images are usually loaded high anyway */
#ifndef __ELF__
@@ -22,9 +22,11 @@
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/vm86.h>
+#include <sys/time.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
+#include "dosmod.h"
/* FIXME: hack because libc vm86 may be the old syscall version */
@@ -54,11 +56,38 @@
return -1;
}
+void set_timer(struct timeval*tim)
+{
+ struct itimerval cur;
+
+ cur.it_interval=*tim;
+ cur.it_value=*tim;
+ setitimer(ITIMER_REAL,&cur,NULL);
+}
+
+volatile int sig_pend,sig_fatal=0;
+void*img;
+struct vm86plus_struct VM86;
+
+void sig_handler(int sig)
+{
+ if (sig_pend) fprintf(stderr,"DOSMOD previous signal %d lost\n",sig_pend);
+ sig_pend=sig;
+ signal(sig,sig_handler);
+}
+
+void bad_handler(int sig)
+{
+ fprintf(stderr,"DOSMOD caught fatal signal %d\n",sig);
+ fprintf(stderr,"(Last known VM86 CS:IP was %04x:%04lx)\n",VM86.regs.cs,VM86.regs.eip);
+ sig_pend=sig; sig_fatal++;
+ signal(sig,bad_handler);
+}
+
int main(int argc,char**argv)
{
int mfd=open(argv[0],O_RDWR);
- void*img;
- struct vm86plus_struct VM86;
+ struct timeval tim;
int func,ret;
off_t fofs=0;
pid_t ppid=getppid();
@@ -73,8 +102,8 @@
/* linux currently only allows mapping a process memory if it's being ptraced */
/* Linus doesn't like it, so this probably won't work in the future */
/* it doesn't even work for me right now */
- ptrace(PTRACE_ATTACH,ppid,0,0);
kill(ppid,SIGSTOP);
+ ptrace(PTRACE_ATTACH,ppid,0,0);
waitpid(ppid,NULL,0);
}
img=mmap(NULL,0x110000,PROT_EXEC|PROT_READ|PROT_WRITE,MAP_FIXED|MAP_SHARED,mfd,fofs);
@@ -87,24 +116,55 @@
fprintf(stderr,"in attempt to map %s, offset %08lX, length 110000, to offset 0\n",argv[0],fofs);
return 1;
}
-/* fprintf(stderr,"Successfully mapped DOS memory, entering vm86 loop\n"); */
- signal(SIGHUP,SIG_IGN);
- signal(SIGINT,SIG_IGN);
+/* initialize signals and system timer */
+ signal(SIGHUP,sig_handler);
+ signal(SIGINT,sig_handler);
+ signal(SIGUSR1,sig_handler);
+ signal(SIGUSR2,sig_handler);
+ signal(SIGALRM,sig_handler);
+
+ signal(SIGQUIT,bad_handler);
+ signal(SIGILL,bad_handler);
+ signal(SIGBUS,bad_handler);
+ signal(SIGFPE,bad_handler);
+ signal(SIGSEGV,bad_handler);
+ signal(SIGTERM,bad_handler);
+#if 0
+ tim.tv_sec=0; tim.tv_usec=54925;
+ set_timer(&tim);
+#endif
/* report back to the main program that we're ready */
- ret=0;
+ ret=1; /* dosmod protocol revision 1 */
write(1,&ret,sizeof(ret));
/* context exchange loop */
do {
if (read(0,&func,sizeof(func))!=sizeof(func)) return 1;
- if (read(0,&VM86,sizeof(VM86))!=sizeof(VM86)) return 1;
if (func<0) break;
- ret=vm86plus(func,&VM86);
- if (write(1,&ret,sizeof(ret))!=sizeof(ret)) return 1;
- if (write(1,&VM86,sizeof(VM86))!=sizeof(VM86)) return 1;
+ switch (func) {
+ case DOSMOD_SET_TIMER:
+ if (read(0,&tim,sizeof(tim))!=sizeof(tim)) return 1;
+ set_timer(&tim);
+ /* no response */
+ break;
+ case DOSMOD_ENTER:
+ default:
+ if (read(0,&VM86,sizeof(VM86))!=sizeof(VM86)) return 1;
+ if (sig_pend) ret=DOSMOD_SIGNAL; else
+ ret=vm86plus(func,&VM86);
+ if (write(1,&ret,sizeof(ret))!=sizeof(ret)) return 1;
+ if (write(1,&VM86,sizeof(VM86))!=sizeof(VM86)) return 1;
+ switch (ret&0xff) {
+ case DOSMOD_SIGNAL:
+ ret=sig_pend; sig_pend=0;
+ if (write(1,&ret,sizeof(ret))!=sizeof(ret)) return 1;
+ if (sig_fatal) return 1;
+ break;
+ }
+ }
} while (1);
return 0;
}
-#else /* !linux */
+#else /* !linux-i386 */
int main(void) {return 1;}
#endif
diff --git a/loader/dos/dosmod.h b/loader/dos/dosmod.h
new file mode 100644
index 0000000..6545620
--- /dev/null
+++ b/loader/dos/dosmod.h
@@ -0,0 +1,9 @@
+#ifndef __WINE_DOSMOD_H
+#define __WINE_DOSMOD_H
+
+#define DOSMOD_ENTER 0x01 /* VM86_ENTER */
+#define DOSMOD_SET_TIMER 0x10
+
+#define DOSMOD_SIGNAL 0x00 /* VM86_SIGNAL */
+
+#endif
diff --git a/loader/dos/dosvm.c b/loader/dos/dosvm.c
index 0ade52a..989b6a9 100644
--- a/loader/dos/dosvm.c
+++ b/loader/dos/dosvm.c
@@ -28,6 +28,7 @@
#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;
@@ -37,7 +38,7 @@
#include <sys/mman.h>
#include <sys/vm86.h>
-static void DOSVM_Dump( LPDOSTASK lpDosTask, int fn,
+static void DOSVM_Dump( LPDOSTASK lpDosTask, int fn, int sig,
struct vm86plus_struct*VM86 )
{
unsigned iofs;
@@ -46,7 +47,7 @@
switch (VM86_TYPE(fn)) {
case VM86_SIGNAL:
- printf("Trapped signal\n"); break;
+ printf("Trapped signal %d\n",sig); break;
case VM86_UNKNOWN:
printf("Trapped unhandled GPF\n"); break;
case VM86_INTx:
@@ -89,13 +90,26 @@
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,
+static int DOSVM_Process( LPDOSTASK lpDosTask, int fn, int sig,
struct vm86plus_struct*VM86 )
{
SIGCONTEXT sigcontext;
@@ -121,15 +135,31 @@
switch (VM86_TYPE(fn)) {
case VM86_SIGNAL:
- DOSVM_Dump(lpDosTask,fn,VM86);
- ret=-1; break;
+ 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,VM86);
+ DOSVM_Dump(lpDosTask,fn,sig,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;
+ 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:
@@ -138,7 +168,7 @@
if (ctx_debug_call) ctx_debug_call(SIGTRAP,&context);
break;
default:
- DOSVM_Dump(lpDosTask,fn,VM86);
+ DOSVM_Dump(lpDosTask,fn,sig,VM86);
ret=-1;
}
@@ -154,7 +184,7 @@
NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
LPDOSTASK lpDosTask;
struct vm86plus_struct VM86;
- int stat,len;
+ int stat,len,sig;
fd_set readfds,exceptfds;
GlobalUnlock16( GetCurrentTask() );
@@ -199,6 +229,7 @@
/* 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);
@@ -241,8 +272,20 @@
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,&VM86)>=0);
+ } while (DOSVM_Process(lpDosTask,stat,sig,&VM86)>=0);
if (context) {
#define CP(x,y) y##_reg(context) = VM86.regs.x
diff --git a/loader/dos/module.c b/loader/dos/module.c
index c0b5f47..df644ae 100644
--- a/loader/dos/module.c
+++ b/loader/dos/module.c
@@ -15,6 +15,7 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/time.h>
#include "windows.h"
#include "winbase.h"
#include "module.h"
@@ -26,6 +27,7 @@
#include "miscemu.h"
#include "debug.h"
#include "dosexe.h"
+#include "dosmod.h"
#include "options.h"
#ifdef MZ_SUPPORTED
@@ -68,6 +70,7 @@
/* FIXME: integrate the PDB stuff from Wine (loader/task.c) */
}
+/* default INT 08 handler: increases timer tick counter but not much more */
static char int08[]={
0xCD,0x1C, /* int $0x1c */
0x50, /* pushw %ax */
@@ -92,7 +95,10 @@
WORD seg;
LPBYTE start=DOSMEM_GetBlock(lpDosTask->hModule,sizeof(int08),&seg);
memcpy(start,int08,sizeof(int08));
+/* INT 08: point it at our tick-incrementing handler */
((SEGPTR*)(lpDosTask->img))[0x08]=PTR_SEG_OFF_TO_SEGPTR(seg,0);
+/* INT 1C: just point it to IRET, we don't want to handle it ourselves */
+ ((SEGPTR*)(lpDosTask->img))[0x1C]=PTR_SEG_OFF_TO_SEGPTR(seg,sizeof(int08)-1);
}
static char enter_xms[]={
@@ -201,6 +207,7 @@
/* initialize the memory */
TRACE(module,"Initializing DOS memory structures\n");
DOSMEM_Init(lpDosTask->hModule);
+ MZ_InitHandlers(lpDosTask);
MZ_InitXMS(lpDosTask);
MZ_InitDPMI(lpDosTask);
return lpDosTask->hModule;
@@ -268,7 +275,7 @@
if (mz_header.e_crlc) {
/* load relocation table */
- TRACE(module,"loading DOS EXE relocation table, %d entries\n",mz_header.e_lfarlc);
+ TRACE(module,"loading DOS EXE relocation table, %d entries\n",mz_header.e_crlc);
/* FIXME: is this too slow without read buffering? */
_llseek16(hFile,mz_header.e_lfarlc,FILE_BEGIN);
for (x=0; x<mz_header.e_crlc; x++) {
@@ -308,6 +315,26 @@
return lpDosTask;
}
+static void MZ_InitTimer( LPDOSTASK lpDosTask, int ver )
+{
+ if (ver<1) {
+#if 0
+ /* start simulated system 55Hz timer */
+ lpDosTask->system_timer = CreateSystemTimer( 55, MZ_Tick );
+ TRACE(module,"created 55Hz timer tick, handle=%d\n",lpDosTask->system_timer);
+#endif
+ } else {
+ int func;
+ struct timeval tim;
+
+ /* start dosmod timer at 55Hz */
+ func=DOSMOD_SET_TIMER;
+ tim.tv_sec=0; tim.tv_usec=54925;
+ write(lpDosTask->write_pipe,&func,sizeof(func));
+ write(lpDosTask->write_pipe,&tim,sizeof(tim));
+ }
+}
+
int MZ_InitTask( LPDOSTASK lpDosTask )
{
int read_fd[2],write_fd[2];
@@ -355,6 +382,8 @@
/* the child has now mmaped the temp file, it's now safe to unlink.
* do it here to avoid leaving a mess in /tmp if/when Wine crashes... */
if (lpDosTask->mm_name[0]!=0) unlink(lpDosTask->mm_name);
+ /* start simulated system timer */
+ MZ_InitTimer(lpDosTask,ret);
/* all systems are now go */
} else {
/* child process */
@@ -362,6 +391,8 @@
/* put our pipes somewhere dosmod can find them */
dup2(write_fd[0],0); /* stdin */
dup2(read_fd[1],1); /* stdout */
+ /* enable signals */
+ SIGNAL_MaskAsyncEvents(FALSE);
/* now load dosmod */
execlp("dosmod",fname,farg,NULL);
execl("dosmod",fname,farg,NULL);
@@ -377,9 +408,6 @@
ERR(module,"Failed to spawn dosmod, error=%s\n",strerror(errno));
exit(1);
}
- /* start simulated system 55Hz timer */
- lpDosTask->system_timer = CreateSystemTimer( 55, MZ_Tick );
- TRACE(module,"created 55Hz timer tick, handle=%d\n",lpDosTask->system_timer);
return lpDosTask->hModule;
}
@@ -447,7 +475,9 @@
void MZ_KillModule( LPDOSTASK lpDosTask )
{
TRACE(module,"killing DOS task\n");
+#if 0
SYSTEM_KillSystemTimer(lpDosTask->system_timer);
+#endif
if (lpDosTask->mm_name[0]!=0) {
munmap(lpDosTask->img,0x110000-START_OFFSET);
close(lpDosTask->mm_fd);