diff --git a/tools/testing/selftests/riscv/sigreturn/sigreturn.c b/tools/testing/selftests/riscv/sigreturn/sigreturn.c index e10873d95fedb0..647249dbd7e6f3 100644 --- a/tools/testing/selftests/riscv/sigreturn/sigreturn.c +++ b/tools/testing/selftests/riscv/sigreturn/sigreturn.c @@ -7,6 +7,7 @@ #include "kselftest_harness.h" #define RISCV_V_MAGIC 0x53465457 +#define END_MAGIC 0 #define DEFAULT_VALUE 2 #define SIGNAL_HANDLER_OVERRIDE 3 @@ -61,6 +62,76 @@ static int vector_sigreturn(int data, void (*handler)(int, siginfo_t *, void *)) return after_sigreturn; } +#define V_TEST_PATTERN_SIGNAL 0x98 +int nulled_val; +static void sigalrm_handler(int sig, siginfo_t *info, void *vcontext) +{ + ucontext_t *context = vcontext; + struct __riscv_extra_ext_header *ext; + struct __riscv_ctx_hdr *hdr; + uint8_t *ext_ptr; + + /* Find the vector context */ + ext = (void *)(&context->uc_mcontext.__fpregs); + ext_ptr = (uint8_t *)ext; + hdr = &ext->hdr; + + while (hdr->magic != END_MAGIC) { + if (hdr->magic == RISCV_V_MAGIC) { + struct __riscv_v_ext_state *v_state = (struct __riscv_v_ext_state *)(hdr + 1); + /* Assume a valid datap */ + nulled_val = *(int *)v_state->datap; + /* Fill all vector registers with magic pattern */ + memset(v_state->datap, V_TEST_PATTERN_SIGNAL, v_state->vlenb * 32); + /* + * We must also set the vector configuration so that when + * userspace reads v0, it uses a valid element width (e8). + */ + v_state->vl = v_state->vlenb; + v_state->vtype = 0; /* e8, m1, tu, mu */ + break; + } + /* Move to the next extension header */ + ext_ptr += hdr->size; + hdr = (struct __riscv_ctx_hdr *)ext_ptr; + } +} + +TEST(test_signal_syscall_ucontext) { + struct sigaction sa; + + /* Make sure we get V in ucontext by executing vsetvli */ + asm volatile ( + ".option push\n\t" + ".option arch, +v\n\t" + "vsetivli x0, 1, e32, m1, ta, ma\n\t" + ".option pop\n\t" : : :); + + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = sigalrm_handler; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGALRM, &sa, NULL) == -1) + ksft_exit_fail_msg("Failed to register signal handler\n"); + + raise(SIGALRM); + /* + * If the kernel successfully parsed and restored our modified ucontext, + * v0 will contain V_TEST_PATTERN_SIGNAL. + */ + unsigned char v0_val; + + asm volatile( + ".option push\n\t" + ".option arch, +zve32x\n\t" + "vmv.x.s %0, v0\n\t" + ".option pop\n\t" + : "=r" (v0_val) + ); + + EXPECT_EQ(v0_val, V_TEST_PATTERN_SIGNAL); + EXPECT_EQ(nulled_val, -1); +} + TEST(vector_restore) { int result; diff --git a/tools/testing/selftests/riscv/vector/vstate_ptrace.c b/tools/testing/selftests/riscv/vector/vstate_ptrace.c index 1479abc0c9cba4..863a16f6e1a3c9 100644 --- a/tools/testing/selftests/riscv/vector/vstate_ptrace.c +++ b/tools/testing/selftests/riscv/vector/vstate_ptrace.c @@ -21,6 +21,41 @@ static long do_ptrace(enum __ptrace_request op, pid_t pid, long type, size_t siz return ptrace(op, pid, type, &v_iovec); } +static int do_child_syscall_stop(void) +{ + int out; + + if (ptrace(PTRACE_TRACEME, -1, NULL, NULL)) { + ksft_perror("PTRACE_TRACEME failed\n"); + return EXIT_FAILURE; + } + + raise(SIGSTOP); + + asm volatile (".option push\n\t" + ".option arch, +v\n\t" + "vsetivli x0, 1, e32, m1, ta, ma\n\t" + "vmv.s.x v31, %[in]\n\t" + ".option pop\n\t" + : + : [in] "r" (child_set_val)); + + getpid(); + + asm volatile (".option push\n\t" + ".option arch, +v\n\t" + "vsetivli x0, 1, e32, m1, ta, ma\n\t" + "vmv.x.s %[out], v31\n\t" + ".option pop\n\t" + : [out] "=r" (out) + :); + + if (out != -1) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} + static int do_child(void) { int out; @@ -59,7 +94,7 @@ static void do_parent(pid_t child) goto out; } else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP)) { size_t size; - void *data, *v31; + void *vctx, *v31; struct __riscv_v_regset_state *v_regset_hdr; struct user_regs_struct *gpreg; @@ -73,9 +108,11 @@ static void do_parent(pid_t child) goto out; ksft_print_msg("vlenb %ld\n", v_regset_hdr->vlenb); - data = realloc(data, size + v_regset_hdr->vlenb * 32); - if (!data) + vctx = realloc(data, size + v_regset_hdr->vlenb * 32); + if (!vctx) goto out; + data = vctx; + v_regset_hdr = (struct __riscv_v_regset_state *)data; v31 = (void *)(data + size + v_regset_hdr->vlenb * 31); size += v_regset_hdr->vlenb * 32; @@ -109,11 +146,65 @@ static void do_parent(pid_t child) free(data); } +static void do_parent_syscall_stop(pid_t child) +{ + int status; + void *data = NULL; + + while (waitpid(child, &status, 0)) { + if (WIFEXITED(status)) { + ksft_test_result(WEXITSTATUS(status) == 0, + "SETREGSET vector at syscall stop\n"); + goto out; + } else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGSTOP)) { + /* Attach to the child at syscall stop */ + ptrace(PTRACE_SYSCALL, child, NULL, NULL); + continue; + } else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP)) { + size_t size; + void *vctx, *v31; + struct __riscv_v_regset_state *v_regset_hdr; + + size = sizeof(*v_regset_hdr); + data = malloc(size); + if (!data) + goto out; + v_regset_hdr = (struct __riscv_v_regset_state *)data; + + if (do_ptrace(PTRACE_GETREGSET, child, NT_RISCV_VECTOR, size, data)) + goto out; + + ksft_print_msg("vlenb %ld\n", v_regset_hdr->vlenb); + vctx = realloc(data, size + v_regset_hdr->vlenb * 32); + if (!vctx) + goto out; + data = vctx; + + v_regset_hdr = (struct __riscv_v_regset_state *)data; + v31 = (void *)(data + size + v_regset_hdr->vlenb * 31); + size += v_regset_hdr->vlenb * 32; + + if (do_ptrace(PTRACE_GETREGSET, child, NT_RISCV_VECTOR, size, data)) + goto out; + + ksft_test_result(*(int *)v31 == -1, "GETREGSET vector at syscall stop\n"); + + *(int *)v31 = parent_set_val; + if (do_ptrace(PTRACE_SETREGSET, child, NT_RISCV_VECTOR, size, data)) + goto out; + } + ptrace(PTRACE_CONT, child, NULL, NULL); + } + +out: + free(data); +} + int main(void) { pid_t child; - ksft_set_plan(2); + ksft_set_plan(4); if (!is_vector_supported() && !is_xtheadvector_supported()) ksft_exit_skip("Vector not supported\n"); @@ -130,5 +221,17 @@ int main(void) do_parent(child); + parent_set_val = 0x53355457; + child_set_val = 0x49504F21; + + child = fork(); + if (child < 0) + ksft_exit_fail_msg("Fork failed %d\n", child); + + if (!child) + return do_child_syscall_stop(); + + do_parent_syscall_stop(child); + ksft_finished(); }