diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile index bb97d31355..9479fb9679 100644 --- a/sysdeps/aarch64/Makefile +++ b/sysdeps/aarch64/Makefile @@ -79,8 +79,18 @@ sysdep_routines += \ tests += \ tst-sme-jmp \ + tst-sme-signal \ tst-sme-za-state \ # tests +tests-internal += \ + tst-sme-clone \ + tst-sme-clone3 \ + tst-sme-fork \ + tst-sme-vfork \ + # tests-internal + +$(objpfx)tst-sme-clone3: $(objpfx)clone3.o $(objpfx)__arm_za_disable.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..7106ec7926 --- /dev/null +++ b/sysdeps/aarch64/tst-sme-clone.c @@ -0,0 +1,53 @@ +/* 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 + . */ + +#include "tst-sme-skeleton.c" + +#include + +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); +} diff --git a/sysdeps/aarch64/tst-sme-clone3.c b/sysdeps/aarch64/tst-sme-clone3.c new file mode 100644 index 0000000000..402b040cfd --- /dev/null +++ b/sysdeps/aarch64/tst-sme-clone3.c @@ -0,0 +1,84 @@ +/* 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 + . */ + +#include "tst-sme-skeleton.c" + +#include + +#include +#include +#include + +/* 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]; + +/* Required by __arm_za_disable.o and provided by the startup code + as a hidden symbol. */ +uint64_t _dl_hwcap2; + +static void +run (struct blk *ptr) +{ + _dl_hwcap2 = getauxval (AT_HWCAP2); + + 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 clone3", /* 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 clone3 in parent", /* Clear. */ true); + + printf ("%s child pid: %d\n", syscall_name, pid); + + xwaitpid (pid, NULL, 0); + printf ("in parent: after %s\n", syscall_name); +} + +/* 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); +} diff --git a/sysdeps/aarch64/tst-sme-fork.c b/sysdeps/aarch64/tst-sme-fork.c new file mode 100644 index 0000000000..b003b08884 --- /dev/null +++ b/sysdeps/aarch64/tst-sme-fork.c @@ -0,0 +1,43 @@ +/* Test that ZA state of SME is cleared in both parent and child + when fork() function 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 + . */ + +#include "tst-sme-skeleton.c" + +static void +run (struct blk *blk) +{ + /* Enabled ZA state so that effect of disabling be observable. */ + enable_sme_za_state (blk); + check_sme_za_state ("before fork", /* Clear. */ false); + fflush (stdout); + + pid_t pid = xfork (); + + if (pid == 0) + { + /* Check that ZA state of SME was disabled in child. */ + check_sme_za_state ("after fork in child", /* Clear. */ true); + exit (0); + } + + /* Check that ZA state of SME was disabled in parent. */ + check_sme_za_state ("after fork in parent", /* Clear. */ true); + + TEST_VERIFY (xwaitpid (pid, NULL, 0) == pid); +} diff --git a/sysdeps/aarch64/tst-sme-helper.h b/sysdeps/aarch64/tst-sme-helper.h index f049416c2b..ab9c503e45 100644 --- a/sysdeps/aarch64/tst-sme-helper.h +++ b/sysdeps/aarch64/tst-sme-helper.h @@ -16,9 +16,6 @@ License along with the GNU C Library; if not, see . */ -/* Streaming SVE vector register size. */ -static unsigned long svl; - struct blk { void *za_save_buffer; uint16_t num_za_save_slices; @@ -68,10 +65,10 @@ start_za (void) /* Load data into ZA byte by byte from p. */ static void __attribute__ ((noinline)) -load_za (const void *p) +load_za (const void *buf, unsigned long svl) { register unsigned long x15 asm ("x15") = 0; - register unsigned long x16 asm ("x16") = (unsigned long)p; + register unsigned long x16 asm ("x16") = (unsigned long)buf; register unsigned long x17 asm ("x17") = svl; asm volatile ( diff --git a/sysdeps/aarch64/tst-sme-jmp.c b/sysdeps/aarch64/tst-sme-jmp.c index 103897ad36..b2d21c6e1a 100644 --- a/sysdeps/aarch64/tst-sme-jmp.c +++ b/sysdeps/aarch64/tst-sme-jmp.c @@ -29,6 +29,9 @@ #include "tst-sme-helper.h" +/* Streaming SVE vector register size. */ +static unsigned long svl; + static uint8_t *za_orig; static uint8_t *za_dump; static uint8_t *za_save; @@ -82,7 +85,7 @@ longjmp_test (void) FAIL_EXIT1 ("svcr != 0: %lu", svcr); set_tpidr2 (&blk); start_za (); - load_za (za_orig); + load_za (za_orig, svl); print_data ("za save space", za_save); p = get_tpidr2 (); @@ -131,7 +134,7 @@ setcontext_test (void) FAIL_EXIT1 ("svcr != 0: %lu", svcr); set_tpidr2 (&blk); start_za (); - load_za (za_orig); + load_za (za_orig, svl); print_data ("za save space", za_save); p = get_tpidr2 (); diff --git a/sysdeps/aarch64/tst-sme-signal.c b/sysdeps/aarch64/tst-sme-signal.c new file mode 100644 index 0000000000..b4b07bcc44 --- /dev/null +++ b/sysdeps/aarch64/tst-sme-signal.c @@ -0,0 +1,115 @@ +/* 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 + . */ + +#include "tst-sme-skeleton.c" + +#include + +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 + +/* We use a pipe to make sure that the final check of the SME state + happens after signal handler finished. */ +static int pipefd[2]; + +#define WRITE(msg) xwrite (1, msg, sizeof (msg)); + +static void +handler (int signo, siginfo_t *si, void *ctx) +{ + TEST_VERIFY (signo == SIGUSR1); + WRITE ("in the handler\n"); + 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) + { + WRITE ("found TPIDR2_MAGIC\n"); + found += 1; + } + if (m == ZA_MAGIC) + { + WRITE ("found ZA_MAGIC\n"); + found += 1; + } + if (m == ZT_MAGIC) + { + WRITE ("found ZT_MAGIC\n"); + found += 1; + } + if (m == EXTRA_MAGIC) + { + WRITE ("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_COMPARE (found, 3); + + /* Signal that the wait is over (see below). */ + char message = '\0'; + xwrite (pipefd[1], &message, 1); +} + +static void +run (struct blk *blk) +{ + xpipe (pipefd); + + struct sigaction sigact; + sigemptyset (&sigact.sa_mask); + sigact.sa_flags = 0; + sigact.sa_flags |= SA_SIGINFO; + sigact.sa_sigaction = handler; + xsigaction (SIGUSR1, &sigact, NULL); + + enable_sme_za_state (blk); + check_sme_za_state ("before signal", false /* State is not clear. */); + xraise (SIGUSR1); + + /* Wait for signal handler to complete. */ + char response; + xread (pipefd[0], &response, 1); + + check_sme_za_state ("after signal", false /* State is not clear. */); +} diff --git a/sysdeps/aarch64/tst-sme-skeleton.c b/sysdeps/aarch64/tst-sme-skeleton.c new file mode 100644 index 0000000000..ba84dda1cb --- /dev/null +++ b/sysdeps/aarch64/tst-sme-skeleton.c @@ -0,0 +1,101 @@ +/* Template for SME tests. + 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 + . */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "tst-sme-helper.h" + +/* Streaming SVE vector register size. */ +static unsigned long svl; + +static uint8_t *state; + +static void +enable_sme_za_state (struct blk *blk) +{ + start_za (); + set_tpidr2 (blk); + load_za (blk, svl); +} + +/* Check if SME state is disabled (when CLEAR is true) or + enabled (when CLEAR is false). */ +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); + } +} + +/* Should be defined in actual test that includes this + skeleton file. */ +static void +run (struct blk *ptr); + +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. */ + state = xmalloc (svl * svl); + memset (state, 1, svl * svl); + struct blk blk = { + .za_save_buffer = state, + .num_za_save_slices = svl, + .__reserved = {0}, + }; + + run (&blk); + + free (state); + return 0; +} + +#include diff --git a/sysdeps/aarch64/tst-sme-vfork.c b/sysdeps/aarch64/tst-sme-vfork.c new file mode 100644 index 0000000000..3feea065e5 --- /dev/null +++ b/sysdeps/aarch64/tst-sme-vfork.c @@ -0,0 +1,43 @@ +/* Test that ZA state of SME is cleared in both parent and child + when vfork() function 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 + . */ + +#include "tst-sme-skeleton.c" + +static void +run (struct blk *blk) +{ + /* Enabled ZA state so that effect of disabling be observable. */ + enable_sme_za_state (blk); + check_sme_za_state ("before vfork", /* Clear. */ false); + fflush (stdout); + + pid_t pid = vfork (); + + if (pid == 0) + { + /* Check that ZA state of SME was disabled in child. */ + check_sme_za_state ("after vfork in child", /* Clear. */ true); + _exit (0); + } + + /* Check that ZA state of SME was disabled in parent. */ + check_sme_za_state ("after vfork in parent", /* Clear. */ true); + + TEST_VERIFY (xwaitpid (pid, NULL, 0) == pid); +} diff --git a/sysdeps/aarch64/tst-sme-za-state.c b/sysdeps/aarch64/tst-sme-za-state.c index 63f6eebeb4..00118ef506 100644 --- a/sysdeps/aarch64/tst-sme-za-state.c +++ b/sysdeps/aarch64/tst-sme-za-state.c @@ -16,47 +16,9 @@ License along with the GNU C Library; if not, see . */ -#include +#include "tst-sme-skeleton.c" + #include -#include -#include -#include - -#include -#include -#include - -#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) @@ -88,32 +50,3 @@ run (struct blk *ptr) TEST_COMPARE (ret, 42); check_sme_za_state ("after longjmp", /* Clear. */ true); } - -static int -do_test (void) -{ - unsigned long hwcap2 = getauxval (AT_HWCAP2); - if ((hwcap2 & HWCAP2_SME) == 0) - return EXIT_UNSUPPORTED; - - /* Get current streaming SVE vector register size. */ - 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); - struct blk blk = { - .za_save_buffer = state, - .num_za_save_slices = svl, - .__reserved = {0}, - }; - - run (&blk); - - free (state); - return 0; -} - -#include