diff --git a/sys/arm64/linux/linux.h b/sys/arm64/linux/linux.h --- a/sys/arm64/linux/linux.h +++ b/sys/arm64/linux/linux.h @@ -243,6 +243,16 @@ #define lsi_band _sifields._sigpoll._band #define lsi_fd _sifields._sigpoll._fd +/* + * This structure is different from the one used by Linux, + * but it doesn't matter - it's not user-accessible. We need + * it instead of the native one because of l_siginfo. + */ +struct l_sigframe { + struct l_siginfo sf_si; + ucontext_t sf_uc; +}; + union l_semun { l_int val; l_uintptr_t buf; diff --git a/sys/arm64/linux/linux_locore.asm b/sys/arm64/linux/linux_locore.asm --- a/sys/arm64/linux/linux_locore.asm +++ b/sys/arm64/linux/linux_locore.asm @@ -44,7 +44,9 @@ .text + nop /* This is what Linux calls a "Mysterious NOP". */ ENTRY(__kernel_rt_sigreturn) - brk #0 /* LINUXTODO: implement __kernel_rt_sigreturn */ + mov x8, #LINUX_SYS_linux_rt_sigreturn + svc #0 ret END(__kernel_rt_sigreturn) diff --git a/sys/arm64/linux/linux_sysvec.c b/sys/arm64/linux/linux_sysvec.c --- a/sys/arm64/linux/linux_sysvec.c +++ b/sys/arm64/linux/linux_sysvec.c @@ -37,12 +37,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -61,6 +63,7 @@ #include #include #include +#include #include #include @@ -407,18 +410,103 @@ int linux_rt_sigreturn(struct thread *td, struct linux_rt_sigreturn_args *args) { + struct l_sigframe frame; + struct trapframe *tf; + int error; + + tf = td->td_frame; + + if (copyin((void *)tf->tf_sp, &frame, sizeof(frame))) + return (EFAULT); - /* LINUXTODO: implement */ - LIN_SDT_PROBE0(sysvec, linux_rt_sigreturn, todo); - return (EDOOFUS); + error = set_mcontext(td, &frame.sf_uc.uc_mcontext); + if (error != 0) + return (error); + + /* Restore signal mask. */ + kern_sigprocmask(td, SIG_SETMASK, &frame.sf_uc.uc_sigmask, NULL, 0); + + return (EJUSTRETURN); } static void linux_rt_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) { + struct thread *td; + struct proc *p; + struct trapframe *tf; + struct l_sigframe *fp, frame; + struct sigacts *psp; + int onstack, sig; + uint32_t spsr; + + 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); + + tf = td->td_frame; + onstack = sigonstack(tf->tf_sp); + + CTR4(KTR_SIG, "sendsig: td=%p (%s) catcher=%p sig=%d", td, p->p_comm, + catcher, sig); + + /* Allocate and validate space for the signal handler context. */ + if ((td->td_pflags & TDP_ALTSTACK) != 0 && !onstack && + SIGISMEMBER(psp->ps_sigonstack, sig)) { + fp = (struct l_sigframe *)((uintptr_t)td->td_sigstk.ss_sp + + td->td_sigstk.ss_size); +#if defined(COMPAT_43) + td->td_sigstk.ss_flags |= SS_ONSTACK; +#endif + } else { + fp = (struct l_sigframe *)td->td_frame->tf_sp; + } + + /* Make room, keeping the stack aligned */ + fp--; + fp = (struct l_sigframe *)STACKALIGN(fp); + + /* Fill in the frame to copy out */ + bzero(&frame, sizeof(frame)); + get_mcontext(td, &frame.sf_uc.uc_mcontext, 0); + spsr = frame.sf_uc.uc_mcontext.mc_gpregs.gp_spsr; + + /* Translate the signal. */ + sig = bsd_to_linux_signal(sig); + + siginfo_to_lsiginfo(&ksi->ksi_info, &frame.sf_si, sig); + frame.sf_uc.uc_sigmask = *mask; + frame.sf_uc.uc_stack = td->td_sigstk; + frame.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) != 0 ? + (onstack ? SS_ONSTACK : 0) : SS_DISABLE; + mtx_unlock(&psp->ps_mtx); + PROC_UNLOCK(td->td_proc); + + /* Copy the sigframe out to the user's stack. */ + if (copyout(&frame, fp, sizeof(*fp)) != 0) { + /* Process has trashed its stack. Kill it. */ + CTR2(KTR_SIG, "sendsig: sigexit td=%p fp=%p", td, fp); + PROC_LOCK(p); + sigexit(td, SIGILL); + } + + tf->tf_x[0]= sig; + tf->tf_x[1] = (register_t)&fp->sf_si; + tf->tf_x[2] = (register_t)&fp->sf_uc; + + tf->tf_elr = (register_t)catcher; + tf->tf_sp = (register_t)fp; + tf->tf_lr = (register_t)__kernel_rt_sigreturn; + + CTR3(KTR_SIG, "sendsig: return td=%p pc=%#x sp=%#x", td, tf->tf_elr, + tf->tf_sp); - /* LINUXTODO: implement */ - LIN_SDT_PROBE0(sysvec, linux_rt_sendsig, todo); + PROC_LOCK(p); + mtx_lock(&psp->ps_mtx); } struct sysentvec elf_linux_sysvec = {