From 7c2eaa21fe792c26e223a2f36a4f5c00fdaf52d8 Mon Sep 17 00:00:00 2001
From: kib <kib@FreeBSD.org>
Date: Fri, 1 Apr 2011 11:16:29 +0000
Subject: Add support for executing the FreeBSD 1/i386 a.out binaries on amd64.

In particular:
- implement compat shims for old stat(2) variants and ogetdirentries(2);
- implement delivery of signals with ancient stack frame layout and
  corresponding sigreturn(2);
- implement old getpagesize(2);
- provide a user-mode trampoline and LDT call gate for lcall $7,$0;
- port a.out image activator and connect it to the build as a module
  on amd64.

The changes are hidden under COMPAT_43.

MFC after:   1 month
---
 sys/amd64/ia32/ia32_misc.c     |  11 +++
 sys/amd64/ia32/ia32_signal.c   | 170 +++++++++++++++++++++++++++++++++++++++++
 sys/amd64/ia32/ia32_sigtramp.S |  37 +++++++++
 sys/amd64/ia32/ia32_syscall.c  |  53 +++++++++++++
 4 files changed, 271 insertions(+)

(limited to 'sys/amd64/ia32')

diff --git a/sys/amd64/ia32/ia32_misc.c b/sys/amd64/ia32/ia32_misc.c
index 2fa1972..5a8a721 100644
--- a/sys/amd64/ia32/ia32_misc.c
+++ b/sys/amd64/ia32/ia32_misc.c
@@ -69,3 +69,14 @@ freebsd32_sysarch(struct thread *td, struct freebsd32_sysarch_args *uap)
 		return (sysarch(td, &uap1));
 	}
 }
+
+#ifdef COMPAT_43
+int
+ofreebsd32_getpagesize(struct thread *td,
+    struct ofreebsd32_getpagesize_args *uap)
+{
+
+	td->td_retval[0] = IA32_PAGE_SIZE;
+	return (0);
+}
+#endif
diff --git a/sys/amd64/ia32/ia32_signal.c b/sys/amd64/ia32/ia32_signal.c
index 29dd01f..2f41870 100644
--- a/sys/amd64/ia32/ia32_signal.c
+++ b/sys/amd64/ia32/ia32_signal.c
@@ -300,6 +300,109 @@ freebsd32_swapcontext(struct thread *td, struct freebsd32_swapcontext_args *uap)
  * frame pointer, it returns to the user
  * specified pc, psl.
  */
+
+#ifdef COMPAT_43
+static void
+ia32_osendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
+{
+	struct ia32_sigframe3 sf, *fp;
+	struct proc *p;
+	struct thread *td;
+	struct sigacts *psp;
+	struct trapframe *regs;
+	int sig;
+	int oonstack;
+
+	td = curthread;
+	p = td->td_proc;
+	PROC_LOCK_ASSERT(p, MA_OWNED);
+	sig = ksi->ksi_signo;
+	psp = p->p_sigacts;
+	mtx_assert(&psp->ps_mtx, MA_OWNED);
+	regs = td->td_frame;
+	oonstack = sigonstack(regs->tf_rsp);
+
+	/* Allocate space for the signal handler context. */
+	if ((td->td_pflags & TDP_ALTSTACK) && !oonstack &&
+	    SIGISMEMBER(psp->ps_sigonstack, sig)) {
+		fp = (struct ia32_sigframe3 *)(td->td_sigstk.ss_sp +
+		    td->td_sigstk.ss_size - sizeof(sf));
+		td->td_sigstk.ss_flags |= SS_ONSTACK;
+	} else
+		fp = (struct ia32_sigframe3 *)regs->tf_rsp - 1;
+
+	/* Translate the signal if appropriate. */
+	if (p->p_sysent->sv_sigtbl && sig <= p->p_sysent->sv_sigsize)
+		sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)];
+
+	/* Build the argument list for the signal handler. */
+	sf.sf_signum = sig;
+	sf.sf_scp = (register_t)&fp->sf_siginfo.si_sc;
+	if (SIGISMEMBER(psp->ps_siginfo, sig)) {
+		/* Signal handler installed with SA_SIGINFO. */
+		sf.sf_arg2 = (register_t)&fp->sf_siginfo;
+		sf.sf_siginfo.si_signo = sig;
+		sf.sf_siginfo.si_code = ksi->ksi_code;
+		sf.sf_ah = (uintptr_t)catcher;
+	} else {
+		/* Old FreeBSD-style arguments. */
+		sf.sf_arg2 = ksi->ksi_code;
+		sf.sf_addr = (register_t)ksi->ksi_addr;
+		sf.sf_ah = (uintptr_t)catcher;
+	}
+	mtx_unlock(&psp->ps_mtx);
+	PROC_UNLOCK(p);
+
+	/* Save most if not all of trap frame. */
+	sf.sf_siginfo.si_sc.sc_eax = regs->tf_rax;
+	sf.sf_siginfo.si_sc.sc_ebx = regs->tf_rbx;
+	sf.sf_siginfo.si_sc.sc_ecx = regs->tf_rcx;
+	sf.sf_siginfo.si_sc.sc_edx = regs->tf_rdx;
+	sf.sf_siginfo.si_sc.sc_esi = regs->tf_rsi;
+	sf.sf_siginfo.si_sc.sc_edi = regs->tf_rdi;
+	sf.sf_siginfo.si_sc.sc_cs = regs->tf_cs;
+	sf.sf_siginfo.si_sc.sc_ds = regs->tf_ds;
+	sf.sf_siginfo.si_sc.sc_ss = regs->tf_ss;
+	sf.sf_siginfo.si_sc.sc_es = regs->tf_es;
+	sf.sf_siginfo.si_sc.sc_fs = regs->tf_fs;
+	sf.sf_siginfo.si_sc.sc_gs = regs->tf_gs;
+	sf.sf_siginfo.si_sc.sc_isp = regs->tf_rsp;
+
+	/* Build the signal context to be used by osigreturn(). */
+	sf.sf_siginfo.si_sc.sc_onstack = (oonstack) ? 1 : 0;
+	SIG2OSIG(*mask, sf.sf_siginfo.si_sc.sc_mask);
+	sf.sf_siginfo.si_sc.sc_esp = regs->tf_rsp;
+	sf.sf_siginfo.si_sc.sc_ebp = regs->tf_rbp;
+	sf.sf_siginfo.si_sc.sc_eip = regs->tf_rip;
+	sf.sf_siginfo.si_sc.sc_eflags = regs->tf_rflags;
+	sf.sf_siginfo.si_sc.sc_trapno = regs->tf_trapno;
+	sf.sf_siginfo.si_sc.sc_err = regs->tf_err;
+
+	/*
+	 * Copy the sigframe out to the user's stack.
+	 */
+	if (copyout(&sf, fp, sizeof(*fp)) != 0) {
+#ifdef DEBUG
+		printf("process %ld has trashed its stack\n", (long)p->p_pid);
+#endif
+		PROC_LOCK(p);
+		sigexit(td, SIGILL);
+	}
+
+	regs->tf_rsp = (uintptr_t)fp;
+	regs->tf_rip = p->p_sysent->sv_psstrings - sz_ia32_osigcode;
+	regs->tf_rflags &= ~(PSL_T | PSL_D);
+	regs->tf_cs = _ucode32sel;
+	regs->tf_ds = _udatasel;
+	regs->tf_es = _udatasel;
+	regs->tf_fs = _udatasel;
+	regs->tf_ss = _udatasel;
+	set_pcb_flags(td->td_pcb, PCB_FULL_IRET);
+	PROC_LOCK(p);
+	mtx_lock(&psp->ps_mtx);
+}
+#endif
+
 #ifdef COMPAT_FREEBSD4
 static void
 freebsd4_ia32_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
@@ -441,6 +544,12 @@ ia32_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
 		return;
 	}
 #endif
+#ifdef COMPAT_43
+	if (SIGISMEMBER(psp->ps_osigset, sig)) {
+		ia32_osendsig(catcher, ksi, mask);
+		return;
+	}
+#endif
 	mtx_assert(&psp->ps_mtx, MA_OWNED);
 	regs = td->td_frame;
 	oonstack = sigonstack(regs->tf_rsp);
@@ -547,6 +656,64 @@ ia32_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
  * make sure that the user has not modified the
  * state to gain improper privileges.
  */
+
+#ifdef COMPAT_43
+int
+ofreebsd32_sigreturn(struct thread *td, struct ofreebsd32_sigreturn_args *uap)
+{
+	struct ia32_sigcontext3 sc, *scp;
+	struct trapframe *regs;
+	int eflags, error;
+	ksiginfo_t ksi;
+
+	regs = td->td_frame;
+	error = copyin(uap->sigcntxp, &sc, sizeof(sc));
+	if (error != 0)
+		return (error);
+	scp = &sc;
+	eflags = scp->sc_eflags;
+	if (!EFL_SECURE(eflags & ~PSL_RF, regs->tf_rflags & ~PSL_RF)) {
+		return (EINVAL);
+	}
+	if (!CS_SECURE(scp->sc_cs)) {
+		ksiginfo_init_trap(&ksi);
+		ksi.ksi_signo = SIGBUS;
+		ksi.ksi_code = BUS_OBJERR;
+		ksi.ksi_trapno = T_PROTFLT;
+		ksi.ksi_addr = (void *)regs->tf_rip;
+		trapsignal(td, &ksi);
+		return (EINVAL);
+	}
+	regs->tf_ds = scp->sc_ds;
+	regs->tf_es = scp->sc_es;
+	regs->tf_fs = scp->sc_fs;
+	regs->tf_gs = scp->sc_gs;
+
+	regs->tf_rax = scp->sc_eax;
+	regs->tf_rbx = scp->sc_ebx;
+	regs->tf_rcx = scp->sc_ecx;
+	regs->tf_rdx = scp->sc_edx;
+	regs->tf_rsi = scp->sc_esi;
+	regs->tf_rdi = scp->sc_edi;
+	regs->tf_cs = scp->sc_cs;
+	regs->tf_ss = scp->sc_ss;
+	regs->tf_rbp = scp->sc_ebp;
+	regs->tf_rsp = scp->sc_esp;
+	regs->tf_rip = scp->sc_eip;
+	regs->tf_rflags = eflags;
+
+	if (scp->sc_onstack & 1)
+		td->td_sigstk.ss_flags |= SS_ONSTACK;
+	else
+		td->td_sigstk.ss_flags &= ~SS_ONSTACK;
+
+	kern_sigprocmask(td, SIG_SETMASK, (sigset_t *)&scp->sc_mask, NULL,
+	    SIGPROCMASK_OLD);
+	set_pcb_flags(td->td_pcb, PCB_FULL_IRET);
+	return (EJUSTRETURN);
+}
+#endif
+
 #ifdef COMPAT_FREEBSD4
 /*
  * MPSAFE
@@ -734,6 +901,9 @@ ia32_setregs(struct thread *td, struct image_params *imgp, u_long stack)
 		user_ldt_free(td);
 	else
 		mtx_unlock(&dt_lock);
+#ifdef COMPAT_43
+	setup_lcall_gate();
+#endif
 
 	pcb->pcb_fsbase = 0;
 	pcb->pcb_gsbase = 0;
diff --git a/sys/amd64/ia32/ia32_sigtramp.S b/sys/amd64/ia32/ia32_sigtramp.S
index 9455169..7d64470 100644
--- a/sys/amd64/ia32/ia32_sigtramp.S
+++ b/sys/amd64/ia32/ia32_sigtramp.S
@@ -66,6 +66,35 @@ freebsd4_ia32_sigcode:
 	jmp	1b
 #endif
 
+#ifdef COMPAT_43
+	ALIGN_TEXT
+ia32_osigcode:
+	calll	*IA32_SIGF_HANDLER(%esp)/* call signal handler */
+	leal	IA32_SIGF_SC(%esp),%eax	/* get sigcontext */
+	pushl	%eax
+	movl	$103,%eax		/* 3.x SYS_sigreturn */
+	pushl	%eax			/* junk to fake return addr. */
+	int	$0x80			/* enter kernel with args */
+1:
+	jmp	1b
+
+
+	ALIGN_TEXT
+lcall_tramp:
+	pushl	%ebp
+	movl	%esp,%ebp
+	pushl	0x24(%ebp) /* arg 6 */
+	pushl	0x20(%ebp)
+	pushl	0x1c(%ebp)
+	pushl	0x18(%ebp)
+	pushl	0x14(%ebp)
+	pushl	0x10(%ebp) /* arg 1 */
+	pushl	0xc(%ebp) /* gap */
+	int	$0x80
+	leave
+	lretl
+#endif
+
 	ALIGN_TEXT
 esigcode:
 
@@ -78,3 +107,11 @@ sz_ia32_sigcode:
 sz_freebsd4_ia32_sigcode:
 	.long	esigcode-freebsd4_ia32_sigcode
 #endif
+#ifdef COMPAT_43
+	.globl	sz_ia32_osigcode
+sz_ia32_osigcode:
+	.long	esigcode-ia32_osigcode
+	.globl	sz_lcall_tramp
+sz_lcall_tramp:
+	.long	esigcode-lcall_tramp
+#endif
diff --git a/sys/amd64/ia32/ia32_syscall.c b/sys/amd64/ia32/ia32_syscall.c
index 9a3488e..0b46dae 100644
--- a/sys/amd64/ia32/ia32_syscall.c
+++ b/sys/amd64/ia32/ia32_syscall.c
@@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
  */
 
 #include "opt_clock.h"
+#include "opt_compat.h"
 #include "opt_cpu.h"
 #include "opt_isa.h"
 
@@ -78,7 +79,17 @@ __FBSDID("$FreeBSD$");
 #include <machine/intr_machdep.h>
 #include <machine/md_var.h>
 
+#include <compat/freebsd32/freebsd32_signal.h>
 #include <compat/freebsd32/freebsd32_util.h>
+#include <compat/ia32/ia32_signal.h>
+#include <machine/psl.h>
+#include <machine/segments.h>
+#include <machine/specialreg.h>
+#include <machine/sysarch.h>
+#include <machine/frame.h>
+#include <machine/md_var.h>
+#include <machine/pcb.h>
+#include <machine/cpufunc.h>
 
 #define	IDTVEC(name)	__CONCAT(X,name)
 
@@ -198,3 +209,45 @@ ia32_syscall_disable(void *dummy)
 
 SYSINIT(ia32_syscall, SI_SUB_EXEC, SI_ORDER_ANY, ia32_syscall_enable, NULL);
 SYSUNINIT(ia32_syscall, SI_SUB_EXEC, SI_ORDER_ANY, ia32_syscall_disable, NULL);
+
+#ifdef COMPAT_43
+int
+setup_lcall_gate(void)
+{
+	struct i386_ldt_args uap;
+	struct user_segment_descriptor descs[2];
+	struct gate_descriptor *ssd;
+	uint32_t lcall_addr;
+	int error;
+
+	bzero(&uap, sizeof(uap));
+	uap.start = 0;
+	uap.num = 2;
+
+	/*
+	 * This is the easiest way to cut the space for system
+	 * descriptor in ldt.  Manually adjust the descriptor type to
+	 * the call gate later.
+	 */
+	bzero(&descs[0], sizeof(descs));
+	descs[0].sd_type = SDT_SYSNULL;
+	descs[1].sd_type = SDT_SYSNULL;
+	error = amd64_set_ldt(curthread, &uap, descs);
+	if (error != 0)
+		return (error);
+
+	lcall_addr = curproc->p_sysent->sv_psstrings - sz_lcall_tramp;
+	mtx_lock(&dt_lock);
+	ssd = (struct gate_descriptor *)(curproc->p_md.md_ldt->ldt_base);
+	bzero(ssd, sizeof(*ssd));
+	ssd->gd_looffset = lcall_addr;
+	ssd->gd_hioffset = lcall_addr >> 16;
+	ssd->gd_selector = _ucode32sel;
+	ssd->gd_type = SDT_SYSCGT;
+	ssd->gd_dpl = SEL_UPL;
+	ssd->gd_p = 1;
+	mtx_unlock(&dt_lock);
+
+	return (0);
+}
+#endif
-- 
cgit v1.1