|  | /* | 
|  | * DOS Virtual Machine | 
|  | * | 
|  | * Copyright 1998 Ove Kåven | 
|  | */ | 
|  |  | 
|  | #if defined(linux) && defined(__i386__) | 
|  |  | 
|  | #include "config.h" | 
|  |  | 
|  | /* apparently ELF images are usually loaded high anyway */ | 
|  | #ifndef __ELF__ | 
|  | /* if not, force dosmod at high addresses */ | 
|  | asm(".org 0x110000"); | 
|  | #endif /* __ELF__ */ | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <errno.h> | 
|  | #include <unistd.h> | 
|  | #include <fcntl.h> | 
|  | #include <signal.h> | 
|  | #include <sys/stat.h> | 
|  | #ifdef HAVE_SYS_MMAN_H | 
|  | # include <sys/mman.h> | 
|  | #endif | 
|  | #ifdef HAVE_SYS_VM86_H | 
|  | # include <sys/vm86.h> | 
|  | #endif | 
|  | #include <sys/time.h> | 
|  | #include <sys/types.h> | 
|  | #include <sys/ptrace.h> | 
|  | #ifdef HAVE_SYS_WAIT_H | 
|  | # include <sys/wait.h> | 
|  | #endif | 
|  | #include "dosmod.h" | 
|  |  | 
|  | /* FIXME: hack because libc vm86 may be the old syscall version */ | 
|  |  | 
|  | #define SYS_vm86   166 | 
|  |  | 
|  | static inline int vm86plus( int func, struct vm86plus_struct *ptr ) | 
|  | { | 
|  | int res; | 
|  | #ifdef __PIC__ | 
|  | __asm__ __volatile__( "pushl %%ebx\n\t" | 
|  | "movl %2,%%ebx\n\t" | 
|  | "int $0x80\n\t" | 
|  | "popl %%ebx" | 
|  | : "=a" (res) | 
|  | : "0" (SYS_vm86), | 
|  | "g" (func), | 
|  | "c" (ptr) ); | 
|  | #else | 
|  | __asm__ __volatile__("int $0x80" | 
|  | : "=a" (res) | 
|  | : "0" (SYS_vm86), | 
|  | "b" (func), | 
|  | "c" (ptr) ); | 
|  | #endif  /* __PIC__ */ | 
|  | if (res >= 0) return res; | 
|  | errno = -res; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int XREAD(int fd,void*buf,int size) { | 
|  | int res; | 
|  |  | 
|  | while (1) { | 
|  | res = read(fd, buf, size); | 
|  | if (res==size) | 
|  | return res; | 
|  | if (res==-1) { | 
|  | if (errno==EINTR) | 
|  | continue; | 
|  | perror("dosmod read"); | 
|  | return -1; | 
|  | } | 
|  | if (res) /* don't print the EOF condition */ | 
|  | fprintf(stderr,"dosmod read only %d of %d bytes.\n",res,size); | 
|  | return res; | 
|  | } | 
|  | } | 
|  | int XWRITE(int fd,void*buf,int size) { | 
|  | int res; | 
|  |  | 
|  | while (1) { | 
|  | res = write(fd, buf, size); | 
|  | if (res==size) | 
|  | return res; | 
|  | if (res==-1) { | 
|  | if (errno==EINTR) | 
|  | continue; | 
|  | perror("dosmod write"); | 
|  | return -1; | 
|  | } | 
|  | fprintf(stderr,"dosmod write only %d of %d bytes.\n",res,size); | 
|  | return res; | 
|  | } | 
|  | } | 
|  |  | 
|  | void set_timer(struct timeval*tim) | 
|  | { | 
|  | struct itimerval cur; | 
|  |  | 
|  | cur.it_interval=*tim; | 
|  | cur.it_value=*tim; | 
|  | setitimer(ITIMER_REAL,&cur,NULL); | 
|  | } | 
|  |  | 
|  | void get_timer(struct timeval*tim) | 
|  | { | 
|  | struct itimerval cur; | 
|  |  | 
|  | getitimer(ITIMER_REAL,&cur); | 
|  | *tim=cur.it_value; | 
|  | } | 
|  |  | 
|  | volatile int sig_pend,sig_fatal=0,sig_alrm=0,sig_cb=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) | 
|  | { | 
|  | if (sig!=SIGTERM) { | 
|  | 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); | 
|  | } | 
|  |  | 
|  | void alarm_handler(int sig) | 
|  | { | 
|  | sig_alrm++; | 
|  | signal(sig,alarm_handler); | 
|  | } | 
|  |  | 
|  | void cb_handler(int sig) | 
|  | { | 
|  | sig_cb++; | 
|  | signal(sig,cb_handler); | 
|  | } | 
|  |  | 
|  | int send_signal(void) | 
|  | { | 
|  | int ret=sig_pend; sig_pend=0; | 
|  | if (!ret) { | 
|  | if (sig_alrm) { | 
|  | ret=SIGALRM; sig_alrm--; | 
|  | } else { | 
|  | ret=SIGUSR2; sig_cb--; | 
|  | } | 
|  | } | 
|  | if (XWRITE(1,&ret,sizeof(ret))!=sizeof(ret)) return 1; | 
|  | if (sig_fatal) return 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int send_enter_reply(int ret) | 
|  | { | 
|  | if (XWRITE(1,&ret,sizeof(ret))!=sizeof(ret)) return 1; | 
|  | if (XWRITE(1,&VM86,sizeof(VM86))!=sizeof(VM86)) return 1; | 
|  | switch (ret&0xff) { | 
|  | case DOSMOD_SIGNAL: | 
|  | return send_signal(); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int main(int argc,char**argv) | 
|  | { | 
|  | int mfd=open(argv[0],O_RDWR); | 
|  | struct timeval tim; | 
|  | mprot_info mpr; | 
|  | int func,ret,idle; | 
|  | off_t fofs=0; | 
|  | pid_t ppid=getppid(); | 
|  |  | 
|  | /* fprintf(stderr,"main is at %08lx, file is %s, fd=%d\n",(unsigned long)&main,argv[0],mfd); */ | 
|  | if (mfd<0) return 1; | 
|  | /* Map in our DOS image at the start of the process address space */ | 
|  | if (argv[1]) { | 
|  | /* Ulrich Weigand suggested mapping in the DOS image directly from the Wine | 
|  | address space */ | 
|  | fofs=atol(argv[1]); | 
|  | /* 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 */ | 
|  | 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); | 
|  | if (argv[1]) { | 
|  | ptrace(PTRACE_DETACH,ppid,0,0); | 
|  | kill(ppid,SIGCONT); | 
|  | } | 
|  | if (img==(void*)-1) { | 
|  | fprintf(stderr,"DOS memory map failed, error=%s\n",strerror(errno)); | 
|  | fprintf(stderr,"in attempt to map %s, offset %08lX, length 110000, to offset 0\n",argv[0],fofs); | 
|  | return 1; | 
|  | } | 
|  | /* initialize signals and system timer */ | 
|  | signal(SIGHUP,sig_handler); | 
|  | signal(SIGINT,sig_handler); | 
|  | signal(SIGUSR1,sig_handler); | 
|  | signal(SIGUSR2,cb_handler); | 
|  | signal(SIGALRM,alarm_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=3; /* dosmod protocol revision */ | 
|  | XWRITE(1,&ret,sizeof(ret)); | 
|  | /* context exchange loop */ | 
|  | idle=0; | 
|  | do { | 
|  | if (idle) { | 
|  | while (1) { | 
|  | int res; | 
|  | /* parent is idle, transmit any signals (particularly SIGALRM) */ | 
|  | if (sig_pend||sig_alrm||sig_cb) { | 
|  | ret=DOSMOD_SIGNAL; | 
|  | if (XWRITE(1,&ret,sizeof(ret))!=sizeof(ret)) return 1; | 
|  | ret=send_signal(); | 
|  | if (ret) return ret; | 
|  | break; | 
|  | } | 
|  | res = read(0,&func,sizeof(func)); | 
|  | if (res==sizeof(func)) | 
|  | break; | 
|  | if (res==-1) { | 
|  | if (errno==EINTR) | 
|  | continue; | 
|  | perror("dosmod read"); | 
|  | return 1; | 
|  | } | 
|  | if (res) /* don't print the EOF condition */ | 
|  | fprintf(stderr,"dosmod read only %d of %d bytes.\n",res,sizeof(func)); | 
|  | return 1; | 
|  | } | 
|  | ret=DOSMOD_LEFTIDLE; | 
|  | if (XWRITE(1,&ret,sizeof(ret))!=sizeof(ret)) return 1; | 
|  | idle--; | 
|  | } else | 
|  | if (XREAD(0,&func,sizeof(func))!=sizeof(func)) return 1; | 
|  | if (func<0) break; | 
|  | switch (func) { | 
|  | case DOSMOD_SET_TIMER: /* rev 1 */ | 
|  | if (XREAD(0,&tim,sizeof(tim))!=sizeof(tim)) return 1; | 
|  | set_timer(&tim); | 
|  | /* no response */ | 
|  | break; | 
|  | case DOSMOD_GET_TIMER: /* rev 1 */ | 
|  | get_timer(&tim); | 
|  | if (XWRITE(1,&tim,sizeof(tim))!=sizeof(tim)) return 1; | 
|  | break; | 
|  | case DOSMOD_MPROTECT: /* rev 3 */ | 
|  | if (XREAD(0,&mpr,sizeof(mpr))!=sizeof(mpr)) return 1; | 
|  | mprotect(mpr.addr,mpr.len,mpr.prot); | 
|  | /* no response */ | 
|  | break; | 
|  | case DOSMOD_ENTERIDLE: /* rev 3 */ | 
|  | idle++; | 
|  | break; | 
|  | case DOSMOD_LEAVEIDLE: /* rev 3 */ | 
|  | break; | 
|  | case DOSMOD_ENTER: /* rev 0 */ | 
|  | default: | 
|  | if (XREAD(0,&VM86,sizeof(VM86))!=sizeof(VM86)) return 1; | 
|  | if (sig_pend||sig_alrm||sig_cb) ret=DOSMOD_SIGNAL; else | 
|  | ret=vm86plus(func,&VM86); | 
|  |  | 
|  | ret=send_enter_reply(ret); | 
|  | if (ret) return ret; | 
|  | } | 
|  | } while (1); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #else /* !linux-i386 */ | 
|  | int main(void) {return 1;} | 
|  | #endif |