On Wed, 03 Apr 2024 16:50:29 PDT (-0700), Charlie Jenkins wrote:
Add two tests to check vector save/restore when a signal is received during a vector routine. One test ensures that a value is not clobbered during signal handling. The other verifies that vector registers modified in the signal handler are properly reflected when the signal handling is complete.
Signed-off-by: Charlie Jenkins charlie@rivosinc.com
These tests came about to highlight the bug fixed in https://lore.kernel.org/lkml/20240403072638.567446-1-bjorn@kernel.org/ and will only pass with that fix applied.
Thaks. I'm just staging this, I have a testcase too that I keep forgetting to post because I haven't gotten in into the selftests sanely yet...
#include "../../kselftest.h" #include <signal.h> #include <stdlib.h> #include <asm/ptrace.h> #include <stdint.h>
#define ITERATIONS (1 << 24) #define RISCV_V_MAGIC 0x53465457
struct __riscv_ctx_hdr { __u32 magic; __u32 size; };
struct __riscv_extra_ext_header { __u32 __padding[129] __attribute__((aligned(16))); /* * Reserved for expansion of sigcontext structure. Currently zeroed * upon signal, and must be zero upon sigreturn. */ __u32 reserved; struct __riscv_ctx_hdr hdr; };
struct __riscv_v_ext_state { unsigned long vstart; unsigned long vl; unsigned long vtype; unsigned long vcsr; unsigned long vlenb; void *datap; /* * In signal handler, datap will be set a correct user stack offset * and vector registers will be copied to the address of datap * pointer. */ };
/* The beginning of the actual state. */ int count;
void handler(int, siginfo_t *si, void *ucontext_void) { ucontext_t *ucontext = ucontext_void;
/* Skip over the SEGV by just jumping to a0. */ ucontext->uc_mcontext.__gregs[REG_PC] = ucontext->uc_mcontext.__gregs[REG_A0];
/* Increment the first element of v0. */ { struct __riscv_extra_ext_header *ext; struct __riscv_v_ext_state *v_ext_state; uint32_t val;
/* Find the vector context. */ ext = (void *)(&ucontext->uc_mcontext.__fpregs); if (ext->hdr.magic != RISCV_V_MAGIC) { fprintf(stderr, "bad vector magic: %x\n", ext->hdr.magic); abort(); }
v_ext_state = (void *)((char *)(ext) + sizeof(*ext));
/* Actually do the increment. */ memcpy(&val, v_ext_state->datap, sizeof(val)); val++; memcpy(v_ext_state->datap, &val, sizeof(val)); }
/* Count the delivered signal, to make sure we don't miss any. */ count++; }
void sigill(int, siginfo_t *si, void *ucontext_void) { fprintf(stderr, "SIGILL after %d iterations\n", count); abort(); }
void check_signal(void) { int check = rand(); int out;
__asm__ volatile ( "vsetvli a0, %2, e32, ta, ma\n\t" "vmv.s.x v0, %1\n\t" "la a0, 1f\n\t" "lw t0, 0(x0)\n\t" "1:\n\t" "vmv.x.s %0, v0" : "=&r"(out) : "r"(check-1), "r"(1) : "a0" #ifdef __riscv_vector , "v0" #endif );
if (out != check) { fprintf(stderr, "out: %d\ncheck: %d\n", out, check); abort(); } }
int main(void) { struct sigaction sa_segv, sa_ill;
memset(&sa_segv, 0, sizeof(sa_segv)); sa_segv.sa_sigaction = &handler; sa_segv.sa_flags = SA_SIGINFO; sigaction(SIGSEGV, &sa_segv, NULL);
memset(&sa_ill, 0, sizeof(sa_ill)); sa_ill.sa_sigaction = &sigill; sa_ill.sa_flags = SA_SIGINFO; sigaction(SIGILL, &sa_ill, NULL);
count = 0; for (int i = 0; i < ITERATIONS; ++i) check_signal();
if (count != ITERATIONS) { fprintf(stderr, "count: %d\n", count); abort(); }
return 0; }
tools/testing/selftests/riscv/Makefile | 2 +- tools/testing/selftests/riscv/sigreturn/.gitignore | 1 + tools/testing/selftests/riscv/sigreturn/Makefile | 12 ++++ .../testing/selftests/riscv/sigreturn/sigreturn.c | 82 ++++++++++++++++++++++ 4 files changed, 96 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/riscv/Makefile b/tools/testing/selftests/riscv/Makefile index 4a9ff515a3a0..7ce03d832b64 100644 --- a/tools/testing/selftests/riscv/Makefile +++ b/tools/testing/selftests/riscv/Makefile @@ -5,7 +5,7 @@ ARCH ?= $(shell uname -m 2>/dev/null || echo not)
ifneq (,$(filter $(ARCH),riscv)) -RISCV_SUBTARGETS ?= hwprobe vector mm +RISCV_SUBTARGETS ?= hwprobe vector mm sigreturn else RISCV_SUBTARGETS := endif diff --git a/tools/testing/selftests/riscv/sigreturn/.gitignore b/tools/testing/selftests/riscv/sigreturn/.gitignore new file mode 100644 index 000000000000..35002b8ae780 --- /dev/null +++ b/tools/testing/selftests/riscv/sigreturn/.gitignore @@ -0,0 +1 @@ +sigreturn diff --git a/tools/testing/selftests/riscv/sigreturn/Makefile b/tools/testing/selftests/riscv/sigreturn/Makefile new file mode 100644 index 000000000000..eb8bac9279a8 --- /dev/null +++ b/tools/testing/selftests/riscv/sigreturn/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2021 ARM Limited +# Originally tools/testing/arm64/abi/Makefile
+CFLAGS += -I$(top_srcdir)/tools/include
+TEST_GEN_PROGS := sigreturn
+include ../../lib.mk
+$(OUTPUT)/sigreturn: sigreturn.c
- $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
diff --git a/tools/testing/selftests/riscv/sigreturn/sigreturn.c b/tools/testing/selftests/riscv/sigreturn/sigreturn.c new file mode 100644 index 000000000000..62397d5934f1 --- /dev/null +++ b/tools/testing/selftests/riscv/sigreturn/sigreturn.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <ucontext.h> +#include <linux/ptrace.h> +#include "../../kselftest_harness.h"
+#define RISCV_V_MAGIC 0x53465457 +#define DEFAULT_VALUE 2 +#define SIGNAL_HANDLER_OVERRIDE 3
+static void simple_handle(int sig_no, siginfo_t *info, void *vcontext) +{
- ucontext_t *context = vcontext;
- context->uc_mcontext.__gregs[REG_PC] = context->uc_mcontext.__gregs[REG_PC] + 4;
+}
+static void vector_override(int sig_no, siginfo_t *info, void *vcontext) +{
- ucontext_t *context = vcontext;
- // vector state
- struct __riscv_extra_ext_header *ext;
- struct __riscv_v_ext_state *v_ext_state;
- /* Find the vector context. */
- ext = (void *)(&context->uc_mcontext.__fpregs);
- if (ext->hdr.magic != RISCV_V_MAGIC) {
fprintf(stderr, "bad vector magic: %x\n", ext->hdr.magic);
abort();
- }
- v_ext_state = (void *)((char *)(ext) + sizeof(*ext));
- *(int *)v_ext_state->datap = SIGNAL_HANDLER_OVERRIDE;
- context->uc_mcontext.__gregs[REG_PC] = context->uc_mcontext.__gregs[REG_PC] + 4;
+}
+static int vector_sigreturn(int data, void (*handler)(int, siginfo_t *, void *)) +{
- int after_sigreturn;
- struct sigaction sig_action = {
.sa_sigaction = handler,
.sa_flags = SA_SIGINFO
- };
- sigaction(SIGSEGV, &sig_action, 0);
- asm(".option push \n\
.option arch, +v \n\
vsetivli x0, 1, e32, ta, ma \n\
vmv.s.x v0, %1 \n\
# Generate SIGSEGV \n\
lw a0, 0(x0) \n\
vmv.x.s %0, v0 \n\
.option pop" : "=r" (after_sigreturn) : "r" (data));
- return after_sigreturn;
+}
+TEST(vector_restore) +{
- int result;
- result = vector_sigreturn(DEFAULT_VALUE, &simple_handle);
- EXPECT_EQ(DEFAULT_VALUE, result);
+}
+TEST(vector_restore_signal_handler_override) +{
- int result;
- result = vector_sigreturn(DEFAULT_VALUE, &vector_override);
- EXPECT_EQ(SIGNAL_HANDLER_OVERRIDE, result);
+}
+TEST_HARNESS_MAIN
base-commit: 4cece764965020c22cff7665b18a012006359095 change-id: 20240403-vector_sigreturn_tests-8118f0ac54fa