[PATCH v4 1/2] aarch64: clear ZA state of SME before clone and clone3
Adhemerval Zanella Netto
adhemerval.zanella@linaro.org
Tue Sep 23 12:17:34 GMT 2025
On 01/09/25 06:51, Yury Khrustalev wrote:
> This change adds a call to the __arm_za_disable() function immediately
> before the SVC instruction inside clone() and clone3() wrappers. This
> sets the ZA state of SME to "off" on return from these functions (for
> both the child and the parent).
>
> This commit also adds relevant tests for the clone() and clone3() use cases.
> While the former is trivial, the latter is a bit complex since the clone3()
> symbol is not public.
>
> To avoid having to check all possible ways clone3() may be called via other
> public functions (e.g. fork(), vfork(), pthread_create()), we put together a
> somewhat synthetic test that links directly with clone3.o. All the existing
> functions that have calls to clone3() may not actually use it, in which case
> the outcome of such tests would be unexpected. Having a direct call to the
> clone3() symbol in the test allows to check precisely what we need to test:
> that the __arm_za_disable() function is indeed called and has the desired
> effect.
>
> Since we use unusual approach when linking test for the clone3() use case,
> to keep things simple, we actually call __arm_za_disable() provided by the
> libgcc library (since GCC 14) instead of using the internal implementation
> from Glibc itself, but that is OK for the purposes of this test since both
> functions do the same thing while the one from libgcc doesn't have extra
> link-time dependencies undesirable for linking this test.
Does it meant that tests won't build for GCC < 14? If so I think we will need
to either pull glibc __arm_za_disable or disable the tests if compiler does
not provide the symbol.
>
> The __arm_za_disable() function is described in [1] (8.1.3). Note that
> the internal Glibc name for this function is __libc_arm_za_disable().
>
> When this change was originally proposed [2,3], it generated a long
> discussion where several questions and concerns were raised. Here we
> will address these concerns and explain why this change is useful and,
> in fact, necessary.
>
> In a nutshell, a C library that conforms to the AAPCS64 spec [1] (pertinent
> to this change, mainly, the chapters 6.2 and 6.6), should have a call to the
> __arm_za_disable() function in clone() and clone3() wrappers. The following
> explains in detail why this is the case.
>
> When we consider using the __arm_za_disable() function inside the clone()
> and clone3() libc wrappers, we talk about the C library subroutines clone()
> and clone3() rather than the syscalls with similar names. In the current
> version of Glibc, clone() is public and clone3() is private, but it being
> private is not pertinent to this discussion.
>
> We will begin with stating that this change is NOT a bug fix for something
> in the kernel. The requirement to call __arm_za_disable() does NOT come from
> the kernel. It also is NOT needed to satisfy a contract between the kernel
> and userspace. This is why it is not for the kernel documentation to describe
> this requirement. This requirement is instead needed to satisfy a pure userspace
> scheme outlined in [1] and to make sure that software that uses Glibc (or any
> other C library that has correct handling of SME states (see below)) conforms
> to [1] without having to unnecessarily become SME-aware thus losing portability.
>
> To recap (see [1] (6.2)), SME extension defines SME state which is part of
> processor state. Part of this SME state is ZA state that is necessary to
> manage ZA storage register in the context of the ZA lazy saving scheme [1]
> (6.6). This scheme exists because it would be challenging to handle ZA
> storage of SME in either callee-saved or caller-saved manner.
>
> There are 3 kinds of ZA state that are defined in terms of the PSTATE.ZA
> bit and the TPIDR2_EL0 register (see [1] (6.6.3)):
>
> - "off": PSTATE.ZA == 0
> - "active": PSTATE.ZA == 1 TPIDR2_EL0 == null
> - "dormant": PSTATE.ZA == 1 TPIDR2_EL0 != null
>
> As [1] (6.7.2) outlines, every subroutine has exactly one SME-interface
> depending on the permitted ZA-states on entry and on normal return from
> a call to this subroutine. Callers of a subroutine must know and respect
> the ZA-interface of the subroutines they are using. Using a subroutine
> in a way that is not permitted by its ZA-interface is undefined behaviour.
>
> In particular, clone() and clone3() (the C library functions) have the
> ZA-private interface. This means that the permitted ZA-states on entry
> are "off" and "dormant" and that the permitted states on return are "off"
> or "dormant" (but if and only if it was "dormant" on entry).
>
> This means that both functions in question should correctly handle both
> "off" and "dormant" ZA-states on entry. The conforming states on return
> are "off" and "dormant" (if inbound state was already "dormant").
>
> This change ensures that the ZA-state on return is always "off". Note,
> that, in the context of clone() and clone3(), "on return" means a point
> when execution resumes at certain address after transferring from clone()
> or clone3(). For the caller (we may refer to it as "parent") this is the
> return address in the link register where the RET instruction jumps. For
> the "child", this is the target branch address.
>
> So, the "off" state on return is permitted and conformant. Why can't we
> retain the "dormant" state? In theory, we can, but we shouldn't, here is
> why.
>
> Every subroutine with a private-ZA interface, including clone() and clone3(),
> must comply with the lazy saving scheme [1] (6.7.2). This puts additional
> responsibility on a subroutine if ZA-state on return is "dormant" because
> this state has special meaning. The "caller" (that is the place in code
> where execution is transferred to, so this include both "parent" and "child")
> may check the ZA-state and use it as per the spec of the "dormant" state that
> is outlined in [1] (6.6.6 and 6.6.7).
>
> Conforming to this would require more code inside of clone() and clone3()
> which hardly is desirable.
>
> For the return to "parent" this could be achieved in theory, but given that
> neither clone() nor clone3() are supposed to be used in the middle of an
> SME operation, if wouldn't be useful. For the "return" to "child" this
> would be particularly difficult to achieve given the complexity of these
> functions and their interfaces. Most importantly, it would be illegal
> and somewhat meaningless to allow a "child" to start execution in the
> "dormant" ZA-state because the very essence of the "dormant" state implies
> that there is a place to return and that there is some outer context that
> we are allowed to interact with.
>
> To sum up, calling __arm_za_disable() to ensure the "off" ZA-state when the
> execution resumes after a call to clone() or clone3() is correct and also
> the most simple way to conform to [1].
>
> Can there be situations when we can avoid calling __arm_za_disable()?
>
> Calling __arm_za_disable() implies certain (sufficiently small) overhead,
> so one might rightly ponder avoiding making a call to this function when
> we can afford not to. The most trivial cases like this (e.g. when the
> calling thread doesn't have access to SME or to the TPIDR2_EL0 register)
> are already handled by this function (see [1] (8.1.3 and 8.1.2)). Reasoning
> about other possible use cases would require making code inside clone() and
> clone3() more complicated and it would defeat the point of trying to make
> an optimisation of not calling __arm_za_disable().
>
> Why can't the kernel do this instead?
>
> The handling of SME state by the kernel is described in [4]. In short,
> kernel must not impose a specific ZA-interface onto a userspace function.
> Interaction with the kernel happens (among other thing) via system calls.
> In Glibc many of the system calls (notably, including SYS_clone and
> SYS_clone3) are used via wrappers, and the kernel has no control of them
> and, moreover, it cannot dictate how these wrappers should behave because
> it is simply outside of the kernel's remit.
>
> However, in certain cases, the kernel may ensure that a "child" doesn't
> start in an incorrect state. This is what is done by the recent change
> included in 6.16 kernel [5]. This is not enough to ensure that code that
> uses clone() and clone3() function conforms to [1] when it runs on a
> system that provides SME, hence this change.
>From the previous discussion, with kernel 6.16+ all *internal* clone/clone3
calls should be correctly handled by the kernel:
* For fork(), CLONE_VM will not be set, and it is safe to inherit both
PSTATE.ZA and TPIDR2_EL0
* For pthread_create() and vfork(), CLONE_VM will be set, and discarding
PSTATE.ZA and TPIDR2_EL0 for the new task doesn't break any existing
assumptions in userspace.
So the issue is only *external* clone calls. In this case I think there is
no need to change clone3.
>
> [1]: https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst
> [2]: https://inbox.sourceware.org/libc-alpha/20250522114828.2291047-1-yury.khrustalev@arm.com
> [3]: https://inbox.sourceware.org/libc-alpha/20250609121407.3316070-1-yury.khrustalev@arm.com
> [4]: https://www.kernel.org/doc/html/v6.16/arch/arm64/sme.html
> [5]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=cde5c32db55740659fca6d56c09b88800d88fd29
> ---
> sysdeps/aarch64/Makefile | 8 ++
> sysdeps/aarch64/tst-sme-clone.c | 93 ++++++++++++++
> sysdeps/aarch64/tst-sme-clone3.c | 148 +++++++++++++++++++++++
> sysdeps/aarch64/tst-sme-helper.h | 34 +++++-
> sysdeps/aarch64/tst-sme-signal.c | 131 ++++++++++++++++++++
> sysdeps/aarch64/tst-sme-za-state.c | 40 +-----
> sysdeps/unix/sysv/linux/aarch64/clone.S | 11 ++
> sysdeps/unix/sysv/linux/aarch64/clone3.S | 11 ++
> 8 files changed, 440 insertions(+), 36 deletions(-)
> create mode 100644 sysdeps/aarch64/tst-sme-clone.c
> create mode 100644 sysdeps/aarch64/tst-sme-clone3.c
> create mode 100644 sysdeps/aarch64/tst-sme-signal.c
>
> diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile
> index bb97d31355..368924c8db 100644
> --- a/sysdeps/aarch64/Makefile
> +++ b/sysdeps/aarch64/Makefile
> @@ -79,8 +79,16 @@ sysdep_routines += \
>
> tests += \
> tst-sme-jmp \
> + tst-sme-signal \
> tst-sme-za-state \
> # tests
> +tests-internal += \
> + tst-sme-clone \
> + tst-sme-clone3 \
> + # tests-internal
> +
> +$(objpfx)tst-sme-clone3: $(objpfx)clone3.o
> +
> endif
>
> ifeq ($(subdir),malloc)
> diff --git a/sysdeps/aarch64/tst-sme-clone.c b/sysdeps/aarch64/tst-sme-clone.c
> new file mode 100644
> index 0000000000..0879d484b2
> --- /dev/null
> +++ b/sysdeps/aarch64/tst-sme-clone.c
> @@ -0,0 +1,93 @@
> +/* Test that ZA state of SME is cleared in both parent and child
> + when clone() syscall is used.
> + Copyright (C) 2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#include <stdio.h>
> +#include <clone3.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/auxv.h>
> +
> +#include <support/check.h>
> +#include <support/xsched.h>
> +#include <support/xunistd.h>
> +
> +#include <support/support.h>
> +#include <support/test-driver.h>
> +
> +#include "tst-sme-helper.h"
> +
> +static int
> +fun (void * const arg)
> +{
> + printf ("in child: %s\n", (const char *)arg);
> + /* Check that ZA state of SME was disabled in child. */
> + check_sme_za_state ("after clone in child", /* Clear. */ true);
> + return 0;
> +}
> +
> +static char __attribute__((aligned(16)))
> +stack[1024 * 1024];
> +
> +static void
> +run (struct blk *ptr)
> +{
> + char *syscall_name = (char *)"clone";
> + printf ("in parent: before %s\n", syscall_name);
> +
> + /* Enabled ZA state so that effect of disabling be observable. */
> + enable_sme_za_state (ptr);
> + check_sme_za_state ("before clone", /* Clear. */ false);
> +
> + pid_t pid = xclone (fun, syscall_name, stack, sizeof (stack),
> + CLONE_NEWUSER | CLONE_NEWNS | SIGCHLD);
> +
> + /* Check that ZA state of SME was disabled in parent. */
> + check_sme_za_state ("after clone in parent", /* Clear. */ true);
> +
> + TEST_VERIFY (xwaitpid (pid, NULL, 0) == pid);
> +}
> +
> +static int
> +do_test (void)
> +{
> + unsigned long hwcap2 = getauxval (AT_HWCAP2);
> + if ((hwcap2 & HWCAP2_SME) == 0)
> + return EXIT_UNSUPPORTED;
> +
> + /* Get current streaming SVE vector length in bytes. */
> + svl = get_svl ();
> + printf ("svl: %lu\n", svl);
> + TEST_VERIFY_EXIT (!(svl < 16 || svl % 16 != 0 || svl >= (1 << 16)));
> +
> + /* Initialise buffer for ZA state of SME. */
> + sme_state = xmalloc (svl * svl);
> + memset (sme_state, 1, svl * svl);
> + struct blk blk = {
> + .za_save_buffer = sme_state,
> + .num_za_save_slices = svl,
> + .__reserved = {0},
> + };
> +
> + run (&blk);
> +
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
> +
> diff --git a/sysdeps/aarch64/tst-sme-clone3.c b/sysdeps/aarch64/tst-sme-clone3.c
> new file mode 100644
> index 0000000000..b057fc1ad2
> --- /dev/null
> +++ b/sysdeps/aarch64/tst-sme-clone3.c
> @@ -0,0 +1,148 @@
> +/* Test that ZA state of SME is cleared in both parent and child
> + when clone3() syscall is used.
> + Copyright (C) 2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#include <stdio.h>
> +#include <clone3.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <errno.h>
> +#include <sys/auxv.h>
> +#include <sys/wait.h>
> +
> +#include <support/check.h>
> +#include <support/xsched.h>
> +#include <support/xunistd.h>
> +#include <support/support.h>
> +#include <support/test-driver.h>
> +
> +#include "tst-sme-helper.h"
> +
> +/* Since clone3 is not a public symbol, we link this test explicitly
> + with clone3.o and have to provide this declaration. */
> +int __clone3 (struct clone_args *cl_args, size_t size,
> + int (*func)(void *arg), void *arg);
> +
> +static int
> +fun (void * const arg)
> +{
> + printf ("in child: %s\n", (const char *)arg);
> + /* Check that ZA state of SME was disabled in child. */
> + check_sme_za_state ("after clone3 in child", /* Clear. */ true);
> + return 0;
> +}
> +
> +static char __attribute__((aligned(16)))
> +stack[1024 * 1024];
> +
> +static void
> +run (struct blk *ptr)
> +{
> + char *syscall_name = (char *)"clone3";
> + struct clone_args args = {
> + .flags = CLONE_VM | CLONE_VFORK,
> + .exit_signal = SIGCHLD,
> + .stack = (uintptr_t) stack,
> + .stack_size = sizeof (stack),
> + };
> + printf ("in parent: before %s\n", syscall_name);
> +
> + /* Enabled ZA state so that effect of disabling be observable. */
> + enable_sme_za_state (ptr);
> + check_sme_za_state ("before clone", /* Clear. */ false);
> +
> + pid_t pid = __clone3 (&args, sizeof (args), fun, syscall_name);
> +
> + /* Check that ZA state of SME was disabled in parent. */
> + check_sme_za_state ("after clone in parent", /* Clear. */ true);
> +
> + printf ("%s child pid: %d\n", syscall_name, pid);
> + if (pid == -1)
> + {
> + if (errno == ENOSYS)
> + {
> + puts ("clone3 syscall is not supported");
> + exit (EXIT_UNSUPPORTED);
> + }
> + perror ("clone3");
> + TEST_VERIFY_EXIT (0);
> + }
> + if (waitid (P_PID, pid, NULL, WEXITED))
> + {
> + perror ("waitid");
> + TEST_VERIFY_EXIT (0);
> + }
> + printf ("in parent: after %s\n", syscall_name);
> +}
> +
> +static int
> +do_test (void)
> +{
> +
> +#if defined __GNUC__ && __GNUC__ >= 14
> + unsigned long hwcap2 = getauxval (AT_HWCAP2);
> + if ((hwcap2 & HWCAP2_SME) == 0)
> + return EXIT_UNSUPPORTED;
> +#else
> + /* In this case we won't be able to use the __arm_za_disable()
> + subroutine from libgcc. */
> + puts ("compiler doesn't support SME");
> + exit (EXIT_UNSUPPORTED);
> +#endif
> +
> + /* Get current streaming SVE vector length in bytes. */
> + svl = get_svl ();
> + printf ("svl: %lu\n", svl);
> + TEST_VERIFY_EXIT (!(svl < 16 || svl % 16 != 0 || svl >= (1 << 16)));
> +
> + /* Initialise buffer for ZA state of SME. */
> + sme_state = xmalloc (svl * svl);
> + memset (sme_state, 1, svl * svl);
> + struct blk blk = {
> + .za_save_buffer = sme_state,
> + .num_za_save_slices = svl,
> + .__reserved = {0},
> + };
> +
> + run (&blk);
> +
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
> +
> +/* Workaround to simplify linking with clone3.o. */
> +void __syscall_error(int code)
> +{
> + int err = -code;
> + fprintf (stderr, "syscall error %d (%s)\n", err, strerror (err));
> + exit (err);
> +}
> +
> +/* Provided by libgcc since GCC 14. */
> +extern void __arm_za_disable (void);
> +
> +/* We don't want to pull all the dependencies of Glibc's implementation
> + of __arm_za_disable, so we use symbol from libgcc because it is
> + available for this test executable. */
> +void __libc_arm_za_disable (void)
> +{
> +#if defined __GNUC__ && __GNUC__ >= 14
> + __arm_za_disable ();
> +#endif
> +}
> diff --git a/sysdeps/aarch64/tst-sme-helper.h b/sysdeps/aarch64/tst-sme-helper.h
> index f049416c2b..8fafd60821 100644
> --- a/sysdeps/aarch64/tst-sme-helper.h
> +++ b/sysdeps/aarch64/tst-sme-helper.h
> @@ -47,7 +47,7 @@ get_tpidr2 (void)
> return (void *) x0;
> }
>
> -/* Obtains current streaming SVE vector register size. */
> +/* Obtains current streaming SVE vector length in bytes. */
> static unsigned long
> get_svl (void)
> {
> @@ -95,3 +95,35 @@ set_tpidr2 (struct blk *blk)
> ".inst 0xd51bd0a0 /* msr tpidr2_el0, x0 */\n"
> :: "r"(x0) : "memory");
> }
> +
> +/* Check if SME state is disabled (when CLEAR is true) or
> + enabled (when CLEAR is false). */
> +static void __attribute__ ((unused))
> +check_sme_za_state (const char msg[], bool clear)
> +{
> + unsigned long svcr = get_svcr ();
> + void *tpidr2 = get_tpidr2 ();
> + printf ("[%s]\n", msg);
> + printf ("svcr = %016lx\n", svcr);
> + printf ("tpidr2 = %016lx\n", (unsigned long)tpidr2);
> + if (clear)
> + {
> + TEST_VERIFY (svcr == 0);
> + TEST_VERIFY (tpidr2 == NULL);
> + }
> + else
> + {
> + TEST_VERIFY (svcr != 0);
> + TEST_VERIFY (tpidr2 != NULL);
> + }
> +}
> +
> +static uint8_t *sme_state;
> +
> +static void __attribute__ ((unused))
> +enable_sme_za_state (struct blk *ptr)
> +{
> + start_za ();
> + set_tpidr2 (ptr);
> + load_za (sme_state);
> +}
> diff --git a/sysdeps/aarch64/tst-sme-signal.c b/sysdeps/aarch64/tst-sme-signal.c
> new file mode 100644
> index 0000000000..7aa137684d
> --- /dev/null
> +++ b/sysdeps/aarch64/tst-sme-signal.c
> @@ -0,0 +1,131 @@
> +/* Test handling of SME state in a signal handler.
> + Copyright (C) 2025 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#include <support/check.h>
> +#include <support/support.h>
> +#include <support/xsignal.h>
> +#include <support/test-driver.h>
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdint.h>
> +#include <sys/auxv.h>
> +
> +#include "tst-sme-helper.h"
> +
> +static bool sme2_supported;
> +
> +static struct _aarch64_ctx *
> +extension (void *p)
> +{
> + return p;
> +}
> +
> +#ifndef TPIDR2_MAGIC
> +#define TPIDR2_MAGIC 0x54504902
> +#endif
> +
> +#ifndef ZA_MAGIC
> +#define ZA_MAGIC 0x54366345
> +#endif
> +
> +#ifndef ZT_MAGIC
> +#define ZT_MAGIC 0x5a544e01
> +#endif
> +
> +#ifndef EXTRA_MAGIC
> +#define EXTRA_MAGIC 0x45585401
> +#endif
> +
> +static void
> +handler (int signo, siginfo_t *si, void *ctx)
> +{
> + TEST_VERIFY (signo == SIGUSR1);
> + printf ("in the handler: %d and %p\n", signo, si);
> + check_sme_za_state ("during signal", true /* State is clear. */);
> + ucontext_t *uc = ctx;
> + void *p = uc->uc_mcontext.__reserved;
> + unsigned int found = 0;
> + uint32_t m;
> + while ((m = extension (p)->magic))
> + {
> + if (m == TPIDR2_MAGIC)
> + {
> + printf ("found TPIDR2_MAGIC\n");
> + found += 1;
> + }
> + if (m == ZA_MAGIC)
> + {
> + printf ("found ZA_MAGIC\n");
> + found += 1;
> + }
> + if (m == ZT_MAGIC)
> + {
> + printf ("found ZT_MAGIC\n");
> + found += 1;
> + }
> + if (m == EXTRA_MAGIC)
> + {
> + printf ("found EXTRA_MAGIC\n");
> + struct { struct _aarch64_ctx h; uint64_t data; } *e = p;
> + p = (char *)e->data;
> + continue;
> + }
> + p = (char *)p + extension (p)->size;
> + }
> + TEST_VERIFY (sme2_supported ? found == 3 : found == 2);
> +}
> +
> +static int
> +do_test (void)
> +{
> + unsigned long hwcap2 = getauxval (AT_HWCAP2);
> + if ((hwcap2 & HWCAP2_SME) == 0)
> + return EXIT_UNSUPPORTED;
> + sme2_supported = hwcap2 & HWCAP2_SME2;
> +
> + /* Get current streaming SVE vector length in bytes. */
> + svl = get_svl ();
> + printf ("svl: %lu\n", svl);
> + TEST_VERIFY_EXIT (!(svl < 16 || svl % 16 != 0 || svl >= (1 << 16)));
> +
> + struct sigaction sigact;
> + sigemptyset (&sigact.sa_mask);
> + sigact.sa_flags = 0;
> + sigact.sa_flags |= SA_SIGINFO;
> + sigact.sa_sigaction = handler;
> + xsigaction (SIGUSR1, &sigact, NULL);
> +
> + struct blk blk = {
> + .za_save_buffer = (void *)0x10,
> + .num_za_save_slices = svl,
> + };
> +
> + start_za ();
> + set_tpidr2 (&blk);
> +
> + check_sme_za_state ("before signal", false /* State is not clear. */);
> +
> + xraise (SIGUSR1);
> +
> + check_sme_za_state ("after signal", false /* State is not clear. */);
> +
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/sysdeps/aarch64/tst-sme-za-state.c b/sysdeps/aarch64/tst-sme-za-state.c
> index 63f6eebeb4..df43b303e7 100644
> --- a/sysdeps/aarch64/tst-sme-za-state.c
> +++ b/sysdeps/aarch64/tst-sme-za-state.c
> @@ -28,36 +28,6 @@
>
> #include "tst-sme-helper.h"
>
> -static uint8_t *state;
> -
> -static void
> -enable_sme_za_state (struct blk *ptr)
> -{
> - set_tpidr2 (ptr);
> - start_za ();
> - load_za (state);
> -}
> -
> -static void
> -check_sme_za_state (const char msg[], bool clear)
> -{
> - unsigned long svcr = get_svcr ();
> - void *tpidr2 = get_tpidr2 ();
> - printf ("[%s]\n", msg);
> - printf ("svcr = %016lx\n", svcr);
> - printf ("tpidr2 = %016lx\n", (unsigned long)tpidr2);
> - if (clear)
> - {
> - TEST_VERIFY (svcr == 0);
> - TEST_VERIFY (tpidr2 == NULL);
> - }
> - else
> - {
> - TEST_VERIFY (svcr != 0);
> - TEST_VERIFY (tpidr2 != NULL);
> - }
> -}
> -
> static void
> run (struct blk *ptr)
> {
> @@ -96,23 +66,23 @@ do_test (void)
> if ((hwcap2 & HWCAP2_SME) == 0)
> return EXIT_UNSUPPORTED;
>
> - /* Get current streaming SVE vector register size. */
> + /* Get current streaming SVE vector length in bytes. */
> svl = get_svl ();
> printf ("svl: %lu\n", svl);
> TEST_VERIFY_EXIT (!(svl < 16 || svl % 16 != 0 || svl >= (1 << 16)));
>
> /* Initialise buffer for ZA state of SME. */
> - state = xmalloc (svl * svl);
> - memset (state, 1, svl * svl);
> + sme_state = xmalloc (svl * svl);
> + memset (sme_state, 1, svl * svl);
> struct blk blk = {
> - .za_save_buffer = state,
> + .za_save_buffer = sme_state,
> .num_za_save_slices = svl,
> .__reserved = {0},
> };
>
> run (&blk);
>
> - free (state);
> + free (sme_state);
> return 0;
> }
>
> diff --git a/sysdeps/unix/sysv/linux/aarch64/clone.S b/sysdeps/unix/sysv/linux/aarch64/clone.S
> index 40015c6933..66812e7efd 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/clone.S
> +++ b/sysdeps/unix/sysv/linux/aarch64/clone.S
> @@ -45,6 +45,17 @@ ENTRY(__clone)
> and x1, x1, -16
> cbz x1, .Lsyscall_error
>
> + /* Clear ZA state of SME. */
> + /* The calling convention of __libc_arm_za_disable allows to do
> + this thus allowing to avoid saving to and reading from stack.
> + As a result we also don't need to sign the return address and
> + check it after returning because it is not stored to stack. */
> + mov x13, x30
> + cfi_register (x30, x13)
> + bl __libc_arm_za_disable
> + mov x30, x13
> + cfi_register (x13, x30)
> +
> /* Do the system call. */
> /* X0:flags, x1:newsp, x2:parenttidptr, x3:newtls, x4:childtid. */
> mov x0, x2 /* flags */
> diff --git a/sysdeps/unix/sysv/linux/aarch64/clone3.S b/sysdeps/unix/sysv/linux/aarch64/clone3.S
> index c9ca845ef2..b2e0d10eb2 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/clone3.S
> +++ b/sysdeps/unix/sysv/linux/aarch64/clone3.S
> @@ -46,6 +46,17 @@ ENTRY(__clone3)
> cbz x10, .Lsyscall_error /* No NULL cl_args pointer. */
> cbz x2, .Lsyscall_error /* No NULL function pointer. */
>
> + /* Clear ZA state of SME. */
> + /* The calling convention of __libc_arm_za_disable allows to do
> + this thus allowing to avoid saving to and reading from stack.
> + As a result we also don't need to sign the return address and
> + check it after returning because it is not stored to stack. */
> + mov x13, x30
> + cfi_register (x30, x13)
> + bl __libc_arm_za_disable
> + mov x30, x13
> + cfi_register (x13, x30)
> +
> /* Do the system call, the kernel expects:
> x8: system call number
> x0: cl_args
More information about the Libc-alpha
mailing list