diff options
author | kib <kib@FreeBSD.org> | 2011-04-01 11:16:29 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2011-04-01 11:16:29 +0000 |
commit | 7c2eaa21fe792c26e223a2f36a4f5c00fdaf52d8 (patch) | |
tree | 601aa0131bd58a0facc71b1b7c7b138597686fbe /sys/amd64/ia32 | |
parent | 6d6007bc145d7665435f03f2ff2043e14240d09e (diff) | |
download | FreeBSD-src-7c2eaa21fe792c26e223a2f36a4f5c00fdaf52d8.zip FreeBSD-src-7c2eaa21fe792c26e223a2f36a4f5c00fdaf52d8.tar.gz |
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
Diffstat (limited to 'sys/amd64/ia32')
-rw-r--r-- | sys/amd64/ia32/ia32_misc.c | 11 | ||||
-rw-r--r-- | sys/amd64/ia32/ia32_signal.c | 170 | ||||
-rw-r--r-- | sys/amd64/ia32/ia32_sigtramp.S | 37 | ||||
-rw-r--r-- | sys/amd64/ia32/ia32_syscall.c | 53 |
4 files changed, 271 insertions, 0 deletions
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 = ≻ + 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 |