xref: /linux/tools/testing/selftests/riscv/vector/vstate_ptrace.c (revision b61104e7a6349bd2c2b3e2fb3260d87f15eda8f4)
1 // SPDX-License-Identifier: GPL-2.0-only
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <asm/ptrace.h>
5 #include <linux/elf.h>
6 #include <sys/ptrace.h>
7 #include <sys/uio.h>
8 #include <sys/wait.h>
9 #include "../../kselftest.h"
10 #include "v_helpers.h"
11 
12 int parent_set_val, child_set_val;
13 
14 static long do_ptrace(enum __ptrace_request op, pid_t pid, long type, size_t size, void *data)
15 {
16 	struct iovec v_iovec = {
17 		.iov_len = size,
18 		.iov_base = data
19 	};
20 
21 	return ptrace(op, pid, type, &v_iovec);
22 }
23 
24 static int do_child(void)
25 {
26 	int out;
27 
28 	if (ptrace(PTRACE_TRACEME, -1, NULL, NULL)) {
29 		ksft_perror("PTRACE_TRACEME failed\n");
30 		return EXIT_FAILURE;
31 	}
32 
33 	asm volatile (".option push\n\t"
34 		".option	arch, +v\n\t"
35 		".option	norvc\n\t"
36 		"vsetivli	x0, 1, e32, m1, ta, ma\n\t"
37 		"vmv.s.x	v31, %[in]\n\t"
38 		"ebreak\n\t"
39 		"vmv.x.s	%[out], v31\n\t"
40 		".option pop\n\t"
41 		: [out] "=r" (out)
42 		: [in] "r" (child_set_val));
43 
44 	if (out != parent_set_val)
45 		return EXIT_FAILURE;
46 
47 	return EXIT_SUCCESS;
48 }
49 
50 static void do_parent(pid_t child)
51 {
52 	int status;
53 	void *data = NULL;
54 
55 	/* Attach to the child */
56 	while (waitpid(child, &status, 0)) {
57 		if (WIFEXITED(status)) {
58 			ksft_test_result(WEXITSTATUS(status) == 0, "SETREGSET vector\n");
59 			goto out;
60 		} else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP)) {
61 			size_t size;
62 			void *data, *v31;
63 			struct __riscv_v_regset_state *v_regset_hdr;
64 			struct user_regs_struct *gpreg;
65 
66 			size = sizeof(*v_regset_hdr);
67 			data = malloc(size);
68 			if (!data)
69 				goto out;
70 			v_regset_hdr = (struct __riscv_v_regset_state *)data;
71 
72 			if (do_ptrace(PTRACE_GETREGSET, child, NT_RISCV_VECTOR, size, data))
73 				goto out;
74 
75 			ksft_print_msg("vlenb %ld\n", v_regset_hdr->vlenb);
76 			data = realloc(data, size + v_regset_hdr->vlenb * 32);
77 			if (!data)
78 				goto out;
79 			v_regset_hdr = (struct __riscv_v_regset_state *)data;
80 			v31 = (void *)(data + size + v_regset_hdr->vlenb * 31);
81 			size += v_regset_hdr->vlenb * 32;
82 
83 			if (do_ptrace(PTRACE_GETREGSET, child, NT_RISCV_VECTOR, size, data))
84 				goto out;
85 
86 			ksft_test_result(*(int *)v31 == child_set_val, "GETREGSET vector\n");
87 
88 			*(int *)v31 = parent_set_val;
89 			if (do_ptrace(PTRACE_SETREGSET, child, NT_RISCV_VECTOR, size, data))
90 				goto out;
91 
92 			/* move the pc forward */
93 			size = sizeof(*gpreg);
94 			data = realloc(data, size);
95 			gpreg = (struct user_regs_struct *)data;
96 
97 			if (do_ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, size, data))
98 				goto out;
99 
100 			gpreg->pc += 4;
101 			if (do_ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, size, data))
102 				goto out;
103 		}
104 
105 		ptrace(PTRACE_CONT, child, NULL, NULL);
106 	}
107 
108 out:
109 	free(data);
110 }
111 
112 int main(void)
113 {
114 	pid_t child;
115 
116 	ksft_set_plan(2);
117 	if (!is_vector_supported() && !is_xtheadvector_supported())
118 		ksft_exit_skip("Vector not supported\n");
119 
120 	srandom(getpid());
121 	parent_set_val = rand();
122 	child_set_val = rand();
123 
124 	child = fork();
125 	if (child < 0)
126 		ksft_exit_fail_msg("Fork failed %d\n", child);
127 
128 	if (!child)
129 		return do_child();
130 
131 	do_parent(child);
132 
133 	ksft_finished();
134 }
135