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