Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/arm64/abi/tpidr2.c
26292 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
3
#include <linux/sched.h>
4
#include <linux/wait.h>
5
6
#include "kselftest.h"
7
8
#define SYS_TPIDR2 "S3_3_C13_C0_5"
9
10
#define EXPECTED_TESTS 5
11
12
static void set_tpidr2(uint64_t val)
13
{
14
asm volatile (
15
"msr " SYS_TPIDR2 ", %0\n"
16
:
17
: "r"(val)
18
: "cc");
19
}
20
21
static uint64_t get_tpidr2(void)
22
{
23
uint64_t val;
24
25
asm volatile (
26
"mrs %0, " SYS_TPIDR2 "\n"
27
: "=r"(val)
28
:
29
: "cc");
30
31
return val;
32
}
33
34
/* Processes should start with TPIDR2 == 0 */
35
static int default_value(void)
36
{
37
return get_tpidr2() == 0;
38
}
39
40
/* If we set TPIDR2 we should read that value */
41
static int write_read(void)
42
{
43
set_tpidr2(getpid());
44
45
return getpid() == get_tpidr2();
46
}
47
48
/* If we set a value we should read the same value after scheduling out */
49
static int write_sleep_read(void)
50
{
51
set_tpidr2(getpid());
52
53
msleep(100);
54
55
return getpid() == get_tpidr2();
56
}
57
58
/*
59
* If we fork the value in the parent should be unchanged and the
60
* child should start with the same value and be able to set its own
61
* value.
62
*/
63
static int write_fork_read(void)
64
{
65
pid_t newpid, waiting, oldpid;
66
int status;
67
68
set_tpidr2(getpid());
69
70
oldpid = getpid();
71
newpid = fork();
72
if (newpid == 0) {
73
/* In child */
74
if (get_tpidr2() != oldpid) {
75
ksft_print_msg("TPIDR2 changed in child: %llx\n",
76
get_tpidr2());
77
exit(0);
78
}
79
80
set_tpidr2(getpid());
81
if (get_tpidr2() == getpid()) {
82
exit(1);
83
} else {
84
ksft_print_msg("Failed to set TPIDR2 in child\n");
85
exit(0);
86
}
87
}
88
if (newpid < 0) {
89
ksft_print_msg("fork() failed: %d\n", newpid);
90
return 0;
91
}
92
93
for (;;) {
94
waiting = waitpid(newpid, &status, 0);
95
96
if (waiting < 0) {
97
if (errno == EINTR)
98
continue;
99
ksft_print_msg("waitpid() failed: %d\n", errno);
100
return 0;
101
}
102
if (waiting != newpid) {
103
ksft_print_msg("waitpid() returned wrong PID: %d != %d\n",
104
waiting, newpid);
105
return 0;
106
}
107
108
if (!WIFEXITED(status)) {
109
ksft_print_msg("child did not exit\n");
110
return 0;
111
}
112
113
if (getpid() != get_tpidr2()) {
114
ksft_print_msg("TPIDR2 corrupted in parent\n");
115
return 0;
116
}
117
118
return WEXITSTATUS(status);
119
}
120
}
121
122
/*
123
* sys_clone() has a lot of per architecture variation so just define
124
* it here rather than adding it to nolibc, plus the raw API is a
125
* little more convenient for this test.
126
*/
127
static int sys_clone(unsigned long clone_flags, unsigned long newsp,
128
int *parent_tidptr, unsigned long tls,
129
int *child_tidptr)
130
{
131
return my_syscall5(__NR_clone, clone_flags, newsp, parent_tidptr, tls,
132
child_tidptr);
133
}
134
135
#define __STACK_SIZE (8 * 1024 * 1024)
136
137
/*
138
* If we clone with CLONE_VM then the value in the parent should
139
* be unchanged and the child should start with zero and be able to
140
* set its own value.
141
*/
142
static int write_clone_read(void)
143
{
144
int parent_tid, child_tid;
145
pid_t parent, waiting;
146
int ret, status;
147
void *stack;
148
149
parent = getpid();
150
set_tpidr2(parent);
151
152
stack = malloc(__STACK_SIZE);
153
if (!stack) {
154
ksft_print_msg("malloc() failed\n");
155
return 0;
156
}
157
158
ret = sys_clone(CLONE_VM, (unsigned long)stack + __STACK_SIZE,
159
&parent_tid, 0, &child_tid);
160
if (ret == -1) {
161
ksft_print_msg("clone() failed: %d\n", errno);
162
return 0;
163
}
164
165
if (ret == 0) {
166
/* In child */
167
if (get_tpidr2() != 0) {
168
ksft_print_msg("TPIDR2 non-zero in child: %llx\n",
169
get_tpidr2());
170
exit(0);
171
}
172
173
if (gettid() == 0)
174
ksft_print_msg("Child TID==0\n");
175
set_tpidr2(gettid());
176
if (get_tpidr2() == gettid()) {
177
exit(1);
178
} else {
179
ksft_print_msg("Failed to set TPIDR2 in child\n");
180
exit(0);
181
}
182
}
183
184
for (;;) {
185
waiting = wait4(ret, &status, __WCLONE, NULL);
186
187
if (waiting < 0) {
188
if (errno == EINTR)
189
continue;
190
ksft_print_msg("wait4() failed: %d\n", errno);
191
return 0;
192
}
193
if (waiting != ret) {
194
ksft_print_msg("wait4() returned wrong PID %d\n",
195
waiting);
196
return 0;
197
}
198
199
if (!WIFEXITED(status)) {
200
ksft_print_msg("child did not exit\n");
201
return 0;
202
}
203
204
if (parent != get_tpidr2()) {
205
ksft_print_msg("TPIDR2 corrupted in parent\n");
206
return 0;
207
}
208
209
return WEXITSTATUS(status);
210
}
211
}
212
213
int main(int argc, char **argv)
214
{
215
int ret;
216
217
ksft_print_header();
218
ksft_set_plan(5);
219
220
ksft_print_msg("PID: %d\n", getpid());
221
222
/*
223
* This test is run with nolibc which doesn't support hwcap and
224
* it's probably disproportionate to implement so instead check
225
* for the default vector length configuration in /proc.
226
*/
227
ret = open("/proc/sys/abi/sme_default_vector_length", O_RDONLY, 0);
228
if (ret >= 0) {
229
ksft_test_result(default_value(), "default_value\n");
230
ksft_test_result(write_read, "write_read\n");
231
ksft_test_result(write_sleep_read, "write_sleep_read\n");
232
ksft_test_result(write_fork_read, "write_fork_read\n");
233
ksft_test_result(write_clone_read, "write_clone_read\n");
234
235
} else {
236
ksft_print_msg("SME support not present\n");
237
238
ksft_test_result_skip("default_value\n");
239
ksft_test_result_skip("write_read\n");
240
ksft_test_result_skip("write_sleep_read\n");
241
ksft_test_result_skip("write_fork_read\n");
242
ksft_test_result_skip("write_clone_read\n");
243
}
244
245
ksft_finished();
246
}
247
248