1 /*
2  * Copyright (c) 2009 ARM Ltd
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the company may not be used to endorse or promote
14  *    products derived from this software without specific prior written
15  *    permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY ARM LTD ``AS IS'' AND ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL ARM LTD BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
22  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #ifndef ARM_ASM__H
30 #define ARM_ASM__H
31 
32 #include "machine/acle-compat.h"
33 
34 #if __ARM_ARCH >= 7 && defined (__ARM_ARCH_ISA_ARM)
35 # define _ISA_ARM_7
36 #endif
37 
38 #if __ARM_ARCH >= 6 && defined (__ARM_ARCH_ISA_ARM)
39 # define _ISA_ARM_6
40 #endif
41 
42 #if __ARM_ARCH >= 5
43 # define _ISA_ARM_5
44 #endif
45 
46 #if __ARM_ARCH >= 4 && __ARM_ARCH_ISA_THUMB >= 1
47 # define _ISA_ARM_4T
48 #endif
49 
50 #if __ARM_ARCH >= 4 && __ARM_ARCH_ISA_THUMB == 0
51 # define _ISA_ARM_4
52 #endif
53 
54 
55 #if __ARM_ARCH_ISA_THUMB >= 2
56 # define _ISA_THUMB_2
57 #endif
58 
59 #if __ARM_ARCH_ISA_THUMB >= 1
60 # define _ISA_THUMB_1
61 #endif
62 
63 /* Check whether leaf function PAC signing has been requested in the
64    -mbranch-protect compile-time option.  */
65 #define LEAF_PROTECT_BIT 2
66 
67 #ifdef __ARM_FEATURE_PAC_DEFAULT
68 # define HAVE_PAC_LEAF \
69 	((__ARM_FEATURE_PAC_DEFAULT & (1 << LEAF_PROTECT_BIT)) && 1)
70 #else
71 # define HAVE_PAC_LEAF 0
72 #endif
73 
74 /* Provide default parameters for PAC-code handling in leaf-functions.  */
75 #if HAVE_PAC_LEAF
76 # ifndef PAC_LEAF_PUSH_IP
77 #  define PAC_LEAF_PUSH_IP 1
78 # endif
79 #else /* !HAVE_PAC_LEAF */
80 # undef PAC_LEAF_PUSH_IP
81 # define PAC_LEAF_PUSH_IP 0
82 #endif /* HAVE_PAC_LEAF */
83 
84 #define STACK_ALIGN_ENFORCE 0
85 
86 #ifdef __ASSEMBLER__
87 
88 /******************************************************************************
89 * Implementation of the prologue and epilogue assembler macros and their
90 * associated helper functions.
91 *
92 * These functions add support for the following:
93 *
94 * - M-profile branch target identification (BTI) landing-pads when compiled
95 *   with `-mbranch-protection=bti'.
96 * - PAC-signing and verification instructions, depending on hardware support
97 *   and whether the PAC-signing of leaf functions has been requested via the
98 *   `-mbranch-protection=pac-ret+leaf' compiler argument.
99 * - 8-byte stack alignment preservation at function entry, defaulting to the
100 *   value of STACK_ALIGN_ENFORCE.
101 *
102 * Notes:
103 * - Prologue stack alignment is implemented by detecting a push with an odd
104 *   number of registers and prepending a dummy register to the list.
105 * - If alignment is attempted on a list containing r0, compilation will result
106 *   in an error.
107 * - If alignment is attempted in a list containing r1, r0 will be prepended to
108 *   the register list and r0 will be restored prior to function return.  for
109 *   functions with non-void return types, this will result in the corruption of
110 *   the result register.
111 * - Stack alignment is enforced via the following helper macro call-chain:
112 *
113 *	{prologue|epilogue} ->_align8 -> _preprocess_reglist ->
114 *		_preprocess_reglist1 -> {_prologue|_epilogue}
115 *
116 * - Debug CFI directives are automatically added to prologues and epilogues,
117 *   assisted by `cfisavelist' and `cfirestorelist', respectively.
118 *
119 * Arguments:
120 * prologue
121 * --------
122 * - first	- If `last' specified, this serves as start of general-purpose
123 *		  register (GPR) range to push onto stack, otherwise represents
124 *		  single GPR to push onto stack.  If omitted, no GPRs pushed
125 *		  onto stack at prologue.
126 * - last	- If given, specifies inclusive upper-bound of GPR range.
127 * - push_ip	- Determines whether IP register is to be pushed to stack at
128 *		  prologue.  When pac-signing is requested, this holds the
129 *		  the pac-key.  Either 1 or 0 to push or not push, respectively.
130 *		  Default behavior: Set to value of PAC_LEAF_PUSH_IP macro.
131 * - push_lr	- Determines whether to push lr to the stack on function entry.
132 *		  Either 1 or 0  to push or not push, respectively.
133 * - align8	- Whether to enforce alignment. Either 1 or 0, with 1 requesting
134 *		  alignment.
135 *
136 * epilogue
137 * --------
138 *   The epilogue should be called passing the same arguments as those passed to
139 *   the prologue to ensure the stack is not corrupted on function return.
140 *
141 * Usage examples:
142 *
143 *   prologue push_ip=1 -> push {ip}
144 *   epilogue push_ip=1, align8=1 -> pop {r2, ip}
145 *   prologue push_ip=1, push_lr=1 -> push {ip, lr}
146 *   epilogue 1 -> pop {r1}
147 *   prologue 1, align8=1 -> push {r0, r1}
148 *   epilogue 1, push_ip=1 -> pop {r1, ip}
149 *   prologue 1, 4 -> push {r1-r4}
150 *   epilogue 1, 4 push_ip=1 -> pop {r1-r4, ip}
151 *
152 ******************************************************************************/
153 
154 /* Emit .cfi_restore directives for a consecutive sequence of registers.  */
155 	.macro cfirestorelist first, last
156 	.cfi_restore \last
157 	.if \last-\first
158 	 cfirestorelist \first, \last-1
159 	.endif
160 	.endm
161 
162 /* Emit .cfi_offset directives for a consecutive sequence of registers.  */
163 	.macro cfisavelist first, last, index=1
164 	.cfi_offset \last, -4*(\index)
165 	.if \last-\first
166 	 cfisavelist \first, \last-1, \index+1
167 	.endif
168 	.endm
169 
170 .macro _prologue first=-1, last=-1, push_ip=PAC_LEAF_PUSH_IP, push_lr=0
171 	.if \push_ip & 1 != \push_ip
172 	 .error "push_ip may be either 0 or 1"
173 	.endif
174 	.if \push_lr & 1 != \push_lr
175 	 .error "push_lr may be either 0 or 1"
176 	.endif
177 	.if \first != -1
178 	 .if \last == -1
179 	  /* Upper-bound not provided: Set upper = lower.  */
180 	  _prologue \first, \first, \push_ip, \push_lr
181 	  .exitm
182 	 .endif
183 	.endif
184 #if HAVE_PAC_LEAF
185 #if __ARM_FEATURE_BTI_DEFAULT
186 	pacbti	ip, lr, sp
187 #else
188 	pac	ip, lr, sp
189 #endif /* __ARM_FEATURE_BTI_DEFAULT */
190 	.cfi_register 143, 12
191 #else
192 #if __ARM_FEATURE_BTI_DEFAULT
193 	bti
194 #endif /* __ARM_FEATURE_BTI_DEFAULT */
195 #endif /* HAVE_PAC_LEAF */
196 	.if \first != -1
197 	 .if \last != \first
198 	  .if \last >= 13
199 	.error "SP cannot be in the save list"
200 	  .endif
201 	  .if \push_ip
202 	   .if \push_lr
203 	/* Case 1: push register range, ip and lr registers.  */
204 	push {r\first-r\last, ip, lr}
205 	.cfi_adjust_cfa_offset ((\last-\first)+3)*4
206 	.cfi_offset 14, -4
207 	.cfi_offset 143, -8
208 	cfisavelist \first, \last, 3
209 	   .else // !\push_lr
210 	/* Case 2: push register range and ip register.  */
211 	push {r\first-r\last, ip}
212 	.cfi_adjust_cfa_offset ((\last-\first)+2)*4
213 	.cfi_offset 143, -4
214 	cfisavelist \first, \last, 2
215 	   .endif
216 	  .else // !\push_ip
217 	   .if \push_lr
218 	/* Case 3: push register range and lr register.  */
219 	push {r\first-r\last, lr}
220 	.cfi_adjust_cfa_offset ((\last-\first)+2)*4
221 	.cfi_offset 14, -4
222 	cfisavelist \first, \last, 2
223 	   .else // !\push_lr
224 	/* Case 4: push register range.  */
225 	push {r\first-r\last}
226 	.cfi_adjust_cfa_offset ((\last-\first)+1)*4
227 	cfisavelist \first, \last, 1
228 	   .endif
229 	  .endif
230 	 .else // \last == \first
231 	  .if \push_ip
232 	   .if \push_lr
233 	/* Case 5: push single GP register plus ip and lr registers.  */
234 	push {r\first, ip, lr}
235 	.cfi_adjust_cfa_offset 12
236 	.cfi_offset 14, -4
237 	.cfi_offset 143, -8
238         cfisavelist \first, \first, 3
239 	   .else // !\push_lr
240 	/* Case 6: push single GP register plus ip register.  */
241 	push {r\first, ip}
242 	.cfi_adjust_cfa_offset 8
243 	.cfi_offset 143, -4
244         cfisavelist \first, \first, 2
245 	   .endif
246 	  .else // !\push_ip
247 	   .if \push_lr
248 	/* Case 7: push single GP register plus lr register.  */
249 	push {r\first, lr}
250 	.cfi_adjust_cfa_offset 8
251 	.cfi_offset 14, -4
252 	cfisavelist \first, \first, 2
253 	   .else // !\push_lr
254 	/* Case 8: push single GP register.  */
255 	push {r\first}
256 	.cfi_adjust_cfa_offset 4
257 	cfisavelist \first, \first, 1
258 	   .endif
259 	  .endif
260 	 .endif
261 	.else // \first == -1
262 	 .if \push_ip
263 	  .if \push_lr
264 	/* Case 9: push ip and lr registers.  */
265 	push {ip, lr}
266 	.cfi_adjust_cfa_offset 8
267 	.cfi_offset 14, -4
268 	.cfi_offset 143, -8
269 	  .else // !\push_lr
270 	/* Case 10: push ip register.  */
271 	push {ip}
272 	.cfi_adjust_cfa_offset 4
273 	.cfi_offset 143, -4
274 	  .endif
275 	 .else // !\push_ip
276           .if \push_lr
277 	/* Case 11: push lr register.  */
278 	push {lr}
279 	.cfi_adjust_cfa_offset 4
280 	.cfi_offset 14, -4
281           .endif
282 	 .endif
283 	.endif
284 .endm
285 
286 .macro _epilogue first=-1, last=-1, push_ip=PAC_LEAF_PUSH_IP, push_lr=0
287 	.if \push_ip & 1 != \push_ip
288 	 .error "push_ip may be either 0 or 1"
289 	.endif
290 	.if \push_lr & 1 != \push_lr
291 	 .error "push_lr may be either 0 or 1"
292 	.endif
293 	.if \first != -1
294 	 .if \last == -1
295 	  /* Upper-bound not provided: Set upper = lower.  */
296 	  _epilogue \first, \first, \push_ip, \push_lr
297 	  .exitm
298 	 .endif
299 	 .if \last != \first
300 	  .if \last >= 13
301 	.error "SP cannot be in the save list"
302 	  .endif
303 	  .if \push_ip
304 	   .if \push_lr
305 	/* Case 1: pop register range, ip and lr registers.  */
306 	pop {r\first-r\last, ip, lr}
307 	.cfi_restore 14
308 	.cfi_register 143, 12
309 	cfirestorelist \first, \last
310 	   .else // !\push_lr
311 	/* Case 2: pop register range and ip register.  */
312 	pop {r\first-r\last, ip}
313 	.cfi_register 143, 12
314 	cfirestorelist \first, \last
315 	   .endif
316 	  .else // !\push_ip
317 	   .if \push_lr
318 	/* Case 3: pop register range and lr register.  */
319 	pop {r\first-r\last, lr}
320 	.cfi_restore 14
321 	cfirestorelist \first, \last
322 	   .else // !\push_lr
323 	/* Case 4: pop register range.  */
324 	pop {r\first-r\last}
325 	cfirestorelist \first, \last
326 	   .endif
327 	  .endif
328 	 .else // \last == \first
329 	  .if \push_ip
330 	   .if \push_lr
331 	/* Case 5: pop single GP register plus ip and lr registers.  */
332 	pop {r\first, ip, lr}
333 	.cfi_restore 14
334 	.cfi_register 143, 12
335 	cfirestorelist \first, \first
336 	   .else // !\push_lr
337 	/* Case 6: pop single GP register plus ip register.  */
338 	pop {r\first, ip}
339 	.cfi_register 143, 12
340 	cfirestorelist \first, \first
341 	   .endif
342 	  .else // !\push_ip
343 	   .if \push_lr
344 	/* Case 7: pop single GP register plus lr register.  */
345 	pop {r\first, lr}
346 	.cfi_restore 14
347 	cfirestorelist \first, \first
348 	   .else // !\push_lr
349 	/* Case 8: pop single GP register.  */
350 	pop {r\first}
351 	cfirestorelist \first, \first
352 	   .endif
353 	  .endif
354 	 .endif
355 	.else // \first == -1
356 	 .if \push_ip
357 	  .if \push_lr
358 	/* Case 9: pop ip and lr registers.  */
359 	pop {ip, lr}
360 	.cfi_restore 14
361 	.cfi_register 143, 12
362 	  .else // !\push_lr
363 	/* Case 10: pop ip register.  */
364 	pop {ip}
365 	.cfi_register 143, 12
366 	  .endif
367 	 .else // !\push_ip
368           .if \push_lr
369 	/* Case 11: pop lr register.  */
370 	pop {lr}
371 	.cfi_restore 14
372           .endif
373 	 .endif
374 	.endif
375 #if HAVE_PAC_LEAF
376 	aut	ip, lr, sp
377 #endif /* HAVE_PAC_LEAF */
378 	bx	lr
379 .endm
380 
381 # clean up expressions in 'last'
382 .macro _preprocess_reglist1 first:req, last:req, push_ip:req, push_lr:req, reglist_op:req
383 	.if \last == 0
384 	 \reglist_op \first, 0, \push_ip, \push_lr
385 	.elseif \last == 1
386 	 \reglist_op \first, 1, \push_ip, \push_lr
387 	.elseif \last == 2
388 	 \reglist_op \first, 2, \push_ip, \push_lr
389 	.elseif \last == 3
390 	 \reglist_op \first, 3, \push_ip, \push_lr
391 	.elseif \last == 4
392 	 \reglist_op \first, 4, \push_ip, \push_lr
393 	.elseif \last == 5
394 	 \reglist_op \first, 5, \push_ip, \push_lr
395 	.elseif \last == 6
396 	 \reglist_op \first, 6, \push_ip, \push_lr
397 	.elseif \last == 7
398 	 \reglist_op \first, 7, \push_ip, \push_lr
399 	.elseif \last == 8
400 	 \reglist_op \first, 8, \push_ip, \push_lr
401 	.elseif \last == 9
402 	 \reglist_op \first, 9, \push_ip, \push_lr
403 	.elseif \last == 10
404 	 \reglist_op \first, 10, \push_ip, \push_lr
405 	.elseif \last == 11
406 	 \reglist_op \first, 11, \push_ip, \push_lr
407 	.else
408 	 .error "last (\last) out of range"
409 	.endif
410 .endm
411 
412 # clean up expressions in 'first'
413 .macro _preprocess_reglist first:req, last, push_ip=0, push_lr=0, reglist_op:req
414 	.ifb \last
415 	 _preprocess_reglist \first \first \push_ip \push_lr
416 	.else
417 	 .if \first > \last
418 	  .error "last (\last) must be at least as great as first (\first)"
419 	 .endif
420 	 .if \first == 0
421 	  _preprocess_reglist1 0, \last, \push_ip, \push_lr, \reglist_op
422 	 .elseif \first == 1
423 	  _preprocess_reglist1 1, \last, \push_ip, \push_lr, \reglist_op
424 	 .elseif \first == 2
425 	  _preprocess_reglist1 2, \last, \push_ip, \push_lr, \reglist_op
426 	 .elseif \first == 3
427 	  _preprocess_reglist1 3, \last, \push_ip, \push_lr, \reglist_op
428 	 .elseif \first == 4
429 	  _preprocess_reglist1 4, \last, \push_ip, \push_lr, \reglist_op
430 	 .elseif \first == 5
431 	  _preprocess_reglist1 5, \last, \push_ip, \push_lr, \reglist_op
432 	 .elseif \first == 6
433 	  _preprocess_reglist1 6, \last, \push_ip, \push_lr, \reglist_op
434 	 .elseif \first == 7
435 	  _preprocess_reglist1 7, \last, \push_ip, \push_lr, \reglist_op
436 	 .elseif \first == 8
437 	  _preprocess_reglist1 8, \last, \push_ip, \push_lr, \reglist_op
438 	 .elseif \first == 9
439 	  _preprocess_reglist1 9, \last, \push_ip, \push_lr, \reglist_op
440 	 .elseif \first == 10
441 	  _preprocess_reglist1 10, \last, \push_ip, \push_lr, \reglist_op
442 	 .elseif \first == 11
443 	  _preprocess_reglist1 11, \last, \push_ip, \push_lr, \reglist_op
444 	 .else
445 	  .error "first (\first) out of range"
446 	 .endif
447 	.endif
448 .endm
449 
450 .macro _align8 first, last, push_ip=0, push_lr=0, reglist_op=_prologue
451 	.ifb \first
452 	 .ifnb \last
453 	  .error "can't have last (\last) without specifying first"
454 	 .else // \last not blank
455 	  .if ((\push_ip + \push_lr) % 2) == 0
456 	   \reglist_op first=-1, last=-1, push_ip=\push_ip, push_lr=\push_lr
457 	   .exitm
458 	  .else // ((\push_ip + \push_lr) % 2) odd
459 	   _align8 2, 2, \push_ip, \push_lr, \reglist_op
460 	   .exitm
461 	  .endif // ((\push_ip + \push_lr) % 2) == 0
462 	 .endif // .ifnb \last
463 	.endif // .ifb \first
464 
465 	.ifb \last
466 	 _align8 \first, \first, \push_ip, \push_lr, \reglist_op
467 	.else
468 	 .if \push_ip & 1 <> \push_ip
469 	  .error "push_ip may be 0 or 1"
470 	 .endif
471 	 .if \push_lr & 1 <> \push_lr
472 	  .error "push_lr may be 0 or 1"
473 	 .endif
474 	 .ifeq (\last - \first + \push_ip + \push_lr) % 2
475 	  .if \first == 0
476 	   .error "Alignment required and first register is r0"
477 	   .exitm
478 	  .endif
479 	  _preprocess_reglist \first-1, \last, \push_ip, \push_lr, \reglist_op
480 	 .else
481 	  _preprocess_reglist \first \last, \push_ip, \push_lr, \reglist_op
482 	 .endif
483 	.endif
484 .endm
485 
486 .macro prologue first, last, push_ip=PAC_LEAF_PUSH_IP, push_lr=0, align8=STACK_ALIGN_ENFORCE
487 	.if \align8
488 	 _align8 \first, \last, \push_ip, \push_lr, _prologue
489 	.else
490 	 _prologue first=\first, last=\last, push_ip=\push_ip, push_lr=\push_lr
491 	.endif
492 .endm
493 
494 .macro epilogue first, last, push_ip=PAC_LEAF_PUSH_IP, push_lr=0, align8=STACK_ALIGN_ENFORCE
495 	.if \align8
496 	 _align8 \first, \last, \push_ip, \push_lr, reglist_op=_epilogue
497 	.else
498 	 _epilogue first=\first, last=\last, push_ip=\push_ip, push_lr=\push_lr
499 	.endif
500 .endm
501 
502 #endif /* __ASSEMBLER__ */
503 
504 #endif /* ARM_ASM__H */
505