Add a test case that does some basic verification of the Vector ptrace
interface. This forks a child process then using ptrace to inspect and
manipulate the v31 register of the child.
Signed-off-by: Yong-Xuan Wang <yongxuan.wang@sifive.com>
---
tools/testing/selftests/riscv/vector/Makefile | 5 +-
.../selftests/riscv/vector/vstate_ptrace.c | 132 ++++++++++++++++++
2 files changed, 136 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/riscv/vector/vstate_ptrace.c
diff --git a/tools/testing/selftests/riscv/vector/Makefile b/tools/testing/selftests/riscv/vector/Makefile
index 6f7497f4e7b3..45f25e9dd264 100644
--- a/tools/testing/selftests/riscv/vector/Makefile
+++ b/tools/testing/selftests/riscv/vector/Makefile
@@ -2,7 +2,7 @@
# Copyright (C) 2021 ARM Limited
# Originally tools/testing/arm64/abi/Makefile
-TEST_GEN_PROGS := v_initval vstate_prctl
+TEST_GEN_PROGS := v_initval vstate_prctl vsate_ptrace
TEST_GEN_PROGS_EXTENDED := vstate_exec_nolibc v_exec_initval_nolibc
include ../../lib.mk
@@ -26,3 +26,6 @@ $(OUTPUT)/v_initval: v_initval.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o
$(OUTPUT)/v_exec_initval_nolibc: v_exec_initval_nolibc.c
$(CC) -nostdlib -static -include ../../../../include/nolibc/nolibc.h \
-Wall $(CFLAGS) $(LDFLAGS) $^ -o $@ -lgcc
+
+$(OUTPUT)/vstate_ptrace: vstate_ptrace.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o
+ $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
diff --git a/tools/testing/selftests/riscv/vector/vstate_ptrace.c b/tools/testing/selftests/riscv/vector/vstate_ptrace.c
new file mode 100644
index 000000000000..8a7bcf318e59
--- /dev/null
+++ b/tools/testing/selftests/riscv/vector/vstate_ptrace.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stdio.h>
+#include <stdlib.h>
+#include <asm/ptrace.h>
+#include <linux/elf.h>
+#include <sys/ptrace.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include "../../kselftest.h"
+#include "v_helpers.h"
+
+int parent_set_val, child_set_val;
+
+static long do_ptrace(enum __ptrace_request op, pid_t pid, long type, size_t size, void *data)
+{
+ struct iovec v_iovec = {
+ .iov_len = size,
+ .iov_base = data
+ };
+
+ return ptrace(op, pid, type, &v_iovec);
+}
+
+static int do_child(void)
+{
+ int out;
+
+ if (ptrace(PTRACE_TRACEME, -1, NULL, NULL)) {
+ ksft_perror("PTRACE_TRACEME failed\n");
+ return EXIT_FAILURE;
+ }
+
+ 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"
+ "ebreak\n\t"
+ "vmv.x.s %[out], v31\n\t"
+ ".option pop\n\t"
+ : [out] "=r" (out)
+ : [in] "r" (child_set_val));
+
+ if (out != parent_set_val)
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
+
+static void do_parent(pid_t child)
+{
+ int status;
+ void *data = NULL;
+
+ /* Attach to the child */
+ while (waitpid(child, &status, 0)) {
+ if (WIFEXITED(status)) {
+ ksft_test_result(WEXITSTATUS(status) == 0, "SETREGSET vector\n");
+ goto out;
+ } else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP)) {
+ size_t size, t;
+ void *data, *v31;
+ struct __riscv_v_regset_state *v_regset_hdr;
+ struct user_regs_struct *gpreg;
+
+ 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);
+ data = realloc(data, size + v_regset_hdr->vlenb * 32);
+ if (!data)
+ goto out;
+ 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 == child_set_val, "GETREGSET vector\n");
+
+ *(int *)v31 = parent_set_val;
+ if (do_ptrace(PTRACE_SETREGSET, child, NT_RISCV_VECTOR, size, data))
+ goto out;
+
+ /* move the pc forward */
+ size = sizeof(*gpreg);
+ data = realloc(data, size);
+ gpreg = (struct user_regs_struct *)data;
+
+ if (do_ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, size, data))
+ goto out;
+
+ gpreg->pc += 2;
+ if (do_ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, size, data))
+ goto out;
+ }
+
+ ptrace(PTRACE_CONT, child, NULL, NULL);
+ }
+
+out:
+ free(data);
+}
+
+int main(void)
+{
+ pid_t child;
+
+ ksft_set_plan(2);
+ if (!is_vector_supported() && !is_xtheadvector_supported())
+ ksft_exit_skip("Vector not supported\n");
+
+ srandom(getpid());
+ parent_set_val = rand();
+ child_set_val = rand();
+
+ child = fork();
+ if (child < 0)
+ ksft_exit_fail_msg("Fork failed %d\n", child);
+
+ if (!child)
+ return do_child();
+
+ do_parent(child);
+
+ ksft_finished();
+}
--
2.43.0
Hi Yong-Xuan,
I found some issues which deserve a re-roll:
On Wed, Oct 1, 2025 at 6:15 AM Yong-Xuan Wang <yongxuan.wang@sifive.com> wrote:
>
> Add a test case that does some basic verification of the Vector ptrace
> interface. This forks a child process then using ptrace to inspect and
> manipulate the v31 register of the child.
>
> Signed-off-by: Yong-Xuan Wang <yongxuan.wang@sifive.com>
> ---
> tools/testing/selftests/riscv/vector/Makefile | 5 +-
> .../selftests/riscv/vector/vstate_ptrace.c | 132 ++++++++++++++++++
> 2 files changed, 136 insertions(+), 1 deletion(-)
> create mode 100644 tools/testing/selftests/riscv/vector/vstate_ptrace.c
>
> diff --git a/tools/testing/selftests/riscv/vector/Makefile b/tools/testing/selftests/riscv/vector/Makefile
> index 6f7497f4e7b3..45f25e9dd264 100644
> --- a/tools/testing/selftests/riscv/vector/Makefile
> +++ b/tools/testing/selftests/riscv/vector/Makefile
> @@ -2,7 +2,7 @@
> # Copyright (C) 2021 ARM Limited
> # Originally tools/testing/arm64/abi/Makefile
>
> -TEST_GEN_PROGS := v_initval vstate_prctl
> +TEST_GEN_PROGS := v_initval vstate_prctl vsate_ptrace
Please s/vsate_ptrace/vstate_ptrace
Otherwise we will not get the program compiled
> TEST_GEN_PROGS_EXTENDED := vstate_exec_nolibc v_exec_initval_nolibc
>
> include ../../lib.mk
> @@ -26,3 +26,6 @@ $(OUTPUT)/v_initval: v_initval.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o
> $(OUTPUT)/v_exec_initval_nolibc: v_exec_initval_nolibc.c
> $(CC) -nostdlib -static -include ../../../../include/nolibc/nolibc.h \
> -Wall $(CFLAGS) $(LDFLAGS) $^ -o $@ -lgcc
> +
> +$(OUTPUT)/vstate_ptrace: vstate_ptrace.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o
> + $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
> diff --git a/tools/testing/selftests/riscv/vector/vstate_ptrace.c b/tools/testing/selftests/riscv/vector/vstate_ptrace.c
> new file mode 100644
> index 000000000000..8a7bcf318e59
> --- /dev/null
> +++ b/tools/testing/selftests/riscv/vector/vstate_ptrace.c
> @@ -0,0 +1,132 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <asm/ptrace.h>
> +#include <linux/elf.h>
> +#include <sys/ptrace.h>
> +#include <sys/uio.h>
> +#include <sys/wait.h>
> +#include "../../kselftest.h"
> +#include "v_helpers.h"
> +
> +int parent_set_val, child_set_val;
> +
> +static long do_ptrace(enum __ptrace_request op, pid_t pid, long type, size_t size, void *data)
> +{
> + struct iovec v_iovec = {
> + .iov_len = size,
> + .iov_base = data
> + };
> +
> + return ptrace(op, pid, type, &v_iovec);
> +}
> +
> +static int do_child(void)
> +{
> + int out;
> +
> + if (ptrace(PTRACE_TRACEME, -1, NULL, NULL)) {
> + ksft_perror("PTRACE_TRACEME failed\n");
> + return EXIT_FAILURE;
> + }
> +
> + asm volatile (".option push\n\t"
> + ".option arch, +v\n\t"
As mentioned before, please use ".option arch, +v,-c\n\t" or ".option
norvc\n\t" and +=4 when advancing the pc
> + "vsetivli x0, 1, e32, m1, ta, ma\n\t"
> + "vmv.s.x v31, %[in]\n\t"
> + "ebreak\n\t"
> + "vmv.x.s %[out], v31\n\t"
> + ".option pop\n\t"
> + : [out] "=r" (out)
> + : [in] "r" (child_set_val));
> +
> + if (out != parent_set_val)
> + return EXIT_FAILURE;
> +
> + return EXIT_SUCCESS;
> +}
> +
> +static void do_parent(pid_t child)
> +{
> + int status;
> + void *data = NULL;
> +
> + /* Attach to the child */
> + while (waitpid(child, &status, 0)) {
> + if (WIFEXITED(status)) {
> + ksft_test_result(WEXITSTATUS(status) == 0, "SETREGSET vector\n");
> + goto out;
> + } else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP)) {
> + size_t size, t;
unused variable t
> + void *data, *v31;
> + struct __riscv_v_regset_state *v_regset_hdr;
> + struct user_regs_struct *gpreg;
> +
> + 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);
> + data = realloc(data, size + v_regset_hdr->vlenb * 32);
realloc may give a new pointer so v_regset_hdr has to be updated here
before the next use
> + if (!data)
> + goto out;
> + 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 == child_set_val, "GETREGSET vector\n");
> +
> + *(int *)v31 = parent_set_val;
> + if (do_ptrace(PTRACE_SETREGSET, child, NT_RISCV_VECTOR, size, data))
> + goto out;
> +
> + /* move the pc forward */
> + size = sizeof(*gpreg);
> + data = realloc(data, size);
> + gpreg = (struct user_regs_struct *)data;
> +
> + if (do_ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, size, data))
> + goto out;
> +
> + gpreg->pc += 2;
> + if (do_ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, size, data))
> + goto out;
> + }
> +
> + ptrace(PTRACE_CONT, child, NULL, NULL);
> + }
> +
> +out:
> + free(data);
> +}
> +
> +int main(void)
> +{
> + pid_t child;
> +
> + ksft_set_plan(2);
> + if (!is_vector_supported() && !is_xtheadvector_supported())
> + ksft_exit_skip("Vector not supported\n");
> +
> + srandom(getpid());
> + parent_set_val = rand();
> + child_set_val = rand();
> +
> + child = fork();
> + if (child < 0)
> + ksft_exit_fail_msg("Fork failed %d\n", child);
> +
> + if (!child)
> + return do_child();
> +
> + do_parent(child);
> +
> + ksft_finished();
> +}
> --
> 2.43.0
>
Thanks,
Andy
On Thu, Oct 02, 2025 at 12:53:13AM -0500, Andy Chiu wrote:
> Hi Yong-Xuan,
>
> I found some issues which deserve a re-roll:
>
> On Wed, Oct 1, 2025 at 6:15 AM Yong-Xuan Wang <yongxuan.wang@sifive.com> wrote:
> >
> > Add a test case that does some basic verification of the Vector ptrace
> > interface. This forks a child process then using ptrace to inspect and
> > manipulate the v31 register of the child.
> >
> > Signed-off-by: Yong-Xuan Wang <yongxuan.wang@sifive.com>
> > ---
> > tools/testing/selftests/riscv/vector/Makefile | 5 +-
> > .../selftests/riscv/vector/vstate_ptrace.c | 132 ++++++++++++++++++
> > 2 files changed, 136 insertions(+), 1 deletion(-)
> > create mode 100644 tools/testing/selftests/riscv/vector/vstate_ptrace.c
> >
> > diff --git a/tools/testing/selftests/riscv/vector/Makefile b/tools/testing/selftests/riscv/vector/Makefile
> > index 6f7497f4e7b3..45f25e9dd264 100644
> > --- a/tools/testing/selftests/riscv/vector/Makefile
> > +++ b/tools/testing/selftests/riscv/vector/Makefile
> > @@ -2,7 +2,7 @@
> > # Copyright (C) 2021 ARM Limited
> > # Originally tools/testing/arm64/abi/Makefile
> >
> > -TEST_GEN_PROGS := v_initval vstate_prctl
> > +TEST_GEN_PROGS := v_initval vstate_prctl vsate_ptrace
>
> Please s/vsate_ptrace/vstate_ptrace
>
> Otherwise we will not get the program compiled
>
> > TEST_GEN_PROGS_EXTENDED := vstate_exec_nolibc v_exec_initval_nolibc
> >
> > include ../../lib.mk
> > @@ -26,3 +26,6 @@ $(OUTPUT)/v_initval: v_initval.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o
> > $(OUTPUT)/v_exec_initval_nolibc: v_exec_initval_nolibc.c
> > $(CC) -nostdlib -static -include ../../../../include/nolibc/nolibc.h \
> > -Wall $(CFLAGS) $(LDFLAGS) $^ -o $@ -lgcc
> > +
> > +$(OUTPUT)/vstate_ptrace: vstate_ptrace.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o
> > + $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
> > diff --git a/tools/testing/selftests/riscv/vector/vstate_ptrace.c b/tools/testing/selftests/riscv/vector/vstate_ptrace.c
> > new file mode 100644
> > index 000000000000..8a7bcf318e59
> > --- /dev/null
> > +++ b/tools/testing/selftests/riscv/vector/vstate_ptrace.c
> > @@ -0,0 +1,132 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <asm/ptrace.h>
> > +#include <linux/elf.h>
> > +#include <sys/ptrace.h>
> > +#include <sys/uio.h>
> > +#include <sys/wait.h>
> > +#include "../../kselftest.h"
> > +#include "v_helpers.h"
> > +
> > +int parent_set_val, child_set_val;
> > +
> > +static long do_ptrace(enum __ptrace_request op, pid_t pid, long type, size_t size, void *data)
> > +{
> > + struct iovec v_iovec = {
> > + .iov_len = size,
> > + .iov_base = data
> > + };
> > +
> > + return ptrace(op, pid, type, &v_iovec);
> > +}
> > +
> > +static int do_child(void)
> > +{
> > + int out;
> > +
> > + if (ptrace(PTRACE_TRACEME, -1, NULL, NULL)) {
> > + ksft_perror("PTRACE_TRACEME failed\n");
> > + return EXIT_FAILURE;
> > + }
> > +
> > + asm volatile (".option push\n\t"
> > + ".option arch, +v\n\t"
>
> As mentioned before, please use ".option arch, +v,-c\n\t" or ".option
> norvc\n\t" and +=4 when advancing the pc
arch -c should be avoided, there are cases when it does not always avoid
using all compressed instructions. norvc should always do the right
thing though. There is discussion at [1] about deprecating it (along with
all variants of -ext).
[1] https://inbox.sourceware.org/binutils/7ecdc846-0822-4666-957f-ff818786fb44@iscas.ac.cn/T/#t
- Charlie
>
> > + "vsetivli x0, 1, e32, m1, ta, ma\n\t"
> > + "vmv.s.x v31, %[in]\n\t"
> > + "ebreak\n\t"
> > + "vmv.x.s %[out], v31\n\t"
> > + ".option pop\n\t"
> > + : [out] "=r" (out)
> > + : [in] "r" (child_set_val));
> > +
> > + if (out != parent_set_val)
> > + return EXIT_FAILURE;
> > +
> > + return EXIT_SUCCESS;
> > +}
> > +
> > +static void do_parent(pid_t child)
> > +{
> > + int status;
> > + void *data = NULL;
> > +
> > + /* Attach to the child */
> > + while (waitpid(child, &status, 0)) {
> > + if (WIFEXITED(status)) {
> > + ksft_test_result(WEXITSTATUS(status) == 0, "SETREGSET vector\n");
> > + goto out;
> > + } else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP)) {
> > + size_t size, t;
>
> unused variable t
>
> > + void *data, *v31;
> > + struct __riscv_v_regset_state *v_regset_hdr;
> > + struct user_regs_struct *gpreg;
> > +
> > + 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);
> > + data = realloc(data, size + v_regset_hdr->vlenb * 32);
>
> realloc may give a new pointer so v_regset_hdr has to be updated here
> before the next use
>
> > + if (!data)
> > + goto out;
> > + 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 == child_set_val, "GETREGSET vector\n");
> > +
> > + *(int *)v31 = parent_set_val;
> > + if (do_ptrace(PTRACE_SETREGSET, child, NT_RISCV_VECTOR, size, data))
> > + goto out;
> > +
> > + /* move the pc forward */
> > + size = sizeof(*gpreg);
> > + data = realloc(data, size);
> > + gpreg = (struct user_regs_struct *)data;
> > +
> > + if (do_ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, size, data))
> > + goto out;
> > +
> > + gpreg->pc += 2;
> > + if (do_ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, size, data))
> > + goto out;
> > + }
> > +
> > + ptrace(PTRACE_CONT, child, NULL, NULL);
> > + }
> > +
> > +out:
> > + free(data);
> > +}
> > +
> > +int main(void)
> > +{
> > + pid_t child;
> > +
> > + ksft_set_plan(2);
> > + if (!is_vector_supported() && !is_xtheadvector_supported())
> > + ksft_exit_skip("Vector not supported\n");
> > +
> > + srandom(getpid());
> > + parent_set_val = rand();
> > + child_set_val = rand();
> > +
> > + child = fork();
> > + if (child < 0)
> > + ksft_exit_fail_msg("Fork failed %d\n", child);
> > +
> > + if (!child)
> > + return do_child();
> > +
> > + do_parent(child);
> > +
> > + ksft_finished();
> > +}
> > --
> > 2.43.0
> >
>
> Thanks,
> Andy
Hi Yong-Xuan,
On Wed, Oct 1, 2025 at 6:15 AM Yong-Xuan Wang <yongxuan.wang@sifive.com> wrote:
>
> Add a test case that does some basic verification of the Vector ptrace
> interface. This forks a child process then using ptrace to inspect and
> manipulate the v31 register of the child.
>
> Signed-off-by: Yong-Xuan Wang <yongxuan.wang@sifive.com>
> ---
> tools/testing/selftests/riscv/vector/Makefile | 5 +-
> .../selftests/riscv/vector/vstate_ptrace.c | 132 ++++++++++++++++++
> 2 files changed, 136 insertions(+), 1 deletion(-)
> create mode 100644 tools/testing/selftests/riscv/vector/vstate_ptrace.c
>
> diff --git a/tools/testing/selftests/riscv/vector/Makefile b/tools/testing/selftests/riscv/vector/Makefile
> index 6f7497f4e7b3..45f25e9dd264 100644
> --- a/tools/testing/selftests/riscv/vector/Makefile
> +++ b/tools/testing/selftests/riscv/vector/Makefile
> @@ -2,7 +2,7 @@
> # Copyright (C) 2021 ARM Limited
> # Originally tools/testing/arm64/abi/Makefile
>
> -TEST_GEN_PROGS := v_initval vstate_prctl
> +TEST_GEN_PROGS := v_initval vstate_prctl vsate_ptrace
> TEST_GEN_PROGS_EXTENDED := vstate_exec_nolibc v_exec_initval_nolibc
>
> include ../../lib.mk
> @@ -26,3 +26,6 @@ $(OUTPUT)/v_initval: v_initval.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o
> $(OUTPUT)/v_exec_initval_nolibc: v_exec_initval_nolibc.c
> $(CC) -nostdlib -static -include ../../../../include/nolibc/nolibc.h \
> -Wall $(CFLAGS) $(LDFLAGS) $^ -o $@ -lgcc
> +
> +$(OUTPUT)/vstate_ptrace: vstate_ptrace.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o
> + $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
> diff --git a/tools/testing/selftests/riscv/vector/vstate_ptrace.c b/tools/testing/selftests/riscv/vector/vstate_ptrace.c
> new file mode 100644
> index 000000000000..8a7bcf318e59
> --- /dev/null
> +++ b/tools/testing/selftests/riscv/vector/vstate_ptrace.c
> @@ -0,0 +1,132 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <asm/ptrace.h>
> +#include <linux/elf.h>
> +#include <sys/ptrace.h>
> +#include <sys/uio.h>
> +#include <sys/wait.h>
> +#include "../../kselftest.h"
> +#include "v_helpers.h"
> +
> +int parent_set_val, child_set_val;
> +
> +static long do_ptrace(enum __ptrace_request op, pid_t pid, long type, size_t size, void *data)
> +{
> + struct iovec v_iovec = {
> + .iov_len = size,
> + .iov_base = data
> + };
> +
> + return ptrace(op, pid, type, &v_iovec);
> +}
> +
> +static int do_child(void)
> +{
> + int out;
> +
> + if (ptrace(PTRACE_TRACEME, -1, NULL, NULL)) {
> + ksft_perror("PTRACE_TRACEME failed\n");
> + return EXIT_FAILURE;
> + }
> +
> + 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"
> + "ebreak\n\t"
> + "vmv.x.s %[out], v31\n\t"
> + ".option pop\n\t"
> + : [out] "=r" (out)
> + : [in] "r" (child_set_val));
> +
> + if (out != parent_set_val)
> + return EXIT_FAILURE;
> +
> + return EXIT_SUCCESS;
> +}
> +
> +static void do_parent(pid_t child)
> +{
> + int status;
> + void *data = NULL;
> +
> + /* Attach to the child */
> + while (waitpid(child, &status, 0)) {
> + if (WIFEXITED(status)) {
> + ksft_test_result(WEXITSTATUS(status) == 0, "SETREGSET vector\n");
> + goto out;
> + } else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP)) {
> + size_t size, t;
> + void *data, *v31;
> + struct __riscv_v_regset_state *v_regset_hdr;
> + struct user_regs_struct *gpreg;
> +
> + 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);
> + data = realloc(data, size + v_regset_hdr->vlenb * 32);
> + if (!data)
> + goto out;
> + 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 == child_set_val, "GETREGSET vector\n");
> +
> + *(int *)v31 = parent_set_val;
> + if (do_ptrace(PTRACE_SETREGSET, child, NT_RISCV_VECTOR, size, data))
> + goto out;
> +
> + /* move the pc forward */
> + size = sizeof(*gpreg);
> + data = realloc(data, size);
> + gpreg = (struct user_regs_struct *)data;
> +
> + if (do_ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, size, data))
> + goto out;
> +
> + gpreg->pc += 2;
Just nitpicking here, simply adding 2 may fail if the program is not
compiled with C. You may either +=4 and use ".option norvc" in the asm
or determine the size of ebreak by decoding it.
with or without the fix,
Reviewed-by: Andy Chiu <andybnac@gmail.com>
© 2016 - 2026 Red Hat, Inc.