git: c56480a83235 - main - linux(4): Implement signal trampoline for arm64 in a FreeBSD-way

From: Dmitry Chagin <dchagin_at_FreeBSD.org>
Date: Sun, 15 May 2022 18:12:30 UTC
The branch main has been updated by dchagin:

URL: https://cgit.FreeBSD.org/src/commit/?id=c56480a832354aff995f9d0bc5da4ccf27dfe78a

commit c56480a832354aff995f9d0bc5da4ccf27dfe78a
Author:     Dmitry Chagin <dchagin@FreeBSD.org>
AuthorDate: 2022-05-15 18:10:50 +0000
Commit:     Dmitry Chagin <dchagin@FreeBSD.org>
CommitDate: 2022-05-15 18:10:50 +0000

    linux(4): Implement signal trampoline for arm64 in a FreeBSD-way
    
    The implemenation differs from others Linuxulators.
    For unwinders Linux ucontext_t is stored, however native machine context
    is used to store/restore process state to avoid code duplication.
    
    As DWARF Aarch64 does not define a register number for PC and provides no
    direct way to encode the PC of the previous frame, CFI cannot describe a
    signal trampoline frame. So, modified the vdso linker script to discard
    unused sections.
    
    Extensions are not implemented.
    
    MFC after:              2 weeks
---
 sys/arm64/linux/linux.h          |  2 +-
 sys/arm64/linux/linux_locore.asm | 15 +++++--
 sys/arm64/linux/linux_sigframe.h | 62 ++++++++++++++++++++++-----
 sys/arm64/linux/linux_sysvec.c   | 90 ++++++++++++++++++++++++++++++----------
 sys/arm64/linux/linux_vdso.lds.s | 33 ++++++++-------
 5 files changed, 150 insertions(+), 52 deletions(-)

diff --git a/sys/arm64/linux/linux.h b/sys/arm64/linux/linux.h
index 402f7aa39bb9..dafec928c7e4 100644
--- a/sys/arm64/linux/linux.h
+++ b/sys/arm64/linux/linux.h
@@ -164,7 +164,7 @@ struct l_newstat {
 #define	LINUX_SIG_SETMASK	2
 
 /* sigaltstack */
-#define	LINUX_MINSIGSTKSZ	2048		/* XXX */
+#define	LINUX_MINSIGSTKSZ	5664		/* sigframe */
 
 typedef void	(*l_handler_t)(l_int);
 
diff --git a/sys/arm64/linux/linux_locore.asm b/sys/arm64/linux/linux_locore.asm
index 0311c2e7e7e9..dfaafba155f2 100644
--- a/sys/arm64/linux/linux_locore.asm
+++ b/sys/arm64/linux/linux_locore.asm
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2018 Turing Robotic Industries Inc.
  * Copyright (C) 2020 Andrew Turner <andrew@FreeBSD.org>
+ * Copyright (C) 2022 Dmitry Chagin <dchagin@FreeBSD.org>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -29,7 +30,7 @@
  */
 
 /*
- * arm64 Linux VDSO implementation.
+ * arm64 Linux VDSO signal trampoline.
  */
 
 #include <machine/asm.h>
@@ -45,8 +46,14 @@ linux_platform:
 	.text
 
 	nop	/* This is what Linux calls a "Mysterious NOP". */
-ENTRY(__kernel_rt_sigreturn)
+EENTRY(__kernel_rt_sigreturn)
 	mov	x8, #LINUX_SYS_linux_rt_sigreturn
 	svc	#0
-	ret
-END(__kernel_rt_sigreturn)
+EEND(__kernel_rt_sigreturn)
+
+EENTRY(linux_vdso_sigcode)
+	blr	x8
+
+	mov	x8, #LINUX_SYS_linux_rt_sigreturn
+	svc	#0
+EEND(linux_vdso_sigcode)
diff --git a/sys/arm64/linux/linux_sigframe.h b/sys/arm64/linux/linux_sigframe.h
index 060b89c920ac..d0d870e51375 100644
--- a/sys/arm64/linux/linux_sigframe.h
+++ b/sys/arm64/linux/linux_sigframe.h
@@ -1,7 +1,7 @@
 /*-
  * Copyright (c) 1994-1996 Søren Schmidt
- * Copyright (c) 2013 Dmitry Chagin <dchagin@FreeBSD.org>
  * Copyright (c) 2018 Turing Robotic Industries Inc.
+ * Copyright (c) 2022 Dmitry Chagin <dchagin@FreeBSD.org>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -23,22 +23,62 @@
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- */
-
-/*
+ *
  * $FreeBSD$
  */
+
 #ifndef _ARM64_LINUX_SIGFRAME_H_
 #define	_ARM64_LINUX_SIGFRAME_H_
 
-/*
- * 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_aarch64_ctx {
+	uint32_t	magic;
+	uint32_t	size;
+};
+
+#define	L_FPSIMD_MAGIC	0x46508001
+#define	L_ESR_MAGIC	0x45535201
+
+struct l_fpsimd_context {
+	struct _l_aarch64_ctx head;
+	uint32_t	fpsr;
+	uint32_t	fpcr;
+	__uint128_t	vregs[32];
+};
+
+struct l_esr_context {
+	struct _l_aarch64_ctx head;
+	uint64_t	esr;
+};
+
+struct l_sigcontext {
+	uint64_t	fault_address;
+	uint64_t	regs[31];
+	uint64_t	sp;
+	uint64_t	pc;
+	uint64_t	pstate;
+	uint8_t		__reserved[4096] __attribute__((__aligned__(16)));
+};
+
+struct l_ucontext {
+	unsigned long	uc_flags;
+	struct l_ucontext *uc_link;
+	l_stack_t	uc_stack;
+	l_sigset_t	uc_sigmask;
+	uint8_t		__glibc_hole[1024 / 8 - sizeof(l_sigset_t)];
+	struct l_sigcontext uc_sc;
+};
+
+struct l_rt_sigframe {
+	l_siginfo_t	sf_si;
+	struct l_ucontext sf_uc;
+} __attribute__((__aligned__(16)));
+
 struct l_sigframe {
-	struct l_siginfo	sf_si;
-	ucontext_t		sf_uc;
+	struct l_rt_sigframe sf;
+	/* frame_record */
+	uint64_t	fp;
+	uint64_t	lr;
+	ucontext_t	uc;
 };
 
 #endif /* _ARM64_LINUX_SIGFRAME_H_ */
diff --git a/sys/arm64/linux/linux_sysvec.c b/sys/arm64/linux/linux_sysvec.c
index 3d3e64f15c6c..461a00bf5b33 100644
--- a/sys/arm64/linux/linux_sysvec.c
+++ b/sys/arm64/linux/linux_sysvec.c
@@ -128,7 +128,7 @@ LIN_SDT_PROBE_DEFINE0(sysvec, linux_elf_fixup, todo);
 
 LINUX_VDSO_SYM_CHAR(linux_platform);
 LINUX_VDSO_SYM_INTPTR(kern_timekeep_base);
-LINUX_VDSO_SYM_INTPTR(__kernel_rt_sigreturn);
+LINUX_VDSO_SYM_INTPTR(linux_vdso_sigcode);
 
 /* LINUXTODO: do we have traps to translate? */
 static int
@@ -405,21 +405,23 @@ 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 l_sigframe *frame;
+	ucontext_t uc;
 	struct trapframe *tf;
 	int error;
 
 	tf = td->td_frame;
+	frame = (struct l_sigframe *)tf->tf_sp;
 
-	if (copyin((void *)tf->tf_sp, &frame, sizeof(frame)))
+	if (copyin((void *)&frame->uc, &uc, sizeof(uc)))
 		return (EFAULT);
 
-	error = set_mcontext(td, &frame.sf_uc.uc_mcontext);
+	error = set_mcontext(td, &uc.uc_mcontext);
 	if (error != 0)
 		return (error);
 
 	/* Restore signal mask. */
-	kern_sigprocmask(td, SIG_SETMASK, &frame.sf_uc.uc_sigmask, NULL, 0);
+	kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0);
 
 	return (EJUSTRETURN);
 }
@@ -430,7 +432,12 @@ 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 l_sigframe *fp, *frame;
+	struct l_fpsimd_context *fpsimd;
+	struct l_esr_context *esr;
+	l_stack_t uc_stack;
+	ucontext_t uc;
+	uint8_t *scr;
 	struct sigacts *psp;
 	int onstack, sig;
 
@@ -464,36 +471,77 @@ linux_rt_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
 	fp--;
 	fp = (struct l_sigframe *)STACKALIGN(fp);
 
+	get_mcontext(td, &uc.uc_mcontext, 0);
+	uc.uc_sigmask = *mask;
+
+	uc_stack.ss_sp = PTROUT(td->td_sigstk.ss_sp);
+	uc_stack.ss_size = td->td_sigstk.ss_size;
+	uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) != 0 ?
+	    (onstack ? LINUX_SS_ONSTACK : 0) : LINUX_SS_DISABLE;
+	mtx_unlock(&psp->ps_mtx);
+	PROC_UNLOCK(td->td_proc);
+
 	/* Fill in the frame to copy out */
-	bzero(&frame, sizeof(frame));
-	get_mcontext(td, &frame.sf_uc.uc_mcontext, 0);
+	frame = malloc(sizeof(*frame), M_LINUX, M_WAITOK | M_ZERO);
+
+	memcpy(&frame->sf.sf_uc.uc_sc.regs, tf->tf_x, sizeof(tf->tf_x));
+	frame->sf.sf_uc.uc_sc.regs[30] = tf->tf_lr;
+	frame->sf.sf_uc.uc_sc.sp = tf->tf_sp;
+	frame->sf.sf_uc.uc_sc.pc = tf->tf_lr;
+	frame->sf.sf_uc.uc_sc.pstate = tf->tf_spsr;
+	frame->sf.sf_uc.uc_sc.fault_address = (register_t)ksi->ksi_addr;
+
+	/* Stack frame for unwinding */
+	frame->fp = tf->tf_x[29];
+	frame->lr = tf->tf_lr;
 
 	/* Translate the signal. */
 	sig = bsd_to_linux_signal(sig);
+	siginfo_to_lsiginfo(&ksi->ksi_info, &frame->sf.sf_si, sig);
+	bsd_to_linux_sigset(mask, &frame->sf.sf_uc.uc_sigmask);
 
-	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);
+	/*
+	 * Prepare fpsimd & esr. Does not check sizes, as
+	 * __reserved is big enougth.
+	 */
+	scr = (uint8_t *)&frame->sf.sf_uc.uc_sc.__reserved;
+#ifdef VFP
+	fpsimd = (struct l_fpsimd_context *) scr;
+	fpsimd->head.magic = L_FPSIMD_MAGIC;
+	fpsimd->head.size = sizeof(struct l_fpsimd_context);
+	fpsimd->fpsr = uc.uc_mcontext.mc_fpregs.fp_sr;
+	fpsimd->fpcr = uc.uc_mcontext.mc_fpregs.fp_cr;
+
+	memcpy(fpsimd->vregs, &uc.uc_mcontext.mc_fpregs.fp_q,
+	    sizeof(uc.uc_mcontext.mc_fpregs.fp_q));
+	scr += roundup(sizeof(struct l_fpsimd_context), 16);
+#endif
+	if (ksi->ksi_addr != 0) {
+		esr = (struct l_esr_context *) scr;
+		esr->head.magic = L_ESR_MAGIC;
+		esr->head.size = sizeof(struct l_esr_context);
+		esr->esr = tf->tf_esr;
+	}
+
+	memcpy(&frame->sf.sf_uc.uc_stack, &uc_stack, sizeof(uc_stack));
+	memcpy(&frame->uc, &uc, sizeof(uc));
 
 	/* Copy the sigframe out to the user's stack. */
-	if (copyout(&frame, fp, sizeof(*fp)) != 0) {
+	if (copyout(frame, fp, sizeof(*fp)) != 0) {
 		/* Process has trashed its stack. Kill it. */
+		free(frame, M_LINUX);
 		CTR2(KTR_SIG, "sendsig: sigexit td=%p fp=%p", td, fp);
 		PROC_LOCK(p);
 		sigexit(td, SIGILL);
 	}
+	free(frame, M_LINUX);
 
 	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_x[1] = (register_t)&fp->sf.sf_si;
+	tf->tf_x[2] = (register_t)&fp->sf.sf_uc;
+	tf->tf_x[8] = (register_t)catcher;
 	tf->tf_sp = (register_t)fp;
-	tf->tf_lr = (register_t)__kernel_rt_sigreturn;
+	tf->tf_elr = (register_t)linux_vdso_sigcode;
 
 	CTR3(KTR_SIG, "sendsig: return td=%p pc=%#x sp=%#x", td, tf->tf_elr,
 	    tf->tf_sp);
diff --git a/sys/arm64/linux/linux_vdso.lds.s b/sys/arm64/linux/linux_vdso.lds.s
index 98cbb9a5736b..8790e14bbb80 100644
--- a/sys/arm64/linux/linux_vdso.lds.s
+++ b/sys/arm64/linux/linux_vdso.lds.s
@@ -1,6 +1,6 @@
 /*
  * Linker script for 64-bit vDSO.
- * Copied from Linux kernel arch/x86/vdso/vdso-layout.lds.S
+ * Copied from Linux kernel arch/arm64/kernel/vdso/vdso.lds.S
  *
  * $FreeBSD$
  */
@@ -17,29 +17,32 @@ SECTIONS
 	.gnu.version_d	: { *(.gnu.version_d) }
 	.gnu.version_r	: { *(.gnu.version_r) }
 
+	/DISCARD/	: {
+		*(.note.GNU-stack .note.gnu.property)
+	}
+
 	.note		: { *(.note.*) }		:text	:note
 
-	.eh_frame_hdr	: { *(.eh_frame_hdr) }		:text	:eh_frame_hdr
-	.eh_frame	: { KEEP (*(.eh_frame)) }	:text
+	. = ALIGN(0x100);
+
+	.text		: { *(.text*) }			:text	=0x90909090
+	PROVIDE (__etext = .);
+	PROVIDE (_etext = .);
+	PROVIDE (etext = .);
 
 	.dynamic	: { *(.dynamic) }		:text	:dynamic
 
 	.rodata		: { *(.rodata*) }		:text
 	.data		: {
-	      *(.data*)
-	      *(.sdata*)
-	      *(.got.plt) *(.got)
-	      *(.gnu.linkonce.d.*)
-	      *(.bss*)
-	      *(.dynbss*)
-	      *(.gnu.linkonce.b.*)
+		*(.data*)
 	}
 
-	.altinstructions	: { *(.altinstructions) }
-	.altinstr_replacement	: { *(.altinstr_replacement) }
+	_end = .;
+	PROVIDE(end = .);
 
-	. = ALIGN(0x100);
-	.text		: { *(.test .text*) }			:text	=0x90909090
+	/DISCARD/	: {
+		*(.eh_frame .eh_frame_hdr)
+	}
 }
 
 PHDRS
@@ -47,7 +50,6 @@ PHDRS
 	text		PT_LOAD		FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */
 	dynamic		PT_DYNAMIC	FLAGS(4);		/* PF_R */
 	note		PT_NOTE		FLAGS(4);		/* PF_R */
-	eh_frame_hdr	PT_GNU_EH_FRAME;
 }
 
 /*
@@ -68,6 +70,7 @@ VERSION
 	global:
 		linux_platform;
 		kern_timekeep_base;
+		linux_vdso_sigcode;
 	local: *;
 	};
 }