Subject: Re: port-i386/825: no support for Virtual 8086 mode
To: None <gnats-admin@NetBSD.ORG, netbsd-bugs@NetBSD.ORG, gnats-bugs@NetBSD.ORG>
From: John Kohl <jtk@kolvir.blrc.ma.us>
List: netbsd-bugs
Date: 03/07/1995 22:55:53
Here are updated diffs that:

(a) use sigreturn() to enter VM86 mode [no more need for the new gate
    and new sysarch() function]
(b) define new macros USERMODE and KERNELMODE, which look at PSL_VM if
    usermode isn't set.
(c) unifies the trapframe and intrframe structures to use same
    substructure (may be needed if other places need to examine
    USERMODE/KERNELMODE on an interrupt frame rather than a trap frame)
(d) squeezes in fs/gs segment selectors into struct sigcontext.  [We can
    do this becuase the selectors are 16 bits--we don't need 32 bits for
    %es and %ds].  This is kind of gross, but it maintains binary
    compatibility with NetBSD 1.0 signal handling code/programs.
(e) If running on a kernel with VM86 mode enabled, all processes have an
    extra 4 longwords at the top of kstack for use when returning to
    VM86 mode.

All the kernel stuff is conditional on 'options VM86'.

Again, a sample (stupid) program is at the end.

===================================================================
RCS file: include/RCS/cpu.h,v
retrieving revision 1.1
diff -ubw -r1.1 include/cpu.h
--- 1.1	1995/02/25 20:21:33
+++ include/cpu.h	1995/03/06 02:40:25
@@ -68,7 +68,12 @@
  */
 #define clockframe intrframe
 
+#ifdef VM86
+#define	CLKF_USERMODE(frame)	(ISPL((frame)->if_cs) == SEL_UPL || ((frame)->if_eflags & PSL_VM))
+#else
 #define	CLKF_USERMODE(frame)	(ISPL((frame)->if_cs) == SEL_UPL)
+#endif
+
 #define	CLKF_BASEPRI(frame)	((frame)->if_ppl == 0)
 #define	CLKF_PC(frame)		((frame)->if_eip)
 #define	CLKF_INTR(frame)	(0)	/* XXX should have an interrupt stack */
===================================================================
RCS file: include/RCS/frame.h,v
retrieving revision 1.1
diff -ubw -r1.1 include/frame.h
--- 1.1	1995/02/25 22:27:19
+++ include/frame.h	1995/03/06 02:52:22
@@ -38,6 +38,7 @@
  *	@(#)frame.h	5.2 (Berkeley) 1/18/91
  */
 
+#ifndef LOCORE
 #include <sys/signal.h>
 
 /*
@@ -71,24 +72,29 @@
 /* Interrupt stack frame */
 struct intrframe {
 	int	if_ppl;
-	int	if_es;
-	int	if_ds;
-	int	if_edi;
-	int	if_esi;
-	int	if_ebp;
-	int	if_ebx;
-	int	if_edx;
-	int	if_ecx;
-	int	if_eax;
-	int	:32;		/* for compat with trap frame - trapno */
-	int	:32;		/* for compat with trap frame - err */
-	/* below portion defined in 386 hardware */
-	int	if_eip;
-	int	if_cs;
-	int	if_eflags;
-	/* below only when transitting rings (e.g. user to kernel) */
-	int	if_esp;
-	int	if_ss;
+	struct trapframe if_tf;
+#define if_es if_tf.tf_es
+#define if_ds if_tf.tf_ds
+#define if_edi if_tf.tf_edi
+#define if_esi if_tf.tf_esi
+#define if_ebp if_tf.tf_ebp
+#define if_ebx if_tf.tf_ebx
+#define if_edx if_tf.tf_edx
+#define if_ecx if_tf.tf_ecx
+#define if_eax if_tf.tf_eax
+#define if_eip if_tf.tf_eip
+#define if_cs if_tf.tf_cs
+#define if_eflags if_tf.tf_eflags
+#define if_esp if_tf.tf_esp
+#define if_ss if_tf.tf_ss
+};
+
+struct vm86_frame {
+    struct trapframe tframe;
+    int vm_es;
+    int vm_ds;
+    int vm_fs;
+    int vm_gs;
 };
 
 /*
@@ -101,3 +107,6 @@
 	sig_t	sf_handler;
 	struct	sigcontext sf_sc;
 };
+#endif /* LOCORE */
+
+#define	VM86_EXTRA_FRAMESIZE	(4*sizeof(long)) /* extra size for VM86 */
===================================================================
RCS file: include/RCS/signal.h,v
retrieving revision 1.1
diff -ubw -r1.1 include/signal.h
--- 1.1	1995/03/07 03:43:30
+++ include/signal.h	1995/03/07 06:25:14
@@ -57,8 +57,10 @@
 	int	sc_onstack;		/* sigstack state to restore */
 	int	sc_mask;		/* signal mask to restore */
 
-	int	sc_es;
-	int	sc_ds;
+	short	sc_es;
+	short	sc_fs;			/* MBZ if not VM86 */
+	short	sc_ds;
+	short	sc_gs;			/* MBZ if not VM86 */
 	int	sc_edi;
 	int	sc_esi;
 	int	sc_ebp;
===================================================================
RCS file: isa/RCS/icu.s,v
retrieving revision 1.1
diff -ubw -r1.1 isa/icu.s
--- 1.1	1995/02/26 04:15:16
+++ isa/icu.s	1995/03/06 02:44:28
@@ -132,8 +132,12 @@
 	cmpb	$0,_astpending
 	je	3f
 	testb   $SEL_RPL_MASK,TF_CS(%esp)
+#ifdef VM86
+	jnz	4f
+	testl	$PSL_VM,TF_EFLAGS(%esp)
+#endif
 	jz	3f
-	movb	$0,_astpending
+4:	movb	$0,_astpending
 	sti
 	/* Pushed T_ASTFLT into tf_trapno on entry. */
 	call	_trap
===================================================================
RCS file: isa/RCS/npx.c,v
retrieving revision 1.1
diff -ubw -r1.1 isa/npx.c
--- 1.1	1995/02/25 20:21:56
+++ isa/npx.c	1995/03/06 03:37:59
@@ -419,7 +419,7 @@
 	 * Pass exception to process.  If it's the current process, try to do
 	 * it immediately.
 	 */
-	if (p == curproc && ISPL(frame->if_cs) == SEL_UPL) {
+	if (p == curproc && USERMODE(&frame->if_tf)) {
 		/*
 		 * Interrupt is essentially a trap, so we can afford to call
 		 * the SIGFPE handler (if any) as soon as the interrupt
===================================================================
RCS file: i386/RCS/db_interface.c,v
retrieving revision 1.2
diff -ubw -r1.2 i386/db_interface.c
--- 1.2	1995/01/30 02:58:58
+++ i386/db_interface.c	1995/03/08 03:33:46
@@ -79,7 +79,7 @@
 	/* XXX Should switch to kdb`s own stack here. */
 
 	ddb_regs = *regs;
-	if (ISPL(regs->tf_cs) == SEL_KPL) {
+	if (KERNELMODE(regs)) {
 		/*
 		 * Kernel mode - esp and ss not saved
 		 */
@@ -107,7 +107,7 @@
 	regs->tf_eip    = ddb_regs.tf_eip;
 	regs->tf_cs     = ddb_regs.tf_cs;
 	regs->tf_eflags = ddb_regs.tf_eflags;
-	if (ISPL(regs->tf_cs) != SEL_KPL) {
+	if (!KERNELMODE(regs)) {
 		/* ring transit - saved esp and ss valid */
 		regs->tf_esp    = ddb_regs.tf_esp;
 		regs->tf_ss     = ddb_regs.tf_ss;
===================================================================
RCS file: i386/RCS/locore.s,v
retrieving revision 1.1
diff -ubw -r1.1 i386/locore.s
--- 1.1	1995/02/26 04:18:17
+++ i386/locore.s	1995/03/08 03:34:36
@@ -1973,8 +1973,12 @@
 	cmpb	$0,_astpending
 	je	1f
 	testb	$SEL_RPL_MASK,TF_CS(%esp)
+#ifdef VM86
+	jnz	5f
+	testl	$PSL_VM,TF_EFLAGS(%esp)
+#endif
 	jz	1f
-	movb	$0,_astpending
+5:	movb	$0,_astpending
 	sti
 	movl	$T_ASTFLT,TF_TRAPNO(%esp)
 	call	_trap
===================================================================
RCS file: i386/RCS/machdep.c,v
retrieving revision 1.1.1.3
diff -ubw -r1.1.1.3 i386/machdep.c
--- 1.1.1.3	1995/03/07 02:40:12
+++ i386/machdep.c	1995/03/08 03:25:58
@@ -468,6 +468,9 @@
 {
 	register struct proc *p = curproc;
 	register struct trapframe *tf;
+#ifdef VM86
+	register struct vm86_frame *vf;
+#endif
 	struct sigframe *fp, frame;
 	struct sigacts *psp = p->p_sigacts;
 	int oonstack;
@@ -521,8 +524,21 @@
 	 */
 	frame.sf_sc.sc_onstack = oonstack;
 	frame.sf_sc.sc_mask = mask;
+#ifdef VM86
+	if (tf->tf_eflags & PSL_VM) {
+	    vf = (struct vm86_frame *)tf;
+	    frame.sf_sc.sc_es     = vf->vm_es;
+	    frame.sf_sc.sc_fs     = vf->vm_fs;
+	    frame.sf_sc.sc_ds     = vf->vm_ds;
+	    frame.sf_sc.sc_gs     = vf->vm_gs;
+	} else
+#endif
+	{
 	frame.sf_sc.sc_es     = tf->tf_es;
+	    frame.sf_sc.sc_fs     = 0;
 	frame.sf_sc.sc_ds     = tf->tf_ds;
+	    frame.sf_sc.sc_gs     = 0;
+	}
 	frame.sf_sc.sc_edi    = tf->tf_edi;
 	frame.sf_sc.sc_esi    = tf->tf_esi;
 	frame.sf_sc.sc_ebp    = tf->tf_ebp;
@@ -555,6 +571,11 @@
 	tf->tf_ds = _udatasel;
 	tf->tf_es = _udatasel;
 	tf->tf_ss = _udatasel;
+	/* when we got out of VM86 mode, the fs & gs selectors were set to
+	   zero by the processor (or so says the i386 book I have!).
+	   see also the locore switching routines--%fs and %gs are handled
+	   there--though for trapping from vm86 mode,
+	   they should always be zero, */
 }
 
 /*
@@ -576,6 +597,10 @@
 {
 	struct sigcontext *scp, context;
 	register struct trapframe *tf;
+#ifdef VM86
+	register struct vm86_frame *vf;
+	vf = (struct vm86_frame *)p->p_md.md_regs;
+#endif
 
 	tf = (struct trapframe *)p->p_md.md_regs;
 
@@ -591,9 +616,22 @@
 	/*
 	 * Check for security violations.
 	 */
+#ifdef VM86
+	/* if turning on PSL_VM (vm86 mode), allow code selector
+	   to be whatever. */
+	if (((context.sc_eflags ^ tf->tf_eflags) & PSL_USERSTATIC) != 0 ||
+	    (ISPL(context.sc_cs) != SEL_UPL && !(context.sc_eflags & PSL_VM)))
+		return (EINVAL);
+
+	/* If _not_ returning to PSL_VM, non-code selectors are
+	   validated by processor before loading the associated
+	   descriptor, and a fault occurs if a security violation is
+	   attempted. */
+#else
 	if (((context.sc_eflags ^ tf->tf_eflags) & PSL_USERSTATIC) != 0 ||
 	    ISPL(context.sc_cs) != SEL_UPL)
 		return (EINVAL);
+#endif
 
 	if (context.sc_onstack & 01)
 		p->p_sigacts->ps_sigstk.ss_flags |= SA_ONSTACK;
@@ -604,8 +642,20 @@
 	/*
 	 * Restore signal context.
 	 */
+#ifdef VM86
+	if (context.sc_eflags & PSL_VM) {
+	    vf->vm_es = context.sc_es;
+	    vf->vm_fs = context.sc_fs;
+	    vf->vm_ds = context.sc_ds;
+	    vf->vm_gs = context.sc_gs;
+	    tf->tf_es     = 0;
+	    tf->tf_ds     = 0;		/* set by VM86 part of frame */
+	} else
+#endif
+        {
 	tf->tf_es     = context.sc_es;
 	tf->tf_ds     = context.sc_ds;
+	}
 	tf->tf_edi    = context.sc_edi;
 	tf->tf_esi    = context.sc_esi;
 	tf->tf_ebp    = context.sc_ebp;
@@ -1168,7 +1223,12 @@
 	/* now running on new page tables, configured,and u/iom is accessible */
 
 	/* make a initial tss so microp can get interrupt stack on syscall! */
+#ifdef VM86
+	proc0.p_addr->u_pcb.pcb_tss.tss_esp0 = (int) USRSTACK + UPAGES*NBPG
+	    - VM86_EXTRA_FRAMESIZE;
+#else
 	proc0.p_addr->u_pcb.pcb_tss.tss_esp0 = (int) USRSTACK + UPAGES*NBPG;
+#endif
 	proc0.p_addr->u_pcb.pcb_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL);
 	_gsel_tss = GSEL(GPROC0_SEL, SEL_KPL);
 
===================================================================
RCS file: i386/RCS/trap.c,v
retrieving revision 1.1.1.1
diff -ubw -r1.1.1.1 i386/trap.c
--- 1.1.1.1	1995/03/07 02:38:46
+++ i386/trap.c	1995/03/08 03:46:52
@@ -183,7 +183,7 @@
 	}
 #endif
 
-	if (ISPL(frame.tf_cs) != SEL_KPL) {
+	if (!KERNELMODE(&frame)) {
 		type |= T_USER;
 		sticks = p->p_sticks;
 		p->p_md.md_regs = (int *)&frame;
@@ -511,8 +511,9 @@
 	int fromtramp;
 #endif
 
+
 	cnt.v_syscall++;
-	if (ISPL(frame.tf_cs) != SEL_UPL)
+	if (!USERMODE(&frame))
 		panic("syscall");
 	p = curproc;
 	sticks = p->p_sticks;
@@ -567,6 +568,16 @@
 #endif
 	}
 
+#ifdef VM86
+	/*
+	 * VM86 mode application found our syscall trap gate by accident...
+	 * let it get a sigsys() and have the VM86 handler in the process
+	 * take care of it.
+	 */
+	if (frame.tf_eflags & PSL_VM)
+	    code = -1;
+	else
+#endif
 	switch (code) {
 	case SYS_syscall:
 #ifdef COMPAT_LINUX
===================================================================
RCS file: include/RCS/psl.h,v
retrieving revision 1.1
diff -ubw -r1.1 include/psl.h
--- 1.1	1995/03/07 03:44:19
+++ include/psl.h	1995/03/07 06:26:18
@@ -66,7 +66,11 @@
 #define	PSL_MBZ		0xffc08028	/* must be zero bits */
 
 #define	PSL_USERSET	(PSL_MBO | PSL_I)
+#ifdef VM86
+#define	PSL_USERSTATIC	(PSL_MBO | PSL_MBZ | PSL_I | PSL_IOPL | PSL_VIF | PSL_VIP)
+#else
 #define	PSL_USERSTATIC	(PSL_MBO | PSL_MBZ | PSL_I | PSL_IOPL | PSL_VM | PSL_VIF | PSL_VIP)
+#endif
 
 #ifdef KERNEL
 
===================================================================
RCS file: include/RCS/segments.h,v
retrieving revision 1.1
diff -ubw -r1.1 include/segments.h
--- 1.1	1995/03/06 02:45:24
+++ include/segments.h	1995/03/07 06:19:11
@@ -58,6 +58,13 @@
 #define	ISPL(s)	((s)&3)		/* what is the priority level of a selector */
 #define	SEL_KPL	0		/* kernel priority level */	
 #define	SEL_UPL	3		/* user priority level */	
+#ifdef VM86
+#define	USERMODE(trapframe) (ISPL((trapframe)->tf_cs) == SEL_UPL || ((trapframe)->tf_eflags & PSL_VM))
+#define	KERNELMODE(trapframe) (ISPL((trapframe)->tf_cs) == SEL_KPL && !((trapframe)->tf_eflags & PSL_VM))
+#else
+#define	USERMODE(trapframe) (ISPL((trapframe)->tf_cs) == SEL_UPL)
+#define	KERNELMODE(trapframe) (ISPL((trapframe)->tf_cs) == SEL_KPL)
+#endif
 #define	ISLDT(s)	((s)&SEL_LDT)	/* is it local or global */
 #define	SEL_LDT	4		/* local descriptor table */	
 #define	IDXSEL(s)	(((s)>>3) & 0x1fff)		/* index of selector */
--- /dev/null	Tue Mar  7 22:13:05 1995
+++ /var/tmp/Makefile	Tue Mar  7 01:33:36 1995
@@ -0,0 +1,3 @@
+sigill: sigill.c
+#	$(CC) -static -I/usr/include.current -g -o sigill sigill.c -li386
+	$(CC) -static -I/usr/include.current -g -o sigill sigill.c
--- /dev/null	Tue Mar  7 22:13:05 1995
+++ /var/tmp/sigill.c	Tue Mar  7 22:27:20 1995
@@ -0,0 +1,61 @@
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <setjmp.h>
+#include <machine/segments.h>
+#include <machine/sysarch.h>
+#include <machine/psl.h>
+
+jmp_buf jbuf;
+
+void
+sigill(int sig, int code, struct sigcontext *scp)
+{
+    printf("sig %d, code %d, eip %#08lx, eflags=%#08lx\n",
+	   sig, code, scp->sc_eip, scp->sc_eflags);
+    longjmp(jbuf, 1);
+    _exit(2);
+}
+
+unsigned long vmstack[256];
+
+unsigned char ill[] = { 144, 144, 144, /* 0xfb, */ 0xeb, 0xfb /* nop, nop, nop, sti, loop */ };
+
+foo()
+{
+    asm("1: nop; nop; nop");
+    asm("jmp 1b");
+}
+
+main()
+{
+    struct sigcontext vmctx;
+    int rval;
+    int psl;
+
+    signal(SIGILL, sigill);
+    signal(SIGBUS, sigill);
+    signal(SIGQUIT, sigill);
+    memset(&vmctx, 0, sizeof(vmctx));
+
+
+    vmctx.sc_esp = (int) &vmstack[sizeof(vmstack)/4-1];
+    vmctx.sc_eip = (int) &ill[0];
+    vmctx.sc_cs = 0;
+    vmctx.sc_ds = 0;
+    vmctx.sc_es = 0;
+    vmctx.sc_fs = 0;
+    vmctx.sc_gs = 0;
+    __asm("pushfl");
+    __asm("popl %0" : "=r" (psl));
+    vmctx.sc_eflags = psl | PSL_VM;
+
+    if (setjmp(jbuf)) {
+	printf("jumped out of sighandler, exiting\n");
+	exit(2);
+    }
+    rval = sigreturn(&vmctx);
+    printf("returning from vm86 with rval=%d?\n", rval);
+    exit(1);
+}
+