git: 7e6437c08415 - main - arm64: Support passing more registers to signals

From: Andrew Turner <andrew_at_FreeBSD.org>
Date: Thu, 21 Mar 2024 10:13:43 UTC
The branch main has been updated by andrew:

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

commit 7e6437c08415ade75403f1ecad75167257e8ea3c
Author:     Andrew Turner <andrew@FreeBSD.org>
AuthorDate: 2024-03-21 10:13:16 +0000
Commit:     Andrew Turner <andrew@FreeBSD.org>
CommitDate: 2024-03-21 10:13:16 +0000

    arm64: Support passing more registers to signals
    
    To support recent extensions to the Arm architecture we may need to
    store more or larger registers when sending a signal.
    
    To support this create a list of these extra registers. Userspace that
    needs to access a register in the signal handler can then walk the list
    to find the correct register struct and read/write its contents.
    
    Reviewed by:    kib, markj (earlier version)
    Sponsored by:   Arm Ltd
    Differential Revision:  https://reviews.freebsd.org/D43302
---
 sys/arm64/arm64/exec_machdep.c | 85 +++++++++++++++++++++++++++++++++++++++---
 sys/arm64/include/ucontext.h   | 16 +++++++-
 2 files changed, 94 insertions(+), 7 deletions(-)

diff --git a/sys/arm64/arm64/exec_machdep.c b/sys/arm64/arm64/exec_machdep.c
index 52c63f801693..1bff70450b69 100644
--- a/sys/arm64/arm64/exec_machdep.c
+++ b/sys/arm64/arm64/exec_machdep.c
@@ -461,8 +461,12 @@ int
 set_mcontext(struct thread *td, mcontext_t *mcp)
 {
 #define	PSR_13_MASK	0xfffffffful
+	struct arm64_reg_context ctx;
 	struct trapframe *tf = td->td_frame;
 	uint64_t spsr;
+	vm_offset_t addr;
+	int error;
+	bool done;
 
 	spsr = mcp->mc_gpregs.gp_spsr;
 #ifdef COMPAT_FREEBSD13
@@ -501,8 +505,35 @@ set_mcontext(struct thread *td, mcontext_t *mcp)
 		    READ_SPECIALREG(mdscr_el1) | MDSCR_SS);
 		isb();
 	}
+
 	set_fpcontext(td, mcp);
 
+	/* Read any register contexts we find */
+	if (mcp->mc_ptr != 0) {
+		addr = mcp->mc_ptr;
+
+		done = false;
+		do {
+			if (!__is_aligned(addr,
+			    _Alignof(struct arm64_reg_context)))
+				return (EINVAL);
+
+			error = copyin((const void *)addr, &ctx, sizeof(ctx));
+			if (error != 0)
+				return (error);
+
+			switch (ctx.ctx_id) {
+			case ARM64_CTX_END:
+				done = true;
+				break;
+			default:
+				return (EINVAL);
+			}
+
+			addr += ctx.ctx_size;
+		} while (!done);
+	}
+
 	return (0);
 #undef PSR_13_MASK
 }
@@ -585,6 +616,31 @@ sys_sigreturn(struct thread *td, struct sigreturn_args *uap)
 	return (EJUSTRETURN);
 }
 
+static bool
+sendsig_ctx_end(struct thread *td, vm_offset_t *addrp)
+{
+	struct arm64_reg_context end_ctx;
+	vm_offset_t ctx_addr;
+
+	*addrp -= sizeof(end_ctx);
+	ctx_addr = *addrp;
+
+	memset(&end_ctx, 0, sizeof(end_ctx));
+	end_ctx.ctx_id = ARM64_CTX_END;
+	end_ctx.ctx_size = sizeof(end_ctx);
+
+	if (copyout(&end_ctx, (void *)ctx_addr, sizeof(end_ctx)) != 0)
+		return (false);
+
+	return (true);
+}
+
+typedef bool(*ctx_func)(struct thread *, vm_offset_t *);
+static const ctx_func ctx_funcs[] = {
+	sendsig_ctx_end,	/* Must be first to end the linked list */
+	NULL,
+};
+
 void
 sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
 {
@@ -593,6 +649,7 @@ sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
 	struct trapframe *tf;
 	struct sigframe *fp, frame;
 	struct sigacts *psp;
+	vm_offset_t addr;
 	int onstack, sig;
 
 	td = curthread;
@@ -612,19 +669,15 @@ sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
 	/* Allocate and validate space for the signal handler context. */
 	if ((td->td_pflags & TDP_ALTSTACK) != 0 && !onstack &&
 	    SIGISMEMBER(psp->ps_sigonstack, sig)) {
-		fp = (struct sigframe *)((uintptr_t)td->td_sigstk.ss_sp +
+		addr = ((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 sigframe *)td->td_frame->tf_sp;
+		addr = td->td_frame->tf_sp;
 	}
 
-	/* Make room, keeping the stack aligned */
-	fp--;
-	fp = (struct sigframe *)STACKALIGN(fp);
-
 	/* Fill in the frame to copy out */
 	bzero(&frame, sizeof(frame));
 	get_mcontext(td, &frame.sf_uc.uc_mcontext, 0);
@@ -636,6 +689,26 @@ sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
 	mtx_unlock(&psp->ps_mtx);
 	PROC_UNLOCK(td->td_proc);
 
+	for (int i = 0; ctx_funcs[i] != NULL; i++) {
+		if (!ctx_funcs[i](td, &addr)) {
+			/* Process has trashed its stack. Kill it. */
+			CTR4(KTR_SIG,
+			    "sendsig: frame sigexit td=%p fp=%#lx func[%d]=%p",
+			    td, addr, i, ctx_funcs[i]);
+			PROC_LOCK(p);
+			sigexit(td, SIGILL);
+			/* NOTREACHED */
+		}
+	}
+
+	/* Point at the first context */
+	frame.sf_uc.uc_mcontext.mc_ptr = addr;
+
+	/* Make room, keeping the stack aligned */
+	fp = (struct sigframe *)addr;
+	fp--;
+	fp = (struct sigframe *)STACKALIGN(fp);
+
 	/* Copy the sigframe out to the user's stack. */
 	if (copyout(&frame, fp, sizeof(*fp)) != 0) {
 		/* Process has trashed its stack. Kill it. */
diff --git a/sys/arm64/include/ucontext.h b/sys/arm64/include/ucontext.h
index e9b914315a19..dedbd061ec6b 100644
--- a/sys/arm64/include/ucontext.h
+++ b/sys/arm64/include/ucontext.h
@@ -51,15 +51,29 @@ struct fpregs {
 	int		fp_pad;
 };
 
+/*
+ * Support for registers that don't fit into gpregs or fpregs, e.g. SVE.
+ * There are some registers that have been added so are optional. To support
+ * these create an array of headers that point at the register data.
+ */
+struct arm64_reg_context {
+	__uint32_t	ctx_id;
+	__uint32_t	ctx_size;
+};
+
+#define	ARM64_CTX_END		0xa5a5a5a5
+
 struct __mcontext {
 	struct gpregs	mc_gpregs;
 	struct fpregs	mc_fpregs;
 	int		mc_flags;
 #define	_MC_FP_VALID	0x1		/* Set when mc_fpregs has valid data */
 	int		mc_pad;		/* Padding */
-	__uint64_t	mc_spare[8];	/* Space for expansion, set to zero */
+	__uint64_t	mc_ptr;		/* Address of extra_regs struct */
+	__uint64_t	mc_spare[7];	/* Space for expansion, set to zero */
 };
 
+
 typedef struct __mcontext mcontext_t;
 
 #ifdef COMPAT_FREEBSD32