1 /*
2  * Macros for asm code.  Arm version.
3  *
4  * Copyright (c) 2019-2022, Arm Limited.
5  * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception
6  */
7 
8 #ifndef _ASMDEFS_H
9 #define _ASMDEFS_H
10 
11 /* Check whether leaf function PAC signing has been requested in the
12    -mbranch-protect compile-time option.  */
13 #define LEAF_PROTECT_BIT 2
14 
15 #ifdef __ARM_FEATURE_PAC_DEFAULT
16 # define HAVE_PAC_LEAF \
17 	((__ARM_FEATURE_PAC_DEFAULT & (1 << LEAF_PROTECT_BIT)) && 1)
18 #else
19 # define HAVE_PAC_LEAF 0
20 #endif
21 
22 /* Provide default parameters for PAC-code handling in leaf-functions.  */
23 #if HAVE_PAC_LEAF
24 # ifndef PAC_LEAF_PUSH_IP
25 #  define PAC_LEAF_PUSH_IP 1
26 # endif
27 #else /* !HAVE_PAC_LEAF */
28 # undef PAC_LEAF_PUSH_IP
29 # define PAC_LEAF_PUSH_IP 0
30 #endif /* HAVE_PAC_LEAF */
31 
32 #define STACK_ALIGN_ENFORCE 0
33 
34 /******************************************************************************
35 * Implementation of the prologue and epilogue assembler macros and their
36 * associated helper functions.
37 *
38 * These functions add support for the following:
39 *
40 * - M-profile branch target identification (BTI) landing-pads when compiled
41 *   with `-mbranch-protection=bti'.
42 * - PAC-signing and verification instructions, depending on hardware support
43 *   and whether the PAC-signing of leaf functions has been requested via the
44 *   `-mbranch-protection=pac-ret+leaf' compiler argument.
45 * - 8-byte stack alignment preservation at function entry, defaulting to the
46 *   value of STACK_ALIGN_ENFORCE.
47 *
48 * Notes:
49 * - Prologue stack alignment is implemented by detecting a push with an odd
50 *   number of registers and prepending a dummy register to the list.
51 * - If alignment is attempted on a list containing r0, compilation will result
52 *   in an error.
53 * - If alignment is attempted in a list containing r1, r0 will be prepended to
54 *   the register list and r0 will be restored prior to function return.  for
55 *   functions with non-void return types, this will result in the corruption of
56 *   the result register.
57 * - Stack alignment is enforced via the following helper macro call-chain:
58 *
59 *	{prologue|epilogue} ->_align8 -> _preprocess_reglist ->
60 *		_preprocess_reglist1 -> {_prologue|_epilogue}
61 *
62 * - Debug CFI directives are automatically added to prologues and epilogues,
63 *   assisted by `cfisavelist' and `cfirestorelist', respectively.
64 *
65 * Arguments:
66 * prologue
67 * --------
68 * - first	- If `last' specified, this serves as start of general-purpose
69 *		  register (GPR) range to push onto stack, otherwise represents
70 *		  single GPR to push onto stack.  If omitted, no GPRs pushed
71 *		  onto stack at prologue.
72 * - last	- If given, specifies inclusive upper-bound of GPR range.
73 * - push_ip	- Determines whether IP register is to be pushed to stack at
74 *		  prologue.  When pac-signing is requested, this holds the
75 *		  the pac-key.  Either 1 or 0 to push or not push, respectively.
76 *		  Default behavior: Set to value of PAC_LEAF_PUSH_IP macro.
77 * - push_lr	- Determines whether to push lr to the stack on function entry.
78 *		  Either 1 or 0  to push or not push, respectively.
79 * - align8	- Whether to enforce alignment. Either 1 or 0, with 1 requesting
80 *		  alignment.
81 *
82 * epilogue
83 * --------
84 *   The epilogue should be called passing the same arguments as those passed to
85 *   the prologue to ensure the stack is not corrupted on function return.
86 *
87 * Usage examples:
88 *
89 *   prologue push_ip=1 -> push {ip}
90 *   epilogue push_ip=1, align8=1 -> pop {r2, ip}
91 *   prologue push_ip=1, push_lr=1 -> push {ip, lr}
92 *   epilogue 1 -> pop {r1}
93 *   prologue 1, align8=1 -> push {r0, r1}
94 *   epilogue 1, push_ip=1 -> pop {r1, ip}
95 *   prologue 1, 4 -> push {r1-r4}
96 *   epilogue 1, 4 push_ip=1 -> pop {r1-r4, ip}
97 *
98 ******************************************************************************/
99 
100 /* Emit .cfi_restore directives for a consecutive sequence of registers.  */
101 	.macro cfirestorelist first, last
102 	.cfi_restore \last
103 	.if \last-\first
104 	 cfirestorelist \first, \last-1
105 	.endif
106 	.endm
107 
108 /* Emit .cfi_offset directives for a consecutive sequence of registers.  */
109 	.macro cfisavelist first, last, index=1
110 	.cfi_offset \last, -4*(\index)
111 	.if \last-\first
112 	 cfisavelist \first, \last-1, \index+1
113 	.endif
114 	.endm
115 
116 .macro _prologue first=-1, last=-1, push_ip=PAC_LEAF_PUSH_IP, push_lr=0
117 	.if \push_ip & 1 != \push_ip
118 	 .error "push_ip may be either 0 or 1"
119 	.endif
120 	.if \push_lr & 1 != \push_lr
121 	 .error "push_lr may be either 0 or 1"
122 	.endif
123 	.if \first != -1
124 	 .if \last == -1
125 	  /* Upper-bound not provided: Set upper = lower.  */
126 	  _prologue \first, \first, \push_ip, \push_lr
127 	  .exitm
128 	 .endif
129 	.endif
130 #if HAVE_PAC_LEAF
131 # if __ARM_FEATURE_BTI_DEFAULT
132 	pacbti	ip, lr, sp
133 # else
134 	pac	ip, lr, sp
135 # endif /* __ARM_FEATURE_BTI_DEFAULT */
136 	.cfi_register 143, 12
137 #else
138 # if __ARM_FEATURE_BTI_DEFAULT
139 	bti
140 # endif /* __ARM_FEATURE_BTI_DEFAULT */
141 #endif /* HAVE_PAC_LEAF */
142 	.if \first != -1
143 	 .if \last != \first
144 	  .if \last >= 13
145 	.error "SP cannot be in the save list"
146 	  .endif
147 	  .if \push_ip
148 	   .if \push_lr
149 	/* Case 1: push register range, ip and lr registers.  */
150 	push {r\first-r\last, ip, lr}
151 	.cfi_adjust_cfa_offset ((\last-\first)+3)*4
152 	.cfi_offset 14, -4
153 	.cfi_offset 143, -8
154 	cfisavelist \first, \last, 3
155 	   .else // !\push_lr
156 	/* Case 2: push register range and ip register.  */
157 	push {r\first-r\last, ip}
158 	.cfi_adjust_cfa_offset ((\last-\first)+2)*4
159 	.cfi_offset 143, -4
160 	cfisavelist \first, \last, 2
161 	   .endif
162 	  .else // !\push_ip
163 	   .if \push_lr
164 	/* Case 3: push register range and lr register.  */
165 	push {r\first-r\last, lr}
166 	.cfi_adjust_cfa_offset ((\last-\first)+2)*4
167 	.cfi_offset 14, -4
168 	cfisavelist \first, \last, 2
169 	   .else // !\push_lr
170 	/* Case 4: push register range.  */
171 	push {r\first-r\last}
172 	.cfi_adjust_cfa_offset ((\last-\first)+1)*4
173 	cfisavelist \first, \last, 1
174 	   .endif
175 	  .endif
176 	 .else // \last == \first
177 	  .if \push_ip
178 	   .if \push_lr
179 	/* Case 5: push single GP register plus ip and lr registers.  */
180 	push {r\first, ip, lr}
181 	.cfi_adjust_cfa_offset 12
182 	.cfi_offset 14, -4
183 	.cfi_offset 143, -8
184         cfisavelist \first, \first, 3
185 	   .else // !\push_lr
186 	/* Case 6: push single GP register plus ip register.  */
187 	push {r\first, ip}
188 	.cfi_adjust_cfa_offset 8
189 	.cfi_offset 143, -4
190         cfisavelist \first, \first, 2
191 	   .endif
192 	  .else // !\push_ip
193 	   .if \push_lr
194 	/* Case 7: push single GP register plus lr register.  */
195 	push {r\first, lr}
196 	.cfi_adjust_cfa_offset 8
197 	.cfi_offset 14, -4
198 	cfisavelist \first, \first, 2
199 	   .else // !\push_lr
200 	/* Case 8: push single GP register.  */
201 	push {r\first}
202 	.cfi_adjust_cfa_offset 4
203 	cfisavelist \first, \first, 1
204 	   .endif
205 	  .endif
206 	 .endif
207 	.else // \first == -1
208 	 .if \push_ip
209 	  .if \push_lr
210 	/* Case 9: push ip and lr registers.  */
211 	push {ip, lr}
212 	.cfi_adjust_cfa_offset 8
213 	.cfi_offset 14, -4
214 	.cfi_offset 143, -8
215 	  .else // !\push_lr
216 	/* Case 10: push ip register.  */
217 	push {ip}
218 	.cfi_adjust_cfa_offset 4
219 	.cfi_offset 143, -4
220 	  .endif
221 	 .else // !\push_ip
222           .if \push_lr
223 	/* Case 11: push lr register.  */
224 	push {lr}
225 	.cfi_adjust_cfa_offset 4
226 	.cfi_offset 14, -4
227           .endif
228 	 .endif
229 	.endif
230 .endm
231 
232 .macro _epilogue first=-1, last=-1, push_ip=PAC_LEAF_PUSH_IP, push_lr=0
233 	.if \push_ip & 1 != \push_ip
234 	 .error "push_ip may be either 0 or 1"
235 	.endif
236 	.if \push_lr & 1 != \push_lr
237 	 .error "push_lr may be either 0 or 1"
238 	.endif
239 	.if \first != -1
240 	 .if \last == -1
241 	  /* Upper-bound not provided: Set upper = lower.  */
242 	  _epilogue \first, \first, \push_ip, \push_lr
243 	  .exitm
244 	 .endif
245 	 .if \last != \first
246 	  .if \last >= 13
247 	.error "SP cannot be in the save list"
248 	  .endif
249 	  .if \push_ip
250 	   .if \push_lr
251 	/* Case 1: pop register range, ip and lr registers.  */
252 	pop {r\first-r\last, ip, lr}
253 	.cfi_restore 14
254 	.cfi_register 143, 12
255 	cfirestorelist \first, \last
256 	   .else // !\push_lr
257 	/* Case 2: pop register range and ip register.  */
258 	pop {r\first-r\last, ip}
259 	.cfi_register 143, 12
260 	cfirestorelist \first, \last
261 	   .endif
262 	  .else // !\push_ip
263 	   .if \push_lr
264 	/* Case 3: pop register range and lr register.  */
265 	pop {r\first-r\last, lr}
266 	.cfi_restore 14
267 	cfirestorelist \first, \last
268 	   .else // !\push_lr
269 	/* Case 4: pop register range.  */
270 	pop {r\first-r\last}
271 	cfirestorelist \first, \last
272 	   .endif
273 	  .endif
274 	 .else // \last == \first
275 	  .if \push_ip
276 	   .if \push_lr
277 	/* Case 5: pop single GP register plus ip and lr registers.  */
278 	pop {r\first, ip, lr}
279 	.cfi_restore 14
280 	.cfi_register 143, 12
281 	cfirestorelist \first, \first
282 	   .else // !\push_lr
283 	/* Case 6: pop single GP register plus ip register.  */
284 	pop {r\first, ip}
285 	.cfi_register 143, 12
286 	cfirestorelist \first, \first
287 	   .endif
288 	  .else // !\push_ip
289 	   .if \push_lr
290 	/* Case 7: pop single GP register plus lr register.  */
291 	pop {r\first, lr}
292 	.cfi_restore 14
293 	cfirestorelist \first, \first
294 	   .else // !\push_lr
295 	/* Case 8: pop single GP register.  */
296 	pop {r\first}
297 	cfirestorelist \first, \first
298 	   .endif
299 	  .endif
300 	 .endif
301 	.else // \first == -1
302 	 .if \push_ip
303 	  .if \push_lr
304 	/* Case 9: pop ip and lr registers.  */
305 	pop {ip, lr}
306 	.cfi_restore 14
307 	.cfi_register 143, 12
308 	  .else // !\push_lr
309 	/* Case 10: pop ip register.  */
310 	pop {ip}
311 	.cfi_register 143, 12
312 	  .endif
313 	 .else // !\push_ip
314           .if \push_lr
315 	/* Case 11: pop lr register.  */
316 	pop {lr}
317 	.cfi_restore 14
318           .endif
319 	 .endif
320 	.endif
321 #if HAVE_PAC_LEAF
322 	aut	ip, lr, sp
323 #endif /* HAVE_PAC_LEAF */
324 	bx	lr
325 .endm
326 
327 /* Clean up expressions in 'last'.  */
328 .macro _preprocess_reglist1 first:req, last:req, push_ip:req, push_lr:req, reglist_op:req
329 	.if \last == 0
330 	 \reglist_op \first, 0, \push_ip, \push_lr
331 	.elseif \last == 1
332 	 \reglist_op \first, 1, \push_ip, \push_lr
333 	.elseif \last == 2
334 	 \reglist_op \first, 2, \push_ip, \push_lr
335 	.elseif \last == 3
336 	 \reglist_op \first, 3, \push_ip, \push_lr
337 	.elseif \last == 4
338 	 \reglist_op \first, 4, \push_ip, \push_lr
339 	.elseif \last == 5
340 	 \reglist_op \first, 5, \push_ip, \push_lr
341 	.elseif \last == 6
342 	 \reglist_op \first, 6, \push_ip, \push_lr
343 	.elseif \last == 7
344 	 \reglist_op \first, 7, \push_ip, \push_lr
345 	.elseif \last == 8
346 	 \reglist_op \first, 8, \push_ip, \push_lr
347 	.elseif \last == 9
348 	 \reglist_op \first, 9, \push_ip, \push_lr
349 	.elseif \last == 10
350 	 \reglist_op \first, 10, \push_ip, \push_lr
351 	.elseif \last == 11
352 	 \reglist_op \first, 11, \push_ip, \push_lr
353 	.else
354 	 .error "last (\last) out of range"
355 	.endif
356 .endm
357 
358 /* Clean up expressions in 'first'.  */
359 .macro _preprocess_reglist first:req, last, push_ip=0, push_lr=0, reglist_op:req
360 	.ifb \last
361 	 _preprocess_reglist \first \first \push_ip \push_lr
362 	.else
363 	 .if \first > \last
364 	  .error "last (\last) must be at least as great as first (\first)"
365 	 .endif
366 	 .if \first == 0
367 	  _preprocess_reglist1 0, \last, \push_ip, \push_lr, \reglist_op
368 	 .elseif \first == 1
369 	  _preprocess_reglist1 1, \last, \push_ip, \push_lr, \reglist_op
370 	 .elseif \first == 2
371 	  _preprocess_reglist1 2, \last, \push_ip, \push_lr, \reglist_op
372 	 .elseif \first == 3
373 	  _preprocess_reglist1 3, \last, \push_ip, \push_lr, \reglist_op
374 	 .elseif \first == 4
375 	  _preprocess_reglist1 4, \last, \push_ip, \push_lr, \reglist_op
376 	 .elseif \first == 5
377 	  _preprocess_reglist1 5, \last, \push_ip, \push_lr, \reglist_op
378 	 .elseif \first == 6
379 	  _preprocess_reglist1 6, \last, \push_ip, \push_lr, \reglist_op
380 	 .elseif \first == 7
381 	  _preprocess_reglist1 7, \last, \push_ip, \push_lr, \reglist_op
382 	 .elseif \first == 8
383 	  _preprocess_reglist1 8, \last, \push_ip, \push_lr, \reglist_op
384 	 .elseif \first == 9
385 	  _preprocess_reglist1 9, \last, \push_ip, \push_lr, \reglist_op
386 	 .elseif \first == 10
387 	  _preprocess_reglist1 10, \last, \push_ip, \push_lr, \reglist_op
388 	 .elseif \first == 11
389 	  _preprocess_reglist1 11, \last, \push_ip, \push_lr, \reglist_op
390 	 .else
391 	  .error "first (\first) out of range"
392 	 .endif
393 	.endif
394 .endm
395 
396 .macro _align8 first, last, push_ip=0, push_lr=0, reglist_op=_prologue
397 	.ifb \first
398 	 .ifnb \last
399 	  .error "can't have last (\last) without specifying first"
400 	 .else // \last not blank
401 	  .if ((\push_ip + \push_lr) % 2) == 0
402 	   \reglist_op first=-1, last=-1, push_ip=\push_ip, push_lr=\push_lr
403 	   .exitm
404 	  .else // ((\push_ip + \push_lr) % 2) odd
405 	   _align8 2, 2, \push_ip, \push_lr, \reglist_op
406 	   .exitm
407 	  .endif // ((\push_ip + \push_lr) % 2) == 0
408 	 .endif // .ifnb \last
409 	.endif // .ifb \first
410 
411 	.ifb \last
412 	 _align8 \first, \first, \push_ip, \push_lr, \reglist_op
413 	.else
414 	 .if \push_ip & 1 <> \push_ip
415 	  .error "push_ip may be 0 or 1"
416 	 .endif
417 	 .if \push_lr & 1 <> \push_lr
418 	  .error "push_lr may be 0 or 1"
419 	 .endif
420 	 .ifeq (\last - \first + \push_ip + \push_lr) % 2
421 	  .if \first == 0
422 	   .error "Alignment required and first register is r0"
423 	   .exitm
424 	  .endif
425 	  _preprocess_reglist \first-1, \last, \push_ip, \push_lr, \reglist_op
426 	 .else
427 	  _preprocess_reglist \first \last, \push_ip, \push_lr, \reglist_op
428 	 .endif
429 	.endif
430 .endm
431 
432 .macro prologue first, last, push_ip=PAC_LEAF_PUSH_IP, push_lr=0, align8=STACK_ALIGN_ENFORCE
433 	.if \align8
434 	 _align8 \first, \last, \push_ip, \push_lr, _prologue
435 	.else
436 	 _prologue first=\first, last=\last, push_ip=\push_ip, push_lr=\push_lr
437 	.endif
438 .endm
439 
440 .macro epilogue first, last, push_ip=PAC_LEAF_PUSH_IP, push_lr=0, align8=STACK_ALIGN_ENFORCE
441 	.if \align8
442 	 _align8 \first, \last, \push_ip, \push_lr, reglist_op=_epilogue
443 	.else
444 	 _epilogue first=\first, last=\last, push_ip=\push_ip, push_lr=\push_lr
445 	.endif
446 .endm
447 
448 #define ENTRY_ALIGN(name, alignment)	\
449   .global name;		\
450   .type name,%function;	\
451   .align alignment;		\
452   name:			\
453   .fnstart;		\
454   .cfi_startproc;
455 
456 #define ENTRY(name)	ENTRY_ALIGN(name, 6)
457 
458 #define ENTRY_ALIAS(name)	\
459   .global name;		\
460   .type name,%function;	\
461   name:
462 
463 #if defined (IS_LEAF)
464 # define END_UNWIND .cantunwind;
465 #else
466 # define END_UNWIND
467 #endif
468 
469 #define END(name)	\
470   .cfi_endproc;		\
471   END_UNWIND		\
472   .fnend;		\
473   .size name, .-name;
474 
475 #define L(l) .L ## l
476 
477 #endif
478