xref: /linux/tools/testing/selftests/arm64/gcs/gcs-stress-thread.S (revision 364eeb79a213fcf9164208b53764223ad522d6b3)
1// Program that loops for ever doing lots of recursions and system calls,
2// intended to be used as part of a stress test for GCS context switching.
3//
4// Copyright 2015-2023 Arm Ltd
5
6#include <asm/unistd.h>
7
8#define sa_sz 32
9#define sa_flags 8
10#define sa_handler 0
11#define sa_mask_sz 8
12
13#define si_code 8
14
15#define SIGINT 2
16#define SIGABRT 6
17#define SIGUSR1 10
18#define SIGSEGV 11
19#define SIGUSR2 12
20#define SIGTERM 15
21#define SEGV_CPERR 10
22
23#define SA_NODEFER 1073741824
24#define SA_SIGINFO 4
25#define ucontext_regs 184
26
27#define PR_SET_SHADOW_STACK_STATUS      75
28# define PR_SHADOW_STACK_ENABLE         (1UL << 0)
29
30#define	GCSPR_EL0 S3_3_C2_C5_1
31
32.macro function name
33	.macro endfunction
34		.type \name, @function
35		.purgem endfunction
36	.endm
37\name:
38.endm
39
40// Print a single character x0 to stdout
41// Clobbers x0-x2,x8
42function putc
43	str	x0, [sp, #-16]!
44
45	mov	x0, #1			// STDOUT_FILENO
46	mov	x1, sp
47	mov	x2, #1
48	mov	x8, #__NR_write
49	svc	#0
50
51	add	sp, sp, #16
52	ret
53endfunction
54.globl	putc
55
56// Print a NUL-terminated string starting at address x0 to stdout
57// Clobbers x0-x3,x8
58function puts
59	mov	x1, x0
60
61	mov	x2, #0
620:	ldrb	w3, [x0], #1
63	cbz	w3, 1f
64	add	x2, x2, #1
65	b	0b
66
671:	mov	w0, #1			// STDOUT_FILENO
68	mov	x8, #__NR_write
69	svc	#0
70
71	ret
72endfunction
73.globl	puts
74
75// Utility macro to print a literal string
76// Clobbers x0-x4,x8
77.macro puts string
78	.pushsection .rodata.str1.1, "aMS", @progbits, 1
79.L__puts_literal\@: .string "\string"
80	.popsection
81
82	ldr	x0, =.L__puts_literal\@
83	bl	puts
84.endm
85
86// Print an unsigned decimal number x0 to stdout
87// Clobbers x0-x4,x8
88function putdec
89	mov	x1, sp
90	str	x30, [sp, #-32]!	// Result can't be > 20 digits
91
92	mov	x2, #0
93	strb	w2, [x1, #-1]!		// Write the NUL terminator
94
95	mov	x2, #10
960:	udiv	x3, x0, x2		// div-mod loop to generate the digits
97	msub	x0, x3, x2, x0
98	add	w0, w0, #'0'
99	strb	w0, [x1, #-1]!
100	mov	x0, x3
101	cbnz	x3, 0b
102
103	ldrb	w0, [x1]
104	cbnz	w0, 1f
105	mov	w0, #'0'		// Print "0" for 0, not ""
106	strb	w0, [x1, #-1]!
107
1081:	mov	x0, x1
109	bl	puts
110
111	ldr	x30, [sp], #32
112	ret
113endfunction
114.globl	putdec
115
116// Print an unsigned decimal number x0 to stdout, followed by a newline
117// Clobbers x0-x5,x8
118function putdecn
119	mov	x5, x30
120
121	bl	putdec
122	mov	x0, #'\n'
123	bl	putc
124
125	ret	x5
126endfunction
127.globl	putdecn
128
129// Fill x1 bytes starting at x0 with 0.
130// Clobbers x1, x2.
131function memclr
132	mov	w2, #0
133endfunction
134.globl	memclr
135	// fall through to memfill
136
137// Trivial memory fill: fill x1 bytes starting at address x0 with byte w2
138// Clobbers x1
139function memfill
140	cmp	x1, #0
141	b.eq	1f
142
1430:	strb	w2, [x0], #1
144	subs	x1, x1, #1
145	b.ne	0b
146
1471:	ret
148endfunction
149.globl	memfill
150
151// w0: signal number
152// x1: sa_action
153// w2: sa_flags
154// Clobbers x0-x6,x8
155function setsignal
156	str	x30, [sp, #-((sa_sz + 15) / 16 * 16 + 16)]!
157
158	mov	w4, w0
159	mov	x5, x1
160	mov	w6, w2
161
162	add	x0, sp, #16
163	mov	x1, #sa_sz
164	bl	memclr
165
166	mov	w0, w4
167	add	x1, sp, #16
168	str	w6, [x1, #sa_flags]
169	str	x5, [x1, #sa_handler]
170	mov	x2, #0
171	mov	x3, #sa_mask_sz
172	mov	x8, #__NR_rt_sigaction
173	svc	#0
174
175	cbz	w0, 1f
176
177	puts	"sigaction failure\n"
178	b	abort
179
1801:	ldr	x30, [sp], #((sa_sz + 15) / 16 * 16 + 16)
181	ret
182endfunction
183
184
185function tickle_handler
186	// Perhaps collect GCSPR_EL0 here in future?
187	ret
188endfunction
189
190function terminate_handler
191	mov	w21, w0
192	mov	x20, x2
193
194	puts	"Terminated by signal "
195	mov	w0, w21
196	bl	putdec
197	puts	", no error\n"
198
199	mov	x0, #0
200	mov	x8, #__NR_exit
201	svc	#0
202endfunction
203
204function segv_handler
205	// stash the siginfo_t *
206	mov	x20, x1
207
208	// Disable GCS, we don't want additional faults logging things
209	mov	x0, PR_SET_SHADOW_STACK_STATUS
210	mov	x1, xzr
211	mov	x2, xzr
212	mov	x3, xzr
213	mov	x4, xzr
214	mov	x5, xzr
215	mov	x8, #__NR_prctl
216	svc	#0
217
218	puts	"Got SIGSEGV code "
219
220	ldr	x21, [x20, #si_code]
221	mov	x0, x21
222	bl	putdec
223
224	// GCS faults should have si_code SEGV_CPERR
225	cmp	x21, #SEGV_CPERR
226	bne	1f
227
228	puts	" (GCS violation)"
2291:
230	mov	x0, '\n'
231	bl	putc
232	b	abort
233endfunction
234
235// Recurse x20 times
236.macro recurse id
237function recurse\id
238	stp	x29, x30, [sp, #-16]!
239	mov	x29, sp
240
241	cmp	x20, 0
242	beq	1f
243	sub	x20, x20, 1
244	bl	recurse\id
245
2461:
247	ldp	x29, x30, [sp], #16
248
249	// Do a syscall immediately prior to returning to try to provoke
250	// scheduling and migration at a point where coherency issues
251	// might trigger.
252	mov	x8, #__NR_getpid
253	svc	#0
254
255	ret
256endfunction
257.endm
258
259// Generate and use two copies so we're changing the GCS contents
260recurse 1
261recurse 2
262
263.globl _start
264function _start
265	// Run with GCS
266	mov	x0, PR_SET_SHADOW_STACK_STATUS
267	mov	x1, PR_SHADOW_STACK_ENABLE
268	mov	x2, xzr
269	mov	x3, xzr
270	mov	x4, xzr
271	mov	x5, xzr
272	mov	x8, #__NR_prctl
273	svc	#0
274	cbz	x0, 1f
275	puts	"Failed to enable GCS\n"
276	b	abort
2771:
278
279	mov	w0, #SIGTERM
280	adr	x1, terminate_handler
281	mov	w2, #SA_SIGINFO
282	bl	setsignal
283
284	mov	w0, #SIGUSR1
285	adr	x1, tickle_handler
286	mov	w2, #SA_SIGINFO
287	orr	w2, w2, #SA_NODEFER
288	bl	setsignal
289
290	mov	w0, #SIGSEGV
291	adr	x1, segv_handler
292	mov	w2, #SA_SIGINFO
293	orr	w2, w2, #SA_NODEFER
294	bl	setsignal
295
296	puts	"Running\n"
297
298loop:
299	// Small recursion depth so we're frequently flipping between
300	// the two recursors and changing what's on the stack
301	mov	x20, #5
302	bl	recurse1
303	mov	x20, #5
304	bl	recurse2
305	b	loop
306endfunction
307
308abort:
309	mov	x0, #255
310	mov	x8, #__NR_exit
311	svc	#0
312