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