Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/arm64/abi/tpidr2.c
51433 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 syscall(__NR_clone, clone_flags, newsp, parent_tidptr, tls, child_tidptr);
132
}
133
134
#define __STACK_SIZE (8 * 1024 * 1024)
135
136
/*
137
* If we clone with CLONE_VM then the value in the parent should
138
* be unchanged and the child should start with zero and be able to
139
* set its own value.
140
*/
141
static int write_clone_read(void)
142
{
143
int parent_tid, child_tid;
144
pid_t parent, waiting;
145
int ret, status;
146
void *stack;
147
148
parent = getpid();
149
set_tpidr2(parent);
150
151
stack = malloc(__STACK_SIZE);
152
if (!stack) {
153
ksft_print_msg("malloc() failed\n");
154
return 0;
155
}
156
157
ret = sys_clone(CLONE_VM, (unsigned long)stack + __STACK_SIZE,
158
&parent_tid, 0, &child_tid);
159
if (ret == -1) {
160
ksft_print_msg("clone() failed: %d\n", errno);
161
return 0;
162
}
163
164
if (ret == 0) {
165
/* In child */
166
if (get_tpidr2() != 0) {
167
ksft_print_msg("TPIDR2 non-zero in child: %llx\n",
168
get_tpidr2());
169
exit(0);
170
}
171
172
if (gettid() == 0)
173
ksft_print_msg("Child TID==0\n");
174
set_tpidr2(gettid());
175
if (get_tpidr2() == gettid()) {
176
exit(1);
177
} else {
178
ksft_print_msg("Failed to set TPIDR2 in child\n");
179
exit(0);
180
}
181
}
182
183
for (;;) {
184
waiting = waitpid(ret, &status, __WCLONE);
185
186
if (waiting < 0) {
187
if (errno == EINTR)
188
continue;
189
ksft_print_msg("waitpid() failed: %d\n", errno);
190
return 0;
191
}
192
if (waiting != ret) {
193
ksft_print_msg("waitpid() returned wrong PID %d\n",
194
waiting);
195
return 0;
196
}
197
198
if (!WIFEXITED(status)) {
199
ksft_print_msg("child did not exit\n");
200
return 0;
201
}
202
203
if (parent != get_tpidr2()) {
204
ksft_print_msg("TPIDR2 corrupted in parent\n");
205
return 0;
206
}
207
208
return WEXITSTATUS(status);
209
}
210
}
211
212
int main(int argc, char **argv)
213
{
214
int ret;
215
216
ksft_print_header();
217
ksft_set_plan(5);
218
219
ksft_print_msg("PID: %d\n", getpid());
220
221
/*
222
* This test is run with nolibc which doesn't support hwcap and
223
* it's probably disproportionate to implement so instead check
224
* for the default vector length configuration in /proc.
225
*/
226
ret = open("/proc/sys/abi/sme_default_vector_length", O_RDONLY, 0);
227
if (ret >= 0) {
228
ksft_test_result(default_value(), "default_value\n");
229
ksft_test_result(write_read(), "write_read\n");
230
ksft_test_result(write_sleep_read(), "write_sleep_read\n");
231
ksft_test_result(write_fork_read(), "write_fork_read\n");
232
ksft_test_result(write_clone_read(), "write_clone_read\n");
233
234
} else {
235
ksft_print_msg("SME support not present\n");
236
237
ksft_test_result_skip("default_value\n");
238
ksft_test_result_skip("write_read\n");
239
ksft_test_result_skip("write_sleep_read\n");
240
ksft_test_result_skip("write_fork_read\n");
241
ksft_test_result_skip("write_clone_read\n");
242
}
243
244
ksft_finished();
245
}
246
247