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
This commit is contained in:
Andrew Turner 2024-03-21 10:13:16 +00:00
parent 69d5783ae8
commit 7e6437c084
2 changed files with 94 additions and 7 deletions

View file

@ -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. */

View file

@ -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