[PATCH v4 1/2] aarch64: clear ZA state of SME before clone and clone3

enh enh@google.com
Tue Sep 2 15:59:32 GMT 2025


On Mon, Sep 1, 2025 at 5:51 AM Yury Khrustalev <yury.khrustalev@arm.com> 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.

is it time to make clone3() public? i've had a few requests over in Android too.

(avoiding this kind of "oh, but for $ARCH you also have to do $TRICK"
in callers being the answer to "why not just use syscall()?".)

> 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.
>
> 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.
>
> [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
> --
> 2.47.2
>


More information about the Libc-alpha mailing list