Skip to content

Commit 3978221

Browse files
brooniectmarinas
authored andcommitted
arm64/sme: Implement ZA signal handling
Implement support for ZA in signal handling in a very similar way to how we implement support for SVE registers, using a signal context structure with optional register state after it. Where present this register state stores the ZA matrix as a series of horizontal vectors numbered from 0 to VL/8 in the endinanness independent format used for vectors. As with SVE we do not allow changes in the vector length during signal return but we do allow ZA to be enabled or disabled. Signed-off-by: Mark Brown <[email protected]> Reviewed-by: Catalin Marinas <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Catalin Marinas <[email protected]>
1 parent 85ed24d commit 3978221

File tree

3 files changed

+180
-3
lines changed

3 files changed

+180
-3
lines changed

arch/arm64/include/uapi/asm/sigcontext.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,14 @@ struct sve_context {
140140

141141
#define SVE_SIG_FLAG_SM 0x1 /* Context describes streaming mode */
142142

143+
#define ZA_MAGIC 0x54366345
144+
145+
struct za_context {
146+
struct _aarch64_ctx head;
147+
__u16 vl;
148+
__u16 __reserved[3];
149+
};
150+
143151
#endif /* !__ASSEMBLY__ */
144152

145153
#include <asm/sve_context.h>
@@ -259,4 +267,37 @@ struct sve_context {
259267
#define SVE_SIG_CONTEXT_SIZE(vq) \
260268
(SVE_SIG_REGS_OFFSET + SVE_SIG_REGS_SIZE(vq))
261269

270+
/*
271+
* If the ZA register is enabled for the thread at signal delivery then,
272+
* za_context.head.size >= ZA_SIG_CONTEXT_SIZE(sve_vq_from_vl(za_context.vl))
273+
* and the register data may be accessed using the ZA_SIG_*() macros.
274+
*
275+
* If za_context.head.size < ZA_SIG_CONTEXT_SIZE(sve_vq_from_vl(za_context.vl))
276+
* then ZA was not enabled and no register data was included in which case
277+
* ZA register was not enabled for the thread and no register data
278+
* the ZA_SIG_*() macros should not be used except for this check.
279+
*
280+
* The same convention applies when returning from a signal: a caller
281+
* will need to remove or resize the za_context block if it wants to
282+
* enable the ZA register when it was previously non-live or vice-versa.
283+
* This may require the caller to allocate fresh memory and/or move other
284+
* context blocks in the signal frame.
285+
*
286+
* Changing the vector length during signal return is not permitted:
287+
* za_context.vl must equal the thread's current SME vector length when
288+
* doing a sigreturn.
289+
*/
290+
291+
#define ZA_SIG_REGS_OFFSET \
292+
((sizeof(struct za_context) + (__SVE_VQ_BYTES - 1)) \
293+
/ __SVE_VQ_BYTES * __SVE_VQ_BYTES)
294+
295+
#define ZA_SIG_REGS_SIZE(vq) ((vq * __SVE_VQ_BYTES) * (vq * __SVE_VQ_BYTES))
296+
297+
#define ZA_SIG_ZAV_OFFSET(vq, n) (ZA_SIG_REGS_OFFSET + \
298+
(SVE_SIG_ZREG_SIZE(vq) * n))
299+
300+
#define ZA_SIG_CONTEXT_SIZE(vq) \
301+
(ZA_SIG_REGS_OFFSET + ZA_SIG_REGS_SIZE(vq))
302+
262303
#endif /* _UAPI__ASM_SIGCONTEXT_H */

arch/arm64/kernel/fpsimd.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,9 +1181,6 @@ void fpsimd_release_task(struct task_struct *dead_task)
11811181

11821182
#ifdef CONFIG_ARM64_SME
11831183

1184-
/* This will move to uapi/asm/sigcontext.h when signals are implemented */
1185-
#define ZA_SIG_REGS_SIZE(vq) ((vq * __SVE_VQ_BYTES) * (vq * __SVE_VQ_BYTES))
1186-
11871184
/*
11881185
* Ensure that task->thread.za_state is allocated and sufficiently large.
11891186
*

arch/arm64/kernel/signal.c

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ struct rt_sigframe_user_layout {
5656
unsigned long fpsimd_offset;
5757
unsigned long esr_offset;
5858
unsigned long sve_offset;
59+
unsigned long za_offset;
5960
unsigned long extra_offset;
6061
unsigned long end_offset;
6162
};
@@ -218,6 +219,7 @@ static int restore_fpsimd_context(struct fpsimd_context __user *ctx)
218219
struct user_ctxs {
219220
struct fpsimd_context __user *fpsimd;
220221
struct sve_context __user *sve;
222+
struct za_context __user *za;
221223
};
222224

223225
#ifdef CONFIG_ARM64_SVE
@@ -346,6 +348,101 @@ extern int restore_sve_fpsimd_context(struct user_ctxs *user);
346348

347349
#endif /* ! CONFIG_ARM64_SVE */
348350

351+
#ifdef CONFIG_ARM64_SME
352+
353+
static int preserve_za_context(struct za_context __user *ctx)
354+
{
355+
int err = 0;
356+
u16 reserved[ARRAY_SIZE(ctx->__reserved)];
357+
unsigned int vl = task_get_sme_vl(current);
358+
unsigned int vq;
359+
360+
if (thread_za_enabled(&current->thread))
361+
vq = sve_vq_from_vl(vl);
362+
else
363+
vq = 0;
364+
365+
memset(reserved, 0, sizeof(reserved));
366+
367+
__put_user_error(ZA_MAGIC, &ctx->head.magic, err);
368+
__put_user_error(round_up(ZA_SIG_CONTEXT_SIZE(vq), 16),
369+
&ctx->head.size, err);
370+
__put_user_error(vl, &ctx->vl, err);
371+
BUILD_BUG_ON(sizeof(ctx->__reserved) != sizeof(reserved));
372+
err |= __copy_to_user(&ctx->__reserved, reserved, sizeof(reserved));
373+
374+
if (vq) {
375+
/*
376+
* This assumes that the ZA state has already been saved to
377+
* the task struct by calling the function
378+
* fpsimd_signal_preserve_current_state().
379+
*/
380+
err |= __copy_to_user((char __user *)ctx + ZA_SIG_REGS_OFFSET,
381+
current->thread.za_state,
382+
ZA_SIG_REGS_SIZE(vq));
383+
}
384+
385+
return err ? -EFAULT : 0;
386+
}
387+
388+
static int restore_za_context(struct user_ctxs __user *user)
389+
{
390+
int err;
391+
unsigned int vq;
392+
struct za_context za;
393+
394+
if (__copy_from_user(&za, user->za, sizeof(za)))
395+
return -EFAULT;
396+
397+
if (za.vl != task_get_sme_vl(current))
398+
return -EINVAL;
399+
400+
if (za.head.size <= sizeof(*user->za)) {
401+
current->thread.svcr &= ~SYS_SVCR_EL0_ZA_MASK;
402+
return 0;
403+
}
404+
405+
vq = sve_vq_from_vl(za.vl);
406+
407+
if (za.head.size < ZA_SIG_CONTEXT_SIZE(vq))
408+
return -EINVAL;
409+
410+
/*
411+
* Careful: we are about __copy_from_user() directly into
412+
* thread.za_state with preemption enabled, so protection is
413+
* needed to prevent a racing context switch from writing stale
414+
* registers back over the new data.
415+
*/
416+
417+
fpsimd_flush_task_state(current);
418+
/* From now, fpsimd_thread_switch() won't touch thread.sve_state */
419+
420+
sme_alloc(current);
421+
if (!current->thread.za_state) {
422+
current->thread.svcr &= ~SYS_SVCR_EL0_ZA_MASK;
423+
clear_thread_flag(TIF_SME);
424+
return -ENOMEM;
425+
}
426+
427+
err = __copy_from_user(current->thread.za_state,
428+
(char __user const *)user->za +
429+
ZA_SIG_REGS_OFFSET,
430+
ZA_SIG_REGS_SIZE(vq));
431+
if (err)
432+
return -EFAULT;
433+
434+
set_thread_flag(TIF_SME);
435+
current->thread.svcr |= SYS_SVCR_EL0_ZA_MASK;
436+
437+
return 0;
438+
}
439+
#else /* ! CONFIG_ARM64_SME */
440+
441+
/* Turn any non-optimised out attempts to use these into a link error: */
442+
extern int preserve_za_context(void __user *ctx);
443+
extern int restore_za_context(struct user_ctxs *user);
444+
445+
#endif /* ! CONFIG_ARM64_SME */
349446

350447
static int parse_user_sigframe(struct user_ctxs *user,
351448
struct rt_sigframe __user *sf)
@@ -360,6 +457,7 @@ static int parse_user_sigframe(struct user_ctxs *user,
360457

361458
user->fpsimd = NULL;
362459
user->sve = NULL;
460+
user->za = NULL;
363461

364462
if (!IS_ALIGNED((unsigned long)base, 16))
365463
goto invalid;
@@ -425,6 +523,19 @@ static int parse_user_sigframe(struct user_ctxs *user,
425523
user->sve = (struct sve_context __user *)head;
426524
break;
427525

526+
case ZA_MAGIC:
527+
if (!system_supports_sme())
528+
goto invalid;
529+
530+
if (user->za)
531+
goto invalid;
532+
533+
if (size < sizeof(*user->za))
534+
goto invalid;
535+
536+
user->za = (struct za_context __user *)head;
537+
break;
538+
428539
case EXTRA_MAGIC:
429540
if (have_extra_context)
430541
goto invalid;
@@ -548,6 +659,9 @@ static int restore_sigframe(struct pt_regs *regs,
548659
}
549660
}
550661

662+
if (err == 0 && system_supports_sme() && user.za)
663+
err = restore_za_context(&user);
664+
551665
return err;
552666
}
553667

@@ -630,6 +744,24 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user,
630744
return err;
631745
}
632746

747+
if (system_supports_sme()) {
748+
unsigned int vl;
749+
unsigned int vq = 0;
750+
751+
if (add_all)
752+
vl = sme_max_vl();
753+
else
754+
vl = task_get_sme_vl(current);
755+
756+
if (thread_za_enabled(&current->thread))
757+
vq = sve_vq_from_vl(vl);
758+
759+
err = sigframe_alloc(user, &user->za_offset,
760+
ZA_SIG_CONTEXT_SIZE(vq));
761+
if (err)
762+
return err;
763+
}
764+
633765
return sigframe_alloc_end(user);
634766
}
635767

@@ -678,6 +810,13 @@ static int setup_sigframe(struct rt_sigframe_user_layout *user,
678810
err |= preserve_sve_context(sve_ctx);
679811
}
680812

813+
/* ZA state if present */
814+
if (system_supports_sme() && err == 0 && user->za_offset) {
815+
struct za_context __user *za_ctx =
816+
apply_user_offset(user, user->za_offset);
817+
err |= preserve_za_context(za_ctx);
818+
}
819+
681820
if (err == 0 && user->extra_offset) {
682821
char __user *sfp = (char __user *)user->sigframe;
683822
char __user *userp =

0 commit comments

Comments
 (0)