diff options
author | Edward Tomasz Napierala <trasz@FreeBSD.org> | 2021-07-25 06:22:47 +0000 |
---|---|---|
committer | Edward Tomasz Napierala <trasz@FreeBSD.org> | 2021-07-25 06:22:57 +0000 |
commit | ccc510b46340da563e21549a1a0cb99915d8d623 (patch) | |
tree | fb129f9aa62557fb2b5347c3edfa91dd913bec7c | |
parent | 0c47338023d44ff130cf69465bd1b2e75ff0bb39 (diff) | |
download | src-ccc510b46340.tar.gz src-ccc510b46340.zip |
linux: implement signal delivery on arm64
Note that this still uses FreeBSD-style sigframe;
this will be addressed later.
Reviewed By: dchagin
Sponsored By: EPSRC
Differential Revision: https://reviews.freebsd.org/D31258
-rw-r--r-- | sys/arm64/linux/linux.h | 10 | ||||
-rw-r--r-- | sys/arm64/linux/linux_locore.asm | 4 | ||||
-rw-r--r-- | sys/arm64/linux/linux_sysvec.c | 98 |
3 files changed, 106 insertions, 6 deletions
diff --git a/sys/arm64/linux/linux.h b/sys/arm64/linux/linux.h index ab3bab8264f4..7ea169b962b4 100644 --- a/sys/arm64/linux/linux.h +++ b/sys/arm64/linux/linux.h @@ -243,6 +243,16 @@ typedef struct l_siginfo { #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 index 24a48d74f7b5..0311c2e7e7e9 100644 --- a/sys/arm64/linux/linux_locore.asm +++ b/sys/arm64/linux/linux_locore.asm @@ -44,7 +44,9 @@ linux_platform: .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 index 72621b4bbfbd..3625155111f2 100644 --- a/sys/arm64/linux/linux_sysvec.c +++ b/sys/arm64/linux/linux_sysvec.c @@ -37,12 +37,14 @@ __FBSDID("$FreeBSD$"); #include <sys/imgact.h> #include <sys/imgact_elf.h> #include <sys/kernel.h> +#include <sys/ktr.h> #include <sys/lock.h> #include <sys/module.h> #include <sys/mutex.h> #include <sys/proc.h> #include <sys/stddef.h> #include <sys/signalvar.h> +#include <sys/syscallsubr.h> #include <sys/sysctl.h> #include <sys/sysent.h> @@ -61,6 +63,7 @@ __FBSDID("$FreeBSD$"); #include <compat/linux/linux_ioctl.h> #include <compat/linux/linux_mib.h> #include <compat/linux/linux_misc.h> +#include <compat/linux/linux_signal.h> #include <compat/linux/linux_util.h> #include <compat/linux/linux_vdso.h> @@ -407,18 +410,103 @@ linux_exec_setregs(struct thread *td, struct image_params *imgp, 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 = { |