1 /* $OpenBSD: vm86.c,v 1.15 2003/01/09 22:27:09 miod Exp $ */
2 /* $NetBSD: vm86.c,v 1.15 1996/05/03 19:42:33 christos Exp $ */
3
4 /*-
5 * Copyright (c) 1996 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by John T. Kohl and Charles M. Hannum.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the NetBSD
22 * Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
31 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/signalvar.h>
43 #include <sys/kernel.h>
44 #include <sys/proc.h>
45 #include <sys/user.h>
46 #include <sys/exec.h>
47 #include <sys/buf.h>
48 #include <sys/reboot.h>
49 #include <sys/conf.h>
50 #include <sys/file.h>
51 #include <sys/malloc.h>
52 #include <sys/mbuf.h>
53 #include <sys/msgbuf.h>
54 #include <sys/mount.h>
55 #include <sys/vnode.h>
56 #include <sys/device.h>
57 #include <sys/sysctl.h>
58 #include <sys/syscallargs.h>
59 #ifdef SYSVMSG
60 #include <sys/msg.h>
61 #endif
62 #ifdef SYSVSEM
63 #include <sys/sem.h>
64 #endif
65 #ifdef SYSVSHM
66 #include <sys/shm.h>
67 #endif
68
69 #include <sys/ktrace.h>
70 #include <machine/sysarch.h>
71 #include <machine/vm86.h>
72
73 static void fast_intxx(struct proc *, int);
74 static __inline int is_bitset(int, caddr_t);
75
76 #define CS(tf) (*(u_short *)&tf->tf_cs)
77 #define IP(tf) (*(u_short *)&tf->tf_eip)
78 #define SS(tf) (*(u_short *)&tf->tf_ss)
79 #define SP(tf) (*(u_short *)&tf->tf_esp)
80
81
82 #define putword(base, ptr, val) \
83 __asm__ __volatile__( \
84 "decw %w0\n\t" \
85 "movb %h2,0(%1,%0)\n\t" \
86 "decw %w0\n\t" \
87 "movb %b2,0(%1,%0)" \
88 : "=r" (ptr) \
89 : "r" (base), "q" (val), "0" (ptr))
90
91 #define putdword(base, ptr, val) \
92 __asm__ __volatile__( \
93 "rorl $16,%2\n\t" \
94 "decw %w0\n\t" \
95 "movb %h2,0(%1,%0)\n\t" \
96 "decw %w0\n\t" \
97 "movb %b2,0(%1,%0)\n\t" \
98 "rorl $16,%2\n\t" \
99 "decw %w0\n\t" \
100 "movb %h2,0(%1,%0)\n\t" \
101 "decw %w0\n\t" \
102 "movb %b2,0(%1,%0)" \
103 : "=r" (ptr) \
104 : "r" (base), "q" (val), "0" (ptr))
105
106 #define getbyte(base, ptr) \
107 ({ unsigned long __res; \
108 __asm__ __volatile__( \
109 "movb 0(%1,%0),%b2\n\t" \
110 "incw %w0" \
111 : "=r" (ptr), "=r" (base), "=q" (__res) \
112 : "0" (ptr), "1" (base), "2" (0)); \
113 __res; })
114
115 #define getword(base, ptr) \
116 ({ unsigned long __res; \
117 __asm__ __volatile__( \
118 "movb 0(%1,%0),%b2\n\t" \
119 "incw %w0\n\t" \
120 "movb 0(%1,%0),%h2\n\t" \
121 "incw %w0" \
122 : "=r" (ptr), "=r" (base), "=q" (__res) \
123 : "0" (ptr), "1" (base), "2" (0)); \
124 __res; })
125
126 #define getdword(base, ptr) \
127 ({ unsigned long __res; \
128 __asm__ __volatile__( \
129 "movb 0(%1,%0),%b2\n\t" \
130 "incw %w0\n\t" \
131 "movb 0(%1,%0),%h2\n\t" \
132 "incw %w0\n\t" \
133 "rorl $16,%2\n\t" \
134 "movb 0(%1,%0),%b2\n\t" \
135 "incw %w0\n\t" \
136 "movb 0(%1,%0),%h2\n\t" \
137 "incw %w0\n\t" \
138 "rorl $16,%2" \
139 : "=r" (ptr), "=r" (base), "=q" (__res) \
140 : "0" (ptr), "1" (base)); \
141 __res; })
142
143
144 static __inline int
is_bitset(nr,bitmap)145 is_bitset(nr, bitmap)
146 int nr;
147 caddr_t bitmap;
148 {
149 u_int byte; /* bt instruction doesn't do
150 bytes--it examines ints! */
151 bitmap += nr / NBBY;
152 nr = nr % NBBY;
153 copyin(bitmap, &byte, sizeof(u_char));
154
155 __asm__ __volatile__("btl %2,%1\n\tsbbl %0,%0"
156 :"=r" (nr)
157 :"r" (byte),"r" (nr));
158 return (nr);
159 }
160
161
162 #define V86_AH(regs) (((u_char *)&((regs)->tf_eax))[1])
163 #define V86_AL(regs) (((u_char *)&((regs)->tf_eax))[0])
164
165 static void
fast_intxx(p,intrno)166 fast_intxx(p, intrno)
167 struct proc *p;
168 int intrno;
169 {
170 struct trapframe *tf = p->p_md.md_regs;
171 /*
172 * handle certain interrupts directly by pushing the interrupt
173 * frame and resetting registers, but only if user said that's ok
174 * (i.e. not revectored.) Otherwise bump to 32-bit user handler.
175 */
176 struct vm86_struct *u_vm86p;
177 struct { u_short ip, cs; } ihand;
178
179 u_long ss, sp;
180
181 /*
182 * Note: u_vm86p points to user-space, we only compute offsets
183 * and don't deref it. is_revectored() above does copyin() to
184 * get stuff from it
185 */
186 u_vm86p = (struct vm86_struct *)p->p_addr->u_pcb.vm86_userp;
187
188 /*
189 * If user requested special handling, return to user space with
190 * indication of which INT was requested.
191 */
192 if (is_bitset(intrno, &u_vm86p->int_byuser[0]))
193 goto vector;
194
195 /*
196 * If it's interrupt 0x21 (special in the DOS world) and the
197 * sub-command (in AH) was requested for special handling,
198 * return to user mode.
199 */
200 if (intrno == 0x21 && is_bitset(V86_AH(tf), &u_vm86p->int21_byuser[0]))
201 goto vector;
202
203 /*
204 * Fetch intr handler info from "real-mode" IDT based at addr 0 in
205 * the user address space.
206 */
207 if (copyin((caddr_t)(intrno * sizeof(ihand)), &ihand, sizeof(ihand)))
208 goto bad;
209
210 /*
211 * Otherwise, push flags, cs, eip, and jump to handler to
212 * simulate direct INT call.
213 */
214 ss = SS(tf) << 4;
215 sp = SP(tf);
216
217 putword(ss, sp, get_vflags_short(p));
218 putword(ss, sp, CS(tf));
219 putword(ss, sp, IP(tf));
220 SP(tf) = sp;
221
222 IP(tf) = ihand.ip;
223 CS(tf) = ihand.cs;
224
225 return;
226
227 vector:
228 vm86_return(p, VM86_MAKEVAL(VM86_INTx, intrno));
229 return;
230
231 bad:
232 vm86_return(p, VM86_UNKNOWN);
233 return;
234 }
235
236 void
vm86_return(p,retval)237 vm86_return(p, retval)
238 struct proc *p;
239 int retval;
240 {
241 union sigval sv;
242
243 /*
244 * We can't set the virtual flags in our real trap frame,
245 * since it's used to jump to the signal handler. Instead we
246 * let sendsig() pull in the vm86_eflags bits.
247 */
248 if (p->p_sigmask & sigmask(SIGURG)) {
249 #ifdef DIAGNOSTIC
250 printf("pid %d killed on VM86 protocol screwup (SIGURG blocked)\n",
251 p->p_pid);
252 #endif
253 sigexit(p, SIGILL);
254 /* NOTREACHED */
255 }
256 sv.sival_int = 0;
257 trapsignal(p, SIGURG, retval, 0, sv);
258 }
259
260 #define CLI 0xFA
261 #define STI 0xFB
262 #define INTxx 0xCD
263 #define INTO 0xCE
264 #define IRET 0xCF
265 #define OPSIZ 0x66
266 #define INT3 0xCC /* Actually the process gets 32-bit IDT to handle it */
267 #define LOCK 0xF0
268 #define PUSHF 0x9C
269 #define POPF 0x9D
270
271 /*
272 * Handle a GP fault that occurred while in VM86 mode. Things that are easy
273 * to handle here are done here (much more efficient than trapping to 32-bit
274 * handler code and then having it restart VM86 mode).
275 */
276 void
vm86_gpfault(p,type)277 vm86_gpfault(p, type)
278 struct proc *p;
279 int type;
280 {
281 struct trapframe *tf = p->p_md.md_regs;
282 union sigval sv;
283
284 /*
285 * we want to fetch some stuff from the current user virtual
286 * address space for checking. remember that the frame's
287 * segment selectors are real-mode style selectors.
288 */
289 u_long cs, ip, ss, sp;
290 u_char tmpbyte;
291 int trace;
292
293 cs = CS(tf) << 4;
294 ip = IP(tf);
295 ss = SS(tf) << 4;
296 sp = SP(tf);
297
298 trace = tf->tf_eflags & PSL_T;
299
300 /*
301 * For most of these, we must set all the registers before calling
302 * macros/functions which might do a vm86_return.
303 */
304 tmpbyte = getbyte(cs, ip);
305 IP(tf) = ip;
306 switch (tmpbyte) {
307 case CLI:
308 /* simulate handling of IF */
309 clr_vif(p);
310 break;
311
312 case STI:
313 /* simulate handling of IF.
314 * XXX the i386 enables interrupts one instruction later.
315 * code here is wrong, but much simpler than doing it Right.
316 */
317 set_vif(p);
318 break;
319
320 case INTxx:
321 /* try fast intxx, or return to 32bit mode to handle it. */
322 tmpbyte = getbyte(cs, ip);
323 IP(tf) = ip;
324 fast_intxx(p, tmpbyte);
325 break;
326
327 case INTO:
328 if (tf->tf_eflags & PSL_V)
329 fast_intxx(p, 4);
330 break;
331
332 case PUSHF:
333 putword(ss, sp, get_vflags_short(p));
334 SP(tf) = sp;
335 break;
336
337 case IRET:
338 IP(tf) = getword(ss, sp);
339 CS(tf) = getword(ss, sp);
340 case POPF:
341 set_vflags_short(p, getword(ss, sp));
342 SP(tf) = sp;
343 break;
344
345 case OPSIZ:
346 tmpbyte = getbyte(cs, ip);
347 IP(tf) = ip;
348 switch (tmpbyte) {
349 case PUSHF:
350 putdword(ss, sp, get_vflags(p) & ~PSL_VM);
351 SP(tf) = sp;
352 break;
353
354 case IRET:
355 IP(tf) = getdword(ss, sp);
356 CS(tf) = getdword(ss, sp);
357 case POPF:
358 set_vflags(p, getdword(ss, sp) | PSL_VM);
359 SP(tf) = sp;
360 break;
361
362 default:
363 IP(tf) -= 2;
364 goto bad;
365 }
366 break;
367
368 case LOCK:
369 default:
370 IP(tf) -= 1;
371 goto bad;
372 }
373
374 if (trace && tf->tf_eflags & PSL_VM) {
375 sv.sival_int = 0;
376 trapsignal(p, SIGTRAP, T_TRCTRAP, TRAP_TRACE, sv);
377 }
378 return;
379
380 bad:
381 vm86_return(p, VM86_UNKNOWN);
382 return;
383 }
384
385 int
i386_vm86(p,args,retval)386 i386_vm86(p, args, retval)
387 struct proc *p;
388 char *args;
389 register_t *retval;
390 {
391 struct trapframe *tf = p->p_md.md_regs;
392 struct pcb *pcb = &p->p_addr->u_pcb;
393 struct vm86_kern vm86s;
394 int error;
395
396 error = copyin(args, &vm86s, sizeof(vm86s));
397 if (error)
398 return (error);
399
400 pcb->vm86_userp = (void *)args;
401
402 /*
403 * Keep mask of flags we simulate to simulate a particular type of
404 * processor.
405 */
406 switch (vm86s.ss_cpu_type) {
407 case VCPU_086:
408 case VCPU_186:
409 case VCPU_286:
410 pcb->vm86_flagmask = PSL_ID|PSL_AC|PSL_NT|PSL_IOPL;
411 break;
412 case VCPU_386:
413 pcb->vm86_flagmask = PSL_ID|PSL_AC;
414 break;
415 case VCPU_486:
416 pcb->vm86_flagmask = PSL_ID;
417 break;
418 case VCPU_586:
419 pcb->vm86_flagmask = 0;
420 break;
421 default:
422 return (EINVAL);
423 }
424
425 #define DOVREG(reg) tf->tf_vm86_##reg = (u_short) vm86s.regs.vmsc.sc_##reg
426 #define DOREG(reg) tf->tf_##reg = (u_short) vm86s.regs.vmsc.sc_##reg
427
428 DOVREG(ds);
429 DOVREG(es);
430 DOVREG(fs);
431 DOVREG(gs);
432 DOREG(edi);
433 DOREG(esi);
434 DOREG(ebp);
435 DOREG(eax);
436 DOREG(ebx);
437 DOREG(ecx);
438 DOREG(edx);
439 DOREG(eip);
440 DOREG(cs);
441 DOREG(esp);
442 DOREG(ss);
443
444 #undef DOVREG
445 #undef DOREG
446
447 /* Going into vm86 mode jumps off the signal stack. */
448 p->p_sigacts->ps_sigstk.ss_flags &= ~SS_ONSTACK;
449
450 set_vflags(p, vm86s.regs.vmsc.sc_eflags | PSL_VM);
451
452 return (EJUSTRETURN);
453 }
454