summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/riscv/vector/vstate_ptrace.c
blob: 1479abc0c9cba401cef7c58c7f6d384fa4ea0a59 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// 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"
		".option	norvc\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;
			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;
			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 == 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 += 4;
			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();
}