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