Separated the MZ loader and core DOS VM into dlls/winedos.
[wine] / loader / dos / dosmod.c
1 /*
2  * DOS Virtual Machine
3  *
4  * Copyright 1998 Ove Kåven
5  */
6
7 #if defined(linux) && defined(__i386__)
8
9 #include "config.h"
10
11 /* apparently ELF images are usually loaded high anyway */
12 #ifndef __ELF__
13 /* if not, force dosmod at high addresses */
14 asm(".org 0x110000");
15 #endif /* __ELF__ */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <errno.h>
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <signal.h>
24 #include <sys/stat.h>
25 #ifdef HAVE_SYS_MMAN_H
26 # include <sys/mman.h>
27 #endif
28 #ifdef HAVE_SYS_VM86_H
29 # include <sys/vm86.h>
30 #endif
31 #include <sys/time.h>
32 #include <sys/types.h>
33 #ifdef HAVE_SYS_PTRACE_H
34 # include <sys/ptrace.h>
35 #endif
36 #ifdef HAVE_SYS_WAIT_H
37 # include <sys/wait.h>
38 #endif
39 #include "dosmod.h"
40
41  /* FIXME: hack because libc vm86 may be the old syscall version */
42
43 #define SYS_vm86   166
44
45 static inline int vm86plus( int func, struct vm86plus_struct *ptr )
46 {
47     int res;
48 #ifdef __PIC__
49     __asm__ __volatile__( "pushl %%ebx\n\t"
50                           "movl %2,%%ebx\n\t"
51                           "int $0x80\n\t"
52                           "popl %%ebx"
53                           : "=a" (res)
54                           : "0" (SYS_vm86),
55                             "g" (func),
56                             "c" (ptr) );
57 #else
58     __asm__ __volatile__("int $0x80"
59                          : "=a" (res)
60                          : "0" (SYS_vm86),
61                            "b" (func),
62                            "c" (ptr) );
63 #endif  /* __PIC__ */
64     if (res >= 0) return res;
65     errno = -res;
66     return -1;
67 }
68
69 int XREAD(int fd,void*buf,int size) {
70  int res;
71
72  while (1) {
73   res = read(fd, buf, size);
74   if (res==size)
75    return res;
76   if (res==-1) {
77    if (errno==EINTR)
78     continue;
79    perror("dosmod read");
80    return -1;
81   }
82   if (res) /* don't print the EOF condition */
83    fprintf(stderr,"dosmod read only %d of %d bytes.\n",res,size);
84   return res;
85  }
86 }
87 int XWRITE(int fd,void*buf,int size) {
88  int res;
89
90  while (1) {
91   res = write(fd, buf, size);
92   if (res==size)
93    return res;
94   if (res==-1) {
95    if (errno==EINTR)
96     continue;
97    perror("dosmod write");
98    return -1;
99   }
100   fprintf(stderr,"dosmod write only %d of %d bytes.\n",res,size);
101   return res;
102  }
103 }
104
105 void set_timer(struct timeval*tim)
106 {
107  struct itimerval cur;
108
109  cur.it_interval=*tim;
110  cur.it_value=*tim;
111  setitimer(ITIMER_REAL,&cur,NULL);
112 }
113
114 void get_timer(struct timeval*tim)
115 {
116  struct itimerval cur;
117
118  getitimer(ITIMER_REAL,&cur);
119  *tim=cur.it_value;
120 }
121
122 volatile int sig_pend,sig_fatal=0,sig_alrm=0,sig_cb=0;
123 void*img;
124 struct vm86plus_struct VM86;
125
126 void sig_handler(int sig)
127 {
128  if (sig_pend) fprintf(stderr,"DOSMOD previous signal %d lost\n",sig_pend);
129  sig_pend=sig;
130  signal(sig,sig_handler);
131 }
132
133 void bad_handler(int sig)
134 {
135  if (sig!=SIGTERM) {
136   fprintf(stderr,"DOSMOD caught fatal signal %d\n",sig);
137   fprintf(stderr,"(Last known VM86 CS:IP was %04x:%04lx)\n",VM86.regs.cs,VM86.regs.eip);
138  }
139  sig_pend=sig; sig_fatal++;
140  signal(sig,bad_handler);
141 }
142
143 void alarm_handler(int sig)
144 {
145  sig_alrm++;
146  signal(sig,alarm_handler);
147 }
148
149 void cb_handler(int sig)
150 {
151  sig_cb++;
152  signal(sig,cb_handler);
153 }
154
155 int send_signal(void)
156 {
157  int ret=sig_pend; sig_pend=0;
158  if (!ret) {
159   if (sig_alrm) {
160    ret=SIGALRM; sig_alrm--;
161   } else {
162    ret=SIGUSR2; sig_cb--;
163   }
164  }
165  if (XWRITE(1,&ret,sizeof(ret))!=sizeof(ret)) return 1;
166  if (sig_fatal) return 1;
167  return 0;
168 }
169
170 int send_enter_reply(int ret)
171 {
172  if (XWRITE(1,&ret,sizeof(ret))!=sizeof(ret)) return 1;
173  if (XWRITE(1,&VM86,sizeof(VM86))!=sizeof(VM86)) return 1;
174  switch (ret&0xff) {
175   case DOSMOD_SIGNAL:
176    return send_signal();
177  }
178  return 0;
179 }
180
181 int main(int argc,char**argv)
182 {
183  int mfd=open(argv[0],O_RDWR);
184  struct timeval tim;
185  mprot_info mpr;
186  int func,ret,idle;
187  off_t fofs=0;
188  pid_t ppid=getppid();
189  
190 /* fprintf(stderr,"main is at %08lx, file is %s, fd=%d\n",(unsigned long)&main,argv[0],mfd); */
191  if (mfd<0) return 1;
192 /* Map in our DOS image at the start of the process address space */
193  if (argv[1]) {
194   /* Ulrich Weigand suggested mapping in the DOS image directly from the Wine
195      address space */
196   fofs=atol(argv[1]);
197   /* linux currently only allows mapping a process memory if it's being ptraced */
198   /* Linus doesn't like it, so this probably won't work in the future */
199   /* it doesn't even work for me right now */
200   kill(ppid,SIGSTOP);
201   ptrace(PTRACE_ATTACH,ppid,0,0);
202   waitpid(ppid,NULL,0);
203  }
204  img=mmap(NULL,0x110000,PROT_EXEC|PROT_READ|PROT_WRITE,MAP_FIXED|MAP_SHARED,mfd,fofs);
205  if (argv[1]) {
206   ptrace(PTRACE_DETACH,ppid,0,0);
207   kill(ppid,SIGCONT);
208  }
209  if (img==(void*)-1) {
210   fprintf(stderr,"DOS memory map failed, error=%s\n",strerror(errno));
211   fprintf(stderr,"in attempt to map %s, offset %08lX, length 110000, to offset 0\n",argv[0],fofs);
212   return 1;
213  }
214 /* initialize signals and system timer */
215  signal(SIGHUP,sig_handler);
216  signal(SIGINT,sig_handler);
217  signal(SIGUSR1,sig_handler);
218  signal(SIGUSR2,cb_handler);
219  signal(SIGALRM,alarm_handler);
220
221  signal(SIGQUIT,bad_handler);
222  signal(SIGILL,bad_handler);
223  signal(SIGBUS,bad_handler);
224  signal(SIGFPE,bad_handler);
225  signal(SIGSEGV,bad_handler);
226  signal(SIGTERM,bad_handler);
227 #if 0
228  tim.tv_sec=0; tim.tv_usec=54925;
229  set_timer(&tim);
230 #endif
231 /* report back to the main program that we're ready */
232  ret=3; /* dosmod protocol revision */
233  XWRITE(1,&ret,sizeof(ret));
234 /* context exchange loop */
235  idle=0;
236  do {
237   if (idle) {
238    while (1) {
239     int res;
240     /* parent is idle, transmit any signals (particularly SIGALRM) */
241     if (sig_pend||sig_alrm||sig_cb) {
242      ret=DOSMOD_SIGNAL;
243      if (XWRITE(1,&ret,sizeof(ret))!=sizeof(ret)) return 1;
244      ret=send_signal();
245      if (ret) return ret;
246      break;
247     }
248     res = read(0,&func,sizeof(func));
249     if (res==sizeof(func))
250      break;
251     if (res==-1) {
252      if (errno==EINTR)
253       continue;
254      perror("dosmod read");
255      return 1;
256     }
257     if (res) /* don't print the EOF condition */
258      fprintf(stderr,"dosmod read only %d of %d bytes.\n",res,sizeof(func));
259     return 1;
260    }
261    ret=DOSMOD_LEFTIDLE;
262    if (XWRITE(1,&ret,sizeof(ret))!=sizeof(ret)) return 1;
263    idle--;
264   } else
265    if (XREAD(0,&func,sizeof(func))!=sizeof(func)) return 1;
266   if (func<0) break;
267   switch (func) {
268    case DOSMOD_SET_TIMER: /* rev 1 */
269     if (XREAD(0,&tim,sizeof(tim))!=sizeof(tim)) return 1;
270     set_timer(&tim);
271     /* no response */
272     break;
273    case DOSMOD_GET_TIMER: /* rev 1 */
274     get_timer(&tim);
275     if (XWRITE(1,&tim,sizeof(tim))!=sizeof(tim)) return 1;
276     break;
277    case DOSMOD_MPROTECT: /* rev 3 */
278     if (XREAD(0,&mpr,sizeof(mpr))!=sizeof(mpr)) return 1;
279     mprotect(mpr.addr,mpr.len,mpr.prot);
280     /* no response */
281     break;
282    case DOSMOD_ENTERIDLE: /* rev 3 */
283     idle++;
284     break;
285    case DOSMOD_LEAVEIDLE: /* rev 3 */
286     break;
287    case DOSMOD_ENTER: /* rev 0 */
288    default:
289     if (XREAD(0,&VM86,sizeof(VM86))!=sizeof(VM86)) return 1;
290     if (sig_pend||sig_alrm||sig_cb) ret=DOSMOD_SIGNAL; else
291      ret=vm86plus(func,&VM86);
292
293     ret=send_enter_reply(ret);
294     if (ret) return ret;
295   }
296  } while (1);
297  return 0;
298 }
299
300 #else /* !linux-i386 */
301 int main(void) {return 1;}
302 #endif