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