1// SPDX-License-Identifier: GPL-2.0-only
2// Copyright (C) 2015-2019 ARM Limited.
3// Original author: Dave Martin <Dave.Martin@arm.com>
4//
5// Simple FPSIMD context switch test
6// Repeatedly writes unique test patterns into each FPSIMD register
7// and reads them back to verify integrity.
8//
9// for x in `seq 1 NR_CPUS`; do fpsimd-test & pids=$pids\ $! ; done
10// (leave it running for as long as you want...)
11// kill $pids
12
13#include <asm/unistd.h>
14#include "assembler.h"
15#include "asm-offsets.h"
16
17#define NVR	32
18#define MAXVL_B	(128 / 8)
19
20.macro _vldr Vn:req, Xt:req
21	ld1	{v\Vn\().2d}, [x\Xt]
22.endm
23
24.macro _vstr Vn:req, Xt:req
25	st1	{v\Vn\().2d}, [x\Xt]
26.endm
27
28// Generate accessor functions to read/write programmatically selected
29// FPSIMD registers.
30// x0 is the register index to access
31// x1 is the memory address to read from (getv,setp) or store to (setv,setp)
32// All clobber x0-x2
33define_accessor setv, NVR, _vldr
34define_accessor getv, NVR, _vstr
35
36// Print a single character x0 to stdout
37// Clobbers x0-x2,x8
38function putc
39	str	x0, [sp, #-16]!
40
41	mov	x0, #1			// STDOUT_FILENO
42	mov	x1, sp
43	mov	x2, #1
44	mov	x8, #__NR_write
45	svc	#0
46
47	add	sp, sp, #16
48	ret
49endfunction
50
51// Print a NUL-terminated string starting at address x0 to stdout
52// Clobbers x0-x3,x8
53function puts
54	mov	x1, x0
55
56	mov	x2, #0
570:	ldrb	w3, [x0], #1
58	cbz	w3, 1f
59	add	x2, x2, #1
60	b	0b
61
621:	mov	w0, #1			// STDOUT_FILENO
63	mov	x8, #__NR_write
64	svc	#0
65
66	ret
67endfunction
68
69// Utility macro to print a literal string
70// Clobbers x0-x4,x8
71.macro puts string
72	.pushsection .rodata.str1.1, "aMS", 1
73.L__puts_literal\@: .string "\string"
74	.popsection
75
76	ldr	x0, =.L__puts_literal\@
77	bl	puts
78.endm
79
80// Print an unsigned decimal number x0 to stdout
81// Clobbers x0-x4,x8
82function putdec
83	mov	x1, sp
84	str	x30, [sp, #-32]!	// Result can't be > 20 digits
85
86	mov	x2, #0
87	strb	w2, [x1, #-1]!		// Write the NUL terminator
88
89	mov	x2, #10
900:	udiv	x3, x0, x2		// div-mod loop to generate the digits
91	msub	x0, x3, x2, x0
92	add	w0, w0, #'0'
93	strb	w0, [x1, #-1]!
94	mov	x0, x3
95	cbnz	x3, 0b
96
97	ldrb	w0, [x1]
98	cbnz	w0, 1f
99	mov	w0, #'0'		// Print "0" for 0, not ""
100	strb	w0, [x1, #-1]!
101
1021:	mov	x0, x1
103	bl	puts
104
105	ldr	x30, [sp], #32
106	ret
107endfunction
108
109// Print an unsigned decimal number x0 to stdout, followed by a newline
110// Clobbers x0-x5,x8
111function putdecn
112	mov	x5, x30
113
114	bl	putdec
115	mov	x0, #'\n'
116	bl	putc
117
118	ret	x5
119endfunction
120
121
122// Clobbers x0-x3,x8
123function puthexb
124	str	x30, [sp, #-0x10]!
125
126	mov	w3, w0
127	lsr	w0, w0, #4
128	bl	puthexnibble
129	mov	w0, w3
130
131	ldr	x30, [sp], #0x10
132	// fall through to puthexnibble
133endfunction
134// Clobbers x0-x2,x8
135function puthexnibble
136	and	w0, w0, #0xf
137	cmp	w0, #10
138	blo	1f
139	add	w0, w0, #'a' - ('9' + 1)
1401:	add	w0, w0, #'0'
141	b	putc
142endfunction
143
144// x0=data in, x1=size in, clobbers x0-x5,x8
145function dumphex
146	str	x30, [sp, #-0x10]!
147
148	mov	x4, x0
149	mov	x5, x1
150
1510:	subs	x5, x5, #1
152	b.lo	1f
153	ldrb	w0, [x4], #1
154	bl	puthexb
155	b	0b
156
1571:	ldr	x30, [sp], #0x10
158	ret
159endfunction
160
161// Declare some storate space to shadow the SVE register contents:
162.pushsection .text
163.data
164.align 4
165vref:
166	.space	MAXVL_B * NVR
167scratch:
168	.space	MAXVL_B
169.popsection
170
171// Trivial memory copy: copy x2 bytes, starting at address x1, to address x0.
172// Clobbers x0-x3
173function memcpy
174	cmp	x2, #0
175	b.eq	1f
1760:	ldrb	w3, [x1], #1
177	strb	w3, [x0], #1
178	subs	x2, x2, #1
179	b.ne	0b
1801:	ret
181endfunction
182
183// Generate a test pattern for storage in SVE registers
184// x0: pid	(16 bits)
185// x1: register number (6 bits)
186// x2: generation (4 bits)
187function pattern
188	orr	w1, w0, w1, lsl #16
189	orr	w2, w1, w2, lsl #28
190
191	ldr	x0, =scratch
192	mov	w1, #MAXVL_B / 4
193
1940:	str	w2, [x0], #4
195	add	w2, w2, #(1 << 22)
196	subs	w1, w1, #1
197	bne	0b
198
199	ret
200endfunction
201
202// Get the address of shadow data for FPSIMD V-register V<xn>
203.macro _adrv xd, xn, nrtmp
204	ldr	\xd, =vref
205	mov	x\nrtmp, #16
206	madd	\xd, x\nrtmp, \xn, \xd
207.endm
208
209// Set up test pattern in a FPSIMD V-register
210// x0: pid
211// x1: register number
212// x2: generation
213function setup_vreg
214	mov	x4, x30
215
216	mov	x6, x1
217	bl	pattern
218	_adrv	x0, x6, 2
219	mov	x5, x0
220	ldr	x1, =scratch
221	bl	memcpy
222
223	mov	x0, x6
224	mov	x1, x5
225	bl	setv
226
227	ret	x4
228endfunction
229
230// Fill x1 bytes starting at x0 with 0xae (for canary purposes)
231// Clobbers x1, x2.
232function memfill_ae
233	mov	w2, #0xae
234	b	memfill
235endfunction
236
237// Fill x1 bytes starting at x0 with 0.
238// Clobbers x1, x2.
239function memclr
240	mov	w2, #0
241endfunction
242	// fall through to memfill
243
244// Trivial memory fill: fill x1 bytes starting at address x0 with byte w2
245// Clobbers x1
246function memfill
247	cmp	x1, #0
248	b.eq	1f
249
2500:	strb	w2, [x0], #1
251	subs	x1, x1, #1
252	b.ne	0b
253
2541:	ret
255endfunction
256
257// Trivial memory compare: compare x2 bytes starting at address x0 with
258// bytes starting at address x1.
259// Returns only if all bytes match; otherwise, the program is aborted.
260// Clobbers x0-x5.
261function memcmp
262	cbz	x2, 1f
263
264	mov	x5, #0
2650:	ldrb	w3, [x0, x5]
266	ldrb	w4, [x1, x5]
267	add	x5, x5, #1
268	cmp	w3, w4
269	b.ne	barf
270	subs	x2, x2, #1
271	b.ne	0b
272
2731:	ret
274endfunction
275
276// Verify that a FPSIMD V-register matches its shadow in memory, else abort
277// x0: reg number
278// Clobbers x0-x5.
279function check_vreg
280	mov	x3, x30
281
282	_adrv	x5, x0, 6
283	mov	x4, x0
284	ldr	x7, =scratch
285
286	mov	x0, x7
287	mov	x1, x6
288	bl	memfill_ae
289
290	mov	x0, x4
291	mov	x1, x7
292	bl	getv
293
294	mov	x0, x5
295	mov	x1, x7
296	mov	x2, x6
297	mov	x30, x3
298	b	memcmp
299endfunction
300
301// Any SVE register modified here can cause corruption in the main
302// thread -- but *only* the registers modified here.
303function irritator_handler
304	// Increment the irritation signal count (x23):
305	ldr	x0, [x2, #ucontext_regs + 8 * 23]
306	add	x0, x0, #1
307	str	x0, [x2, #ucontext_regs + 8 * 23]
308
309	// Corrupt some random V-regs
310	adr	x0, .text + (irritator_handler - .text) / 16 * 16
311	movi	v0.8b, #7
312	movi	v9.16b, #9
313	movi	v31.8b, #31
314
315	ret
316endfunction
317
318function terminate_handler
319	mov	w21, w0
320	mov	x20, x2
321
322	puts	"Terminated by signal "
323	mov	w0, w21
324	bl	putdec
325	puts	", no error, iterations="
326	ldr	x0, [x20, #ucontext_regs + 8 * 22]
327	bl	putdec
328	puts	", signals="
329	ldr	x0, [x20, #ucontext_regs + 8 * 23]
330	bl	putdecn
331
332	mov	x0, #0
333	mov	x8, #__NR_exit
334	svc	#0
335endfunction
336
337// w0: signal number
338// x1: sa_action
339// w2: sa_flags
340// Clobbers x0-x6,x8
341function setsignal
342	str	x30, [sp, #-((sa_sz + 15) / 16 * 16 + 16)]!
343
344	mov	w4, w0
345	mov	x5, x1
346	mov	w6, w2
347
348	add	x0, sp, #16
349	mov	x1, #sa_sz
350	bl	memclr
351
352	mov	w0, w4
353	add	x1, sp, #16
354	str	w6, [x1, #sa_flags]
355	str	x5, [x1, #sa_handler]
356	mov	x2, #0
357	mov	x3, #sa_mask_sz
358	mov	x8, #__NR_rt_sigaction
359	svc	#0
360
361	cbz	w0, 1f
362
363	puts	"sigaction failure\n"
364	b	.Labort
365
3661:	ldr	x30, [sp], #((sa_sz + 15) / 16 * 16 + 16)
367	ret
368endfunction
369
370// Main program entry point
371.globl _start
372function _start
373_start:
374	// Sanity-check and report the vector length
375
376	mov	x19, #128
377	cmp	x19, #128
378	b.lo	1f
379	cmp	x19, #2048
380	b.hi	1f
381	tst	x19, #(8 - 1)
382	b.eq	2f
383
3841:	puts	"Bad vector length: "
385	mov	x0, x19
386	bl	putdecn
387	b	.Labort
388
3892:	puts	"Vector length:\t"
390	mov	x0, x19
391	bl	putdec
392	puts	" bits\n"
393
394	// Obtain our PID, to ensure test pattern uniqueness between processes
395
396	mov	x8, #__NR_getpid
397	svc	#0
398	mov	x20, x0
399
400	puts	"PID:\t"
401	mov	x0, x20
402	bl	putdecn
403
404	mov	x23, #0		// Irritation signal count
405
406	mov	w0, #SIGINT
407	adr	x1, terminate_handler
408	mov	w2, #SA_SIGINFO
409	bl	setsignal
410
411	mov	w0, #SIGTERM
412	adr	x1, terminate_handler
413	mov	w2, #SA_SIGINFO
414	bl	setsignal
415
416	mov	w0, #SIGUSR1
417	adr	x1, irritator_handler
418	mov	w2, #SA_SIGINFO
419	orr	w2, w2, #SA_NODEFER
420	bl	setsignal
421
422	mov	x22, #0		// generation number, increments per iteration
423.Ltest_loop:
424
425	mov	x21, #0		// Set up V-regs & shadow with test pattern
4260:	mov	x0, x20
427	mov	x1, x21
428	and	x2, x22, #0xf
429	bl	setup_vreg
430	add	x21, x21, #1
431	cmp	x21, #NVR
432	b.lo	0b
433
434// Can't do this when SVE state is volatile across SVC:
435	mov	x8, #__NR_sched_yield	// Encourage preemption
436	svc	#0
437
438	mov	x21, #0
4390:	mov	x0, x21
440	bl	check_vreg
441	add	x21, x21, #1
442	cmp	x21, #NVR
443	b.lo	0b
444
445	add	x22, x22, #1
446	b	.Ltest_loop
447
448.Labort:
449	mov	x0, #0
450	mov	x1, #SIGABRT
451	mov	x8, #__NR_kill
452	svc	#0
453endfunction
454
455function barf
456	mov	x10, x0	// expected data
457	mov	x11, x1	// actual data
458	mov	x12, x2	// data size
459
460	puts	"Mismatch: PID="
461	mov	x0, x20
462	bl	putdec
463	puts	", iteration="
464	mov	x0, x22
465	bl	putdec
466	puts	", reg="
467	mov	x0, x21
468	bl	putdecn
469	puts	"\tExpected ["
470	mov	x0, x10
471	mov	x1, x12
472	bl	dumphex
473	puts	"]\n\tGot      ["
474	mov	x0, x11
475	mov	x1, x12
476	bl	dumphex
477	puts	"]\n"
478
479	mov	x8, #__NR_exit
480	mov	x1, #1
481	svc	#0
482endfunction
483