Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tests/sys/arch/aarch64/sve.c
289024 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2023,2024 Arm Ltd
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
*
15
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
* SUCH DAMAGE.
26
*/
27
28
#include <sys/types.h>
29
#include <sys/auxv.h>
30
#include <sys/ptrace.h>
31
#include <sys/reg.h>
32
#include <sys/ucontext.h>
33
#include <sys/user.h>
34
#include <sys/wait.h>
35
36
#include <machine/armreg.h>
37
#include <machine/sysarch.h>
38
39
#include <signal.h>
40
#include <stdint.h>
41
#include <stdio.h>
42
#include <stdlib.h>
43
44
#include <atf-c.h>
45
46
static unsigned long vl;
47
48
static void
49
check_for_sve(void)
50
{
51
unsigned long hwcap;
52
53
if (elf_aux_info(AT_HWCAP, &hwcap, sizeof(hwcap)) != 0)
54
atf_tc_skip("No HWCAP");
55
56
if ((hwcap & HWCAP_SVE) == 0)
57
atf_tc_skip("No SVE support in HW");
58
59
ATF_REQUIRE(sysarch(ARM64_GET_SVE_VL, &vl) == 0);
60
}
61
62
ATF_TC_WITHOUT_HEAD(sve_registers);
63
ATF_TC_BODY(sve_registers, tc)
64
{
65
uint64_t reg, val;
66
67
check_for_sve();
68
69
/* Check the ID registers are sane */
70
reg = READ_SPECIALREG(id_aa64pfr0_el1);
71
ATF_REQUIRE((reg & ID_AA64PFR0_SVE_MASK) >= ID_AA64PFR0_SVE_IMPL);
72
73
/*
74
* Store 1.0 in z0, repeated every 16 bits. Read it from d0 as
75
* the registers alias.
76
*/
77
asm volatile(
78
".arch_extension sve \n"
79
"fmov z0.h, #1 \n"
80
"str d0, [%0] \n"
81
".arch_extension nosve \n"
82
:: "r"(&val) : "z0", "d0"
83
);
84
85
/* Check for the 1.0 bit pattern */
86
ATF_REQUIRE_EQ(val, 0x3c003c003c003c00);
87
}
88
89
static void
90
sve_signal_handler(int sig __unused, siginfo_t *info, void *context)
91
{
92
struct arm64_reg_context *regctx;
93
struct sve_context *svectx;
94
ucontext_t *ctx;
95
uint64_t *sveregs;
96
97
ctx = context;
98
99
/* Check the trap is from a breakpoint instruction */
100
ATF_REQUIRE_EQ(info->si_trapno, EXCP_BRK);
101
ctx->uc_mcontext.mc_gpregs.gp_elr += 4;
102
103
/* Trash z0 to check it's not kept when exiting the handler */
104
asm volatile(
105
".arch_extension sve \n"
106
"fmov z0.h, #2 \n"
107
".arch_extension nosve \n"
108
::: "z0");
109
110
/* Change the lower bits of z1 through the siginfo struct */
111
ctx->uc_mcontext.mc_fpregs.fp_q[1] = 0x5a5a5a5a5a5a5a5a;
112
113
/* Find the SVE registers */
114
regctx = (struct arm64_reg_context *)ctx->uc_mcontext.mc_ptr;
115
if (regctx != NULL) {
116
int idx, next;
117
do {
118
if (regctx->ctx_id == ARM64_CTX_SVE)
119
break;
120
121
ATF_REQUIRE(regctx->ctx_id != ARM64_CTX_END);
122
regctx = (struct arm64_reg_context *)
123
((uintptr_t)regctx + regctx->ctx_size);
124
} while (1);
125
126
/* Update the register context */
127
svectx = (struct sve_context *)regctx;
128
ATF_REQUIRE_EQ(svectx->sve_vector_len, vl);
129
130
sveregs = (uint64_t *)(void *)(svectx + 1);
131
/* Find the array entries to change */
132
idx = 2 * svectx->sve_vector_len / sizeof(*sveregs);
133
next = 3 * svectx->sve_vector_len / sizeof(*sveregs);
134
while (idx != next) {
135
sveregs[idx] = 0xdeaddeaddeaddead;
136
idx++;
137
}
138
}
139
}
140
141
ATF_TC_WITHOUT_HEAD(sve_signal);
142
ATF_TC_BODY(sve_signal, tc)
143
{
144
struct sigaction sa = {
145
.sa_sigaction = sve_signal_handler,
146
.sa_flags = SA_SIGINFO,
147
};
148
uint64_t val0, val1, *val2;
149
150
check_for_sve();
151
ATF_REQUIRE(sigaction(SIGTRAP, &sa, NULL) == 0);
152
val2 = malloc(vl);
153
ATF_REQUIRE(val2 != NULL);
154
155
asm volatile(
156
".arch_extension sve \n"
157
"fmov z0.h, #1 \n"
158
"fmov z1.h, #1 \n"
159
"fmov z2.h, #1 \n"
160
/* Raise a SIGTRAP */
161
"brk #1 \n"
162
"str d0, [%0] \n"
163
"str d1, [%1] \n"
164
"str z2, [%2] \n"
165
".arch_extension nosve \n"
166
:: "r"(&val0), "r"(&val1), "r"(val2) : "z0", "z1", "z2", "d0", "d1"
167
);
168
169
/* Check for the 1.0 bit pattern */
170
ATF_REQUIRE_EQ(val0, 0x3c003c003c003c00);
171
/* Check for the changed bit pattern */
172
ATF_REQUIRE_EQ(val1, 0x5a5a5a5a5a5a5a5a);
173
/*
174
* Check the lower 128 bits are restored from fp_q and the
175
* upper bits are restored from the sve data region
176
*/
177
for (size_t i = 0; i < vl / sizeof(*val2); i++) {
178
if (i < 2)
179
ATF_REQUIRE_EQ(val2[i], 0x3c003c003c003c00);
180
else
181
ATF_REQUIRE_EQ(val2[i], 0xdeaddeaddeaddead);
182
}
183
184
free(val2);
185
}
186
187
ATF_TC_WITHOUT_HEAD(sve_signal_altstack);
188
ATF_TC_BODY(sve_signal_altstack, tc)
189
{
190
struct sigaction sa = {
191
.sa_sigaction = sve_signal_handler,
192
.sa_flags = SA_ONSTACK | SA_SIGINFO,
193
};
194
stack_t ss = {
195
.ss_size = SIGSTKSZ,
196
};
197
uint64_t val0, val1, *val2;
198
199
check_for_sve();
200
ss.ss_sp = malloc(ss.ss_size);
201
ATF_REQUIRE(ss.ss_sp != NULL);
202
ATF_REQUIRE(sigaltstack(&ss, NULL) == 0);
203
ATF_REQUIRE(sigaction(SIGTRAP, &sa, NULL) == 0);
204
val2 = malloc(vl);
205
ATF_REQUIRE(val2 != NULL);
206
207
asm volatile(
208
".arch_extension sve \n"
209
"fmov z0.h, #1 \n"
210
"fmov z1.h, #1 \n"
211
"fmov z2.h, #1 \n"
212
/* Raise a SIGTRAP */
213
"brk #1 \n"
214
"str d0, [%0] \n"
215
"str d1, [%1] \n"
216
"str z2, [%2] \n"
217
".arch_extension nosve \n"
218
:: "r"(&val0), "r"(&val1), "r"(val2) : "z0", "z1", "z2", "d0", "d1"
219
);
220
221
/* Check for the 1.0 bit pattern */
222
ATF_REQUIRE_EQ(val0, 0x3c003c003c003c00);
223
/* Check for the changed bit pattern */
224
ATF_REQUIRE_EQ(val1, 0x5a5a5a5a5a5a5a5a);
225
/*
226
* Check the lower 128 bits are restored from fp_q and the
227
* upper bits are restored from the sve data region
228
*/
229
for (size_t i = 0; i < vl / sizeof(*val2); i++) {
230
if (i < 2)
231
ATF_REQUIRE_EQ(val2[i], 0x3c003c003c003c00);
232
else
233
ATF_REQUIRE_EQ(val2[i], 0xdeaddeaddeaddead);
234
}
235
236
free(val2);
237
}
238
239
ATF_TC_WITHOUT_HEAD(sve_ptrace);
240
ATF_TC_BODY(sve_ptrace, tc)
241
{
242
struct reg reg;
243
struct iovec fpvec, svevec;
244
struct svereg_header *header;
245
pid_t child, wpid;
246
int status;
247
248
check_for_sve();
249
250
child = fork();
251
ATF_REQUIRE(child >= 0);
252
if (child == 0) {
253
char exec_path[1024];
254
255
/* Calculate the location of the helper */
256
snprintf(exec_path, sizeof(exec_path), "%s/sve_ptrace_helper",
257
atf_tc_get_config_var(tc, "srcdir"));
258
259
ptrace(PT_TRACE_ME, 0, NULL, 0);
260
261
/* Execute the helper so SVE will be disabled */
262
execl(exec_path, "sve_ptrace_helper", NULL);
263
_exit(1);
264
}
265
266
/* The first event should be the SIGSTOP at the start of the child */
267
wpid = waitpid(child, &status, 0);
268
ATF_REQUIRE(WIFSTOPPED(status));
269
270
fpvec.iov_base = NULL;
271
fpvec.iov_len = 0;
272
/* Read the length before SVE has been used */
273
ATF_REQUIRE(ptrace(PT_GETREGSET, wpid, (caddr_t)&fpvec, NT_ARM_SVE) ==
274
0);
275
ATF_REQUIRE(fpvec.iov_len == (sizeof(struct svereg_header) +
276
sizeof(struct fpregs)));
277
278
fpvec.iov_base = malloc(fpvec.iov_len);
279
header = fpvec.iov_base;
280
ATF_REQUIRE(fpvec.iov_base != NULL);
281
ATF_REQUIRE(ptrace(PT_GETREGSET, wpid, (caddr_t)&fpvec, NT_ARM_SVE) ==
282
0);
283
ATF_REQUIRE((header->sve_flags & SVEREG_FLAG_REGS_MASK) ==
284
SVEREG_FLAG_FP);
285
286
/* Check writing back the FP registers works */
287
ATF_REQUIRE(ptrace(PT_SETREGSET, wpid, (caddr_t)&fpvec, NT_ARM_SVE) ==
288
0);
289
290
ptrace(PT_CONTINUE, wpid, (caddr_t)1, 0);
291
292
/* The second event should be the SIGINFO at the end */
293
wpid = waitpid(child, &status, 0);
294
ATF_REQUIRE(WIFSTOPPED(status));
295
ATF_REQUIRE_EQ(WSTOPSIG(status), SIGTRAP);
296
297
/* Check writing back FP registers when SVE has started will fail */
298
ATF_REQUIRE(ptrace(PT_SETREGSET, wpid, (caddr_t)&fpvec, NT_ARM_SVE) ==
299
-1);
300
301
svevec.iov_base = NULL;
302
svevec.iov_len = 0;
303
/* Read the length after SVE has been used */
304
ATF_REQUIRE(ptrace(PT_GETREGSET, wpid, (caddr_t)&svevec, NT_ARM_SVE) ==
305
0);
306
/* TODO: Check the length is correct based on vector length */
307
ATF_REQUIRE(svevec.iov_len > (sizeof(struct svereg_header) +
308
sizeof(struct fpregs)));
309
310
svevec.iov_base = malloc(svevec.iov_len);
311
header = svevec.iov_base;
312
ATF_REQUIRE(svevec.iov_base != NULL);
313
ATF_REQUIRE(ptrace(PT_GETREGSET, wpid, (caddr_t)&svevec, NT_ARM_SVE) ==
314
0);
315
ATF_REQUIRE((header->sve_flags & SVEREG_FLAG_REGS_MASK) ==
316
SVEREG_FLAG_SVE);
317
318
/* Test writing back the SVE registers works */
319
ATF_REQUIRE(ptrace(PT_SETREGSET, wpid, (caddr_t)&svevec, NT_ARM_SVE) ==
320
0);
321
322
free(svevec.iov_base);
323
free(fpvec.iov_base);
324
325
/* Step over the brk instruction */
326
ATF_REQUIRE(ptrace(PT_GETREGS, wpid, (caddr_t)&reg, 0) != -1);
327
reg.elr += 4;
328
ATF_REQUIRE(ptrace(PT_SETREGS, wpid, (caddr_t)&reg, 0) != -1);
329
330
ptrace(PT_CONTINUE, wpid, (caddr_t)1, 0);
331
waitpid(child, &status, 0);
332
ATF_REQUIRE(WIFEXITED(status));
333
ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
334
}
335
336
ATF_TC_WITHOUT_HEAD(sve_fork_env);
337
ATF_TC_BODY(sve_fork_env, tc)
338
{
339
pid_t child, wpid;
340
int status;
341
342
check_for_sve();
343
344
child = fork();
345
ATF_REQUIRE(child >= 0);
346
347
/* Check the child environment is sane */
348
if (child == 0) {
349
unsigned long child_vl, hwcap;
350
351
if (elf_aux_info(AT_HWCAP, &hwcap, sizeof(hwcap)) != 0)
352
_exit(1);
353
354
if ((hwcap & HWCAP_SVE) == 0)
355
_exit(2);
356
357
if (sysarch(ARM64_GET_SVE_VL, &child_vl) != 0)
358
_exit(3);
359
360
if (child_vl != vl)
361
_exit(4);
362
363
_exit(0);
364
}
365
366
wpid = waitpid(child, &status, 0);
367
ATF_REQUIRE_EQ(child, wpid);
368
ATF_REQUIRE(WIFEXITED(status));
369
ATF_REQUIRE(WEXITSTATUS(status) == 0);
370
}
371
372
ATF_TC_WITHOUT_HEAD(sve_fork_regs);
373
ATF_TC_BODY(sve_fork_regs, tc)
374
{
375
pid_t child, wpid;
376
uint64_t *val;
377
int status;
378
379
check_for_sve();
380
381
/*
382
* Malloc before fork to reduce the change of trashing sve registers
383
*/
384
val = malloc(vl);
385
ATF_REQUIRE(val != NULL);
386
387
/*
388
* Store 1.0 in z0, repeated every 16 bits. Read it from d0 as
389
* the registers alias.
390
*/
391
asm volatile(
392
".arch_extension sve \n"
393
"fmov z8.h, #1 \n"
394
".arch_extension nosve \n"
395
::: "z8"
396
);
397
398
/* TODO: Move to asm to ensure z8 isn't trashed */
399
child = fork();
400
401
asm volatile(
402
".arch_extension sve \n"
403
"str z8, [%0] \n"
404
".arch_extension nosve \n"
405
:: "r"(val) : "z8"
406
);
407
408
ATF_REQUIRE(child >= 0);
409
410
/* Check the child environment is sane */
411
if (child == 0) {
412
for (size_t i = 0; i < vl / sizeof(*val); i++) {
413
if (val[i] != 0x3c003c003c003c00)
414
_exit(i + 1);
415
}
416
417
free(val);
418
_exit(0);
419
}
420
421
free(val);
422
wpid = waitpid(child, &status, 0);
423
ATF_REQUIRE_EQ(child, wpid);
424
ATF_REQUIRE(WIFEXITED(status));
425
ATF_REQUIRE(WEXITSTATUS(status) == 0);
426
}
427
428
ATF_TP_ADD_TCS(tp)
429
{
430
ATF_TP_ADD_TC(tp, sve_registers);
431
ATF_TP_ADD_TC(tp, sve_signal);
432
ATF_TP_ADD_TC(tp, sve_signal_altstack);
433
/* TODO: Check a too small signal stack */
434
ATF_TP_ADD_TC(tp, sve_ptrace);
435
ATF_TP_ADD_TC(tp, sve_fork_env);
436
ATF_TP_ADD_TC(tp, sve_fork_regs);
437
return (atf_no_error());
438
}
439
440