Message ID | e948e94e5b85cea99c11d6f1fe342bad217ba95d.1702051831.git.szabolcs.nagy@arm.com |
---|---|
State | New |
Headers | show |
Series | aarch64: Add SME support | expand |
On 08/12/23 13:32, Szabolcs Nagy wrote: > Includes test for setcontext too. > > The test directly checks after longjmp if ZA got disabled and the > ZA contents got saved following the lazy saving scheme. It does not > use ACLE code to verify that gcc can interoperate with glibc. LGTM, thanks. Some minor suggestions below. Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org> > --- > sysdeps/aarch64/Makefile | 3 + > sysdeps/aarch64/tst-sme-jmp.c | 278 ++++++++++++++++++++++++++++++++++ > 2 files changed, 281 insertions(+) > create mode 100644 sysdeps/aarch64/tst-sme-jmp.c > > diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile > index 9d8844d9c8..141d7d9cc2 100644 > --- a/sysdeps/aarch64/Makefile > +++ b/sysdeps/aarch64/Makefile > @@ -68,6 +68,9 @@ sysdep_routines += \ > __mtag_tag_zero_region \ > __mtag_tag_region \ > __arm_za_disable > + > +tests += \ > + tst-sme-jmp > endif > > ifeq ($(subdir),malloc) Ok. > diff --git a/sysdeps/aarch64/tst-sme-jmp.c b/sysdeps/aarch64/tst-sme-jmp.c > new file mode 100644 > index 0000000000..08dd291b0c > --- /dev/null > +++ b/sysdeps/aarch64/tst-sme-jmp.c > @@ -0,0 +1,278 @@ > +/* Test for SME longjmp. > + Copyright (C) 2023 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 <stdint.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <setjmp.h> > +#include <ucontext.h> > +#include <sys/auxv.h> > +#include <support/check.h> > + > +struct blk { > + void *za_save_buffer; > + uint16_t num_za_save_slices; > + char __reserved[6]; > +}; > + > +static unsigned long svl; > +static uint8_t *za_orig; > +static uint8_t *za_dump; > +static uint8_t *za_save; > + > +static unsigned long > +get_svl (void) > +{ > + register unsigned long x0 asm ("x0"); > + asm volatile ( > + ".inst 0x04bf5820 /* rdsvl x0, 1 */\n" > + : "=r" (x0)); > + return x0; > +} > + > +/* PSTATE.ZA = 1, set ZA state to active. */ > +static void > +start_za (void) > +{ > + asm volatile ( > + ".inst 0xd503457f /* smstart za */"); > +} > + > +/* Read SVCR to get SM (bit0) and ZA (bit1) state. */ > +static unsigned long > +get_svcr (void) > +{ > + register unsigned long x0 asm ("x0"); > + asm volatile ( > + ".inst 0xd53b4240 /* mrs x0, svcr */\n" > + : "=r" (x0)); > + return x0; > +} > + > +/* Load data into ZA byte by byte from p. */ > +static void __attribute__ ((noinline)) > +load_za (const void *p) > +{ > + register unsigned long x15 asm ("x15") = 0; > + register unsigned long x16 asm ("x16") = (unsigned long)p; > + register unsigned long x17 asm ("x17") = svl; > + > + asm volatile ( > + ".inst 0xd503437f /* smstart sm */\n" > + ".L_ldr_loop:\n" > + ".inst 0xe1006200 /* ldr za[w15, 0], [x16] */\n" > + "add w15, w15, 1\n" > + ".inst 0x04305030 /* addvl x16, x16, 1 */\n" > + "cmp w15, w17\n" > + "bne .L_ldr_loop\n" > + ".inst 0xd503427f /* smstop sm */\n" > + : "+r"(x15), "+r"(x16), "+r"(x17)); > +} > + > +/* Set tpidr2 to BLK. */ > +static void > +set_tpidr2 (struct blk *blk) > +{ > + register unsigned long x0 asm ("x0") = (unsigned long)blk; > + asm volatile ( > + ".inst 0xd51bd0a0 /* msr tpidr2_el0, x0 */\n" > + :: "r"(x0) : "memory"); > +} > + > +/* Returns tpidr2. */ > +static void * > +get_tpidr2 (void) > +{ > + register unsigned long x0 asm ("x0"); > + asm volatile ( > + ".inst 0xd53bd0a0 /* mrs x0, tpidr2_el0 */\n" > + : "=r"(x0) :: "memory"); > + return (void *) x0; > +} > + > +static void > +print_data(const char *msg, void *p) > +{ > + unsigned char *a = p; > + printf ("%s:\n", msg); > + for (int i = 0; i < svl; i++) > + { > + printf ("%d: ", i); > + for (int j = 0; j < svl; j++) > + printf("%02x,", a[i*svl+j]); > + printf("\n"); > + } > + printf(".\n"); > + fflush (stdout); > +} > + > +__attribute__ ((noinline)) > +static void > +do_longjmp (jmp_buf env) > +{ > + longjmp (env, 1); > +} > + > +__attribute__ ((noinline)) > +static void > +do_setcontext (const ucontext_t *p) > +{ > + setcontext (p); > +} > + > +static void > +longjmp_test (void) > +{ > + unsigned long svcr; > + jmp_buf env; > + void *p; > + int r; > + struct blk blk = {za_save, svl, {0}}; > + > + printf ("longjmp test:\n"); > + p = get_tpidr2 (); > + printf ("initial tp2 = %p\n", p); > + if (p != NULL) > + FAIL_EXIT1 ("tpidr2 is not initialized to 0"); > + svcr = get_svcr (); > + if (svcr != 0) > + FAIL_EXIT1 ("svcr != 0: %lu", svcr); > + set_tpidr2 (&blk); > + start_za (); > + load_za (za_orig); > + > + print_data ("za save space", za_save); > + p = get_tpidr2 (); > + printf ("before setjmp: tp2 = %p\n", p); > + if (p != &blk) > + FAIL_EXIT1 ("tpidr2 is not set to BLK %p", (void *)&blk); > + if (setjmp (env) == 0) > + { > + p = get_tpidr2 (); > + printf ("before longjmp: tp2 = %p\n", p); > + if (p != &blk) > + FAIL_EXIT1 ("tpidr2 is clobbered"); > + do_longjmp (env); > + FAIL_EXIT1 ("longjmp returned"); > + } > + p = get_tpidr2 (); > + printf ("after longjmp: tp2 = %p\n", p); > + if (p != NULL) > + FAIL_EXIT1 ("tpidr2 is not set to 0"); > + svcr = get_svcr (); > + if (svcr != 0) > + FAIL_EXIT1 ("svcr != 0: %lu", svcr); > + print_data ("za save space", za_save); > + r = memcmp (za_orig, za_save, svl*svl); > + if (r != 0) > + FAIL_EXIT1 ("saving za failed"); > +} > + > +static void > +setcontext_test (void) > +{ > + volatile int setcontext_done = 0; > + unsigned long svcr; > + ucontext_t ctx; > + void *p; > + int r; > + struct blk blk = {za_save, svl, {0}}; > + > + printf ("setcontext test:\n"); > + p = get_tpidr2 (); > + printf ("initial tp2 = %p\n", p); > + if (p != NULL) > + FAIL_EXIT1 ("tpidr2 is not initialized to 0"); > + svcr = get_svcr (); > + if (svcr != 0) > + FAIL_EXIT1 ("svcr != 0: %lu", svcr); > + set_tpidr2 (&blk); > + start_za (); > + load_za (za_orig); > + > + print_data ("za save space", za_save); > + p = get_tpidr2 (); > + printf ("before getcontext: tp2 = %p\n", p); > + if (p != &blk) > + FAIL_EXIT1 ("tpidr2 is not set to BLK %p", (void *)&blk); > + r = getcontext (&ctx); > + if (r != 0) > + FAIL_EXIT1 ("getcontext failed"); > + if (setcontext_done == 0) > + { > + p = get_tpidr2 (); > + printf ("before setcontext: tp2 = %p\n", p); > + if (p != &blk) > + FAIL_EXIT1 ("tpidr2 is clobbered"); > + setcontext_done = 1; > + do_setcontext (&ctx); > + FAIL_EXIT1 ("setcontext returned"); > + } > + p = get_tpidr2 (); > + printf ("after setcontext: tp2 = %p\n", p); > + if (p != NULL) > + FAIL_EXIT1 ("tpidr2 is not set to 0"); > + svcr = get_svcr (); > + if (svcr != 0) > + FAIL_EXIT1 ("svcr != 0: %lu", svcr); > + print_data ("za save space", za_save); > + r = memcmp (za_orig, za_save, svl*svl); > + if (r != 0) > + FAIL_EXIT1 ("saving za failed"); > +} > + > +static int > +do_test (void) > +{ > + unsigned long hwcap2; > + > + hwcap2 = getauxval (AT_HWCAP2); > + if ((hwcap2 & HWCAP2_SME) == 0) > + return 77; Use EXIT_UNSUPPORTED here. > + > + svl = get_svl (); > + printf ("svl: %lu\n", svl); > + if (svl < 16 || svl % 16 != 0 || svl >= (1 << 16)) > + FAIL_EXIT1 ("invalid svl"); > + > + za_orig = malloc (svl*svl); > + za_save = malloc (svl*svl); > + za_dump = malloc (svl*svl); Use xmalloc here (or xcalloc). > + memset (za_orig, 1, svl*svl); > + memset (za_save, 2, svl*svl); > + memset (za_dump, 3, svl*svl); > + for (int i = 0; i < svl; i++) > + for (int j = 0; j < svl; j++) > + za_orig[i*svl+j] = i*svl+j; > + print_data ("original data", za_orig); > + > + longjmp_test (); > + > + memset (za_save, 2, svl*svl); > + memset (za_dump, 3, svl*svl); > + > + setcontext_test (); > + > + free (za_orig); > + free (za_save); > + free (za_dump); > + return 0; > +} > + > +#include <support/test-driver.c>
The 12/28/2023 11:36, Adhemerval Zanella Netto wrote: > On 08/12/23 13:32, Szabolcs Nagy wrote: > > Includes test for setcontext too. > > > > The test directly checks after longjmp if ZA got disabled and the > > ZA contents got saved following the lazy saving scheme. It does not > > use ACLE code to verify that gcc can interoperate with glibc. > > LGTM, thanks. Some minor suggestions below. > > Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org> thanks. > > + hwcap2 = getauxval (AT_HWCAP2); > > + if ((hwcap2 & HWCAP2_SME) == 0) > > + return 77; > > Use EXIT_UNSUPPORTED here. changed. > > + za_orig = malloc (svl*svl); > > + za_save = malloc (svl*svl); > > + za_dump = malloc (svl*svl); > > Use xmalloc here (or xcalloc). updated to xmalloc.
diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile index 9d8844d9c8..141d7d9cc2 100644 --- a/sysdeps/aarch64/Makefile +++ b/sysdeps/aarch64/Makefile @@ -68,6 +68,9 @@ sysdep_routines += \ __mtag_tag_zero_region \ __mtag_tag_region \ __arm_za_disable + +tests += \ + tst-sme-jmp endif ifeq ($(subdir),malloc) diff --git a/sysdeps/aarch64/tst-sme-jmp.c b/sysdeps/aarch64/tst-sme-jmp.c new file mode 100644 index 0000000000..08dd291b0c --- /dev/null +++ b/sysdeps/aarch64/tst-sme-jmp.c @@ -0,0 +1,278 @@ +/* Test for SME longjmp. + Copyright (C) 2023 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 <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <setjmp.h> +#include <ucontext.h> +#include <sys/auxv.h> +#include <support/check.h> + +struct blk { + void *za_save_buffer; + uint16_t num_za_save_slices; + char __reserved[6]; +}; + +static unsigned long svl; +static uint8_t *za_orig; +static uint8_t *za_dump; +static uint8_t *za_save; + +static unsigned long +get_svl (void) +{ + register unsigned long x0 asm ("x0"); + asm volatile ( + ".inst 0x04bf5820 /* rdsvl x0, 1 */\n" + : "=r" (x0)); + return x0; +} + +/* PSTATE.ZA = 1, set ZA state to active. */ +static void +start_za (void) +{ + asm volatile ( + ".inst 0xd503457f /* smstart za */"); +} + +/* Read SVCR to get SM (bit0) and ZA (bit1) state. */ +static unsigned long +get_svcr (void) +{ + register unsigned long x0 asm ("x0"); + asm volatile ( + ".inst 0xd53b4240 /* mrs x0, svcr */\n" + : "=r" (x0)); + return x0; +} + +/* Load data into ZA byte by byte from p. */ +static void __attribute__ ((noinline)) +load_za (const void *p) +{ + register unsigned long x15 asm ("x15") = 0; + register unsigned long x16 asm ("x16") = (unsigned long)p; + register unsigned long x17 asm ("x17") = svl; + + asm volatile ( + ".inst 0xd503437f /* smstart sm */\n" + ".L_ldr_loop:\n" + ".inst 0xe1006200 /* ldr za[w15, 0], [x16] */\n" + "add w15, w15, 1\n" + ".inst 0x04305030 /* addvl x16, x16, 1 */\n" + "cmp w15, w17\n" + "bne .L_ldr_loop\n" + ".inst 0xd503427f /* smstop sm */\n" + : "+r"(x15), "+r"(x16), "+r"(x17)); +} + +/* Set tpidr2 to BLK. */ +static void +set_tpidr2 (struct blk *blk) +{ + register unsigned long x0 asm ("x0") = (unsigned long)blk; + asm volatile ( + ".inst 0xd51bd0a0 /* msr tpidr2_el0, x0 */\n" + :: "r"(x0) : "memory"); +} + +/* Returns tpidr2. */ +static void * +get_tpidr2 (void) +{ + register unsigned long x0 asm ("x0"); + asm volatile ( + ".inst 0xd53bd0a0 /* mrs x0, tpidr2_el0 */\n" + : "=r"(x0) :: "memory"); + return (void *) x0; +} + +static void +print_data(const char *msg, void *p) +{ + unsigned char *a = p; + printf ("%s:\n", msg); + for (int i = 0; i < svl; i++) + { + printf ("%d: ", i); + for (int j = 0; j < svl; j++) + printf("%02x,", a[i*svl+j]); + printf("\n"); + } + printf(".\n"); + fflush (stdout); +} + +__attribute__ ((noinline)) +static void +do_longjmp (jmp_buf env) +{ + longjmp (env, 1); +} + +__attribute__ ((noinline)) +static void +do_setcontext (const ucontext_t *p) +{ + setcontext (p); +} + +static void +longjmp_test (void) +{ + unsigned long svcr; + jmp_buf env; + void *p; + int r; + struct blk blk = {za_save, svl, {0}}; + + printf ("longjmp test:\n"); + p = get_tpidr2 (); + printf ("initial tp2 = %p\n", p); + if (p != NULL) + FAIL_EXIT1 ("tpidr2 is not initialized to 0"); + svcr = get_svcr (); + if (svcr != 0) + FAIL_EXIT1 ("svcr != 0: %lu", svcr); + set_tpidr2 (&blk); + start_za (); + load_za (za_orig); + + print_data ("za save space", za_save); + p = get_tpidr2 (); + printf ("before setjmp: tp2 = %p\n", p); + if (p != &blk) + FAIL_EXIT1 ("tpidr2 is not set to BLK %p", (void *)&blk); + if (setjmp (env) == 0) + { + p = get_tpidr2 (); + printf ("before longjmp: tp2 = %p\n", p); + if (p != &blk) + FAIL_EXIT1 ("tpidr2 is clobbered"); + do_longjmp (env); + FAIL_EXIT1 ("longjmp returned"); + } + p = get_tpidr2 (); + printf ("after longjmp: tp2 = %p\n", p); + if (p != NULL) + FAIL_EXIT1 ("tpidr2 is not set to 0"); + svcr = get_svcr (); + if (svcr != 0) + FAIL_EXIT1 ("svcr != 0: %lu", svcr); + print_data ("za save space", za_save); + r = memcmp (za_orig, za_save, svl*svl); + if (r != 0) + FAIL_EXIT1 ("saving za failed"); +} + +static void +setcontext_test (void) +{ + volatile int setcontext_done = 0; + unsigned long svcr; + ucontext_t ctx; + void *p; + int r; + struct blk blk = {za_save, svl, {0}}; + + printf ("setcontext test:\n"); + p = get_tpidr2 (); + printf ("initial tp2 = %p\n", p); + if (p != NULL) + FAIL_EXIT1 ("tpidr2 is not initialized to 0"); + svcr = get_svcr (); + if (svcr != 0) + FAIL_EXIT1 ("svcr != 0: %lu", svcr); + set_tpidr2 (&blk); + start_za (); + load_za (za_orig); + + print_data ("za save space", za_save); + p = get_tpidr2 (); + printf ("before getcontext: tp2 = %p\n", p); + if (p != &blk) + FAIL_EXIT1 ("tpidr2 is not set to BLK %p", (void *)&blk); + r = getcontext (&ctx); + if (r != 0) + FAIL_EXIT1 ("getcontext failed"); + if (setcontext_done == 0) + { + p = get_tpidr2 (); + printf ("before setcontext: tp2 = %p\n", p); + if (p != &blk) + FAIL_EXIT1 ("tpidr2 is clobbered"); + setcontext_done = 1; + do_setcontext (&ctx); + FAIL_EXIT1 ("setcontext returned"); + } + p = get_tpidr2 (); + printf ("after setcontext: tp2 = %p\n", p); + if (p != NULL) + FAIL_EXIT1 ("tpidr2 is not set to 0"); + svcr = get_svcr (); + if (svcr != 0) + FAIL_EXIT1 ("svcr != 0: %lu", svcr); + print_data ("za save space", za_save); + r = memcmp (za_orig, za_save, svl*svl); + if (r != 0) + FAIL_EXIT1 ("saving za failed"); +} + +static int +do_test (void) +{ + unsigned long hwcap2; + + hwcap2 = getauxval (AT_HWCAP2); + if ((hwcap2 & HWCAP2_SME) == 0) + return 77; + + svl = get_svl (); + printf ("svl: %lu\n", svl); + if (svl < 16 || svl % 16 != 0 || svl >= (1 << 16)) + FAIL_EXIT1 ("invalid svl"); + + za_orig = malloc (svl*svl); + za_save = malloc (svl*svl); + za_dump = malloc (svl*svl); + memset (za_orig, 1, svl*svl); + memset (za_save, 2, svl*svl); + memset (za_dump, 3, svl*svl); + for (int i = 0; i < svl; i++) + for (int j = 0; j < svl; j++) + za_orig[i*svl+j] = i*svl+j; + print_data ("original data", za_orig); + + longjmp_test (); + + memset (za_save, 2, svl*svl); + memset (za_dump, 3, svl*svl); + + setcontext_test (); + + free (za_orig); + free (za_save); + free (za_dump); + return 0; +} + +#include <support/test-driver.c>