1/* This is a simple version of setjmp and longjmp.
2
3   Copyright (c) 1997 Nick Clifton, Cygnus Solutions
4 */
5
6#include <picolibc.h>
7
8#include "machine/acle-compat.h"
9
10/* ANSI concatenation macros.  */
11#define CONCAT(a, b)  CONCAT2(a, b)
12#define CONCAT2(a, b) a##b
13
14#ifndef __USER_LABEL_PREFIX__
15#error  __USER_LABEL_PREFIX__ not defined
16#endif
17
18#define SYM(x) CONCAT (__USER_LABEL_PREFIX__, x)
19
20#ifdef __ELF__
21#define TYPE(x) .type SYM(x),function
22#define SIZE(x) .size SYM(x), . - SYM(x)
23#else
24#define TYPE(x)
25#define SIZE(x)
26#endif
27
28/* Jump buffer allocation sizes.  */
29#define JUMPBUF_CORE_REGS_SIZE (10 * 4)
30#define JUMPBUF_FP_REGS_SIZE (8 * 8)
31#define JUMPBUF_PAC (JUMPBUF_CORE_REGS_SIZE + JUMPBUF_FP_REGS_SIZE + 0)
32
33/* Arm/Thumb interworking support:
34
35   The interworking scheme expects functions to use a BX instruction
36   to return control to their parent.  Since we need this code to work
37   in both interworked and non-interworked environments as well as with
38   older processors which do not have the BX instruction we do the
39   following:
40	Test the return address.
41	If the bottom bit is clear perform an "old style" function exit.
42	(We know that we are in ARM mode and returning to an ARM mode caller).
43	Otherwise use the BX instruction to perform the function exit.
44
45   We know that we will never attempt to perform the BX instruction on
46   an older processor, because that kind of processor will never be
47   interworked, and a return address with the bottom bit set will never
48   be generated.
49
50   In addition, we do not actually assemble the BX instruction as this would
51   require us to tell the assembler that the processor is an ARM7TDMI and
52   it would store this information in the binary.  We want this binary to be
53   able to be linked with binaries compiled for older processors however, so
54   we do not want such information stored there.
55
56   If we are running using the APCS-26 convention however, then we never
57   test the bottom bit, because this is part of the processor status.
58   Instead we just do a normal return, since we know that we cannot be
59   returning to a Thumb caller - the Thumb does not support APCS-26.
60
61   Function entry is much simpler.  If we are compiling for the Thumb we
62   just switch into ARM mode and then drop through into the rest of the
63   function.  The function exit code will take care of the restore to
64   Thumb mode.
65
66   For Thumb-2 do everything in Thumb mode.  */
67
68	.syntax unified
69
70/*  GCC 12.1 and later and clang will tell the assembler exactly which
71    floating point (or MVE) unit is required and we don't want to
72    override that.  Conversely, older versions of the compiler don't
73    pass this information so we need to enable the VFP version that is
74    most appropriate.  The choice here should support all suitable VFP
75    versions that the older toolchains can handle.  */
76#if __GNUC__ && __GNUC__ < 12 && !defined(__clang__)
77/*  Ensure that FPU instructions are correctly compiled and, likewise,
78    the appropriate build attributes are added to the resulting object
79    file.  Check whether the MVE extension is present and whether
80    we have support for hardware floating point-operations.  VFPxd
81    covers all the cases we need in this file for hardware
82    floating-point and should be compatible with all required FPUs
83    that we need to support.  */
84# if __ARM_FP
85	.fpu vfpxd
86# endif
87# if __ARM_FEATURE_MVE
88	.arch_extension mve
89# endif
90#endif
91
92#if __ARM_ARCH_ISA_THUMB == 1 && !__ARM_ARCH_ISA_ARM
93/* ARMv6-M-like has to be implemented in Thumb mode.  */
94
95.thumb
96.thumb_func
97	.globl SYM (setjmp)
98	TYPE (setjmp)
99SYM (setjmp):
100	/* Save registers in jump buffer.  */
101	stmia	r0!, {r4, r5, r6, r7}
102	mov	r1, r8
103	mov	r2, r9
104	mov	r3, r10
105	mov	r4, fp
106	mov	r5, sp
107	mov	r6, lr
108	stmia	r0!, {r1, r2, r3, r4, r5, r6}
109	subs	r0, r0, #40
110	/* Restore callee-saved low regs.  */
111	ldmia	r0!, {r4, r5, r6, r7}
112	/* Return zero.  */
113	movs	r0, #0
114	bx lr
115
116.thumb_func
117	.globl SYM (longjmp)
118	TYPE (longjmp)
119SYM (longjmp):
120	/* Restore High regs.  */
121	adds	r0, r0, #16
122	ldmia	r0!, {r2, r3, r4, r5, r6}
123	mov	r8, r2
124	mov	r9, r3
125	mov	r10, r4
126	mov	fp, r5
127	mov	sp, r6
128	ldmia	r0!, {r3} /* lr */
129	/* Restore low regs.  */
130	subs	r0, r0, #40
131	ldmia	r0!, {r4, r5, r6, r7}
132	/* Return the result argument, or 1 if it is zero.  */
133	movs	r0, r1
134	bne	1f
135	movs	r0, #1
1361:
137	bx	r3
138
139#else
140
141#ifdef __APCS_26__
142#define RET	movs		pc, lr
143#elif defined(__thumb2__)
144#define RET	bx lr
145#else
146#define RET	tst		lr, #1; \
147	        moveq		pc, lr ; \
148.inst           0xe12fff1e	/* bx lr */
149#endif
150
151#ifdef __thumb2__
152.macro COND where when
153	i\where	\when
154.endm
155#else
156.macro COND where when
157.endm
158#endif
159
160#if defined(__thumb2__)
161.macro MODE
162	.thumb
163	.thumb_func
164.endm
165.macro PROLOGUE name
166.endm
167
168#elif defined(__thumb__)
169#define	MODE		.thumb_func
170.macro PROLOGUE name
171	.code 16
172	bx	pc
173	nop
174	.code 32
175SYM (.arm_start_of.\name):
176.endm
177#else /* Arm */
178#define	MODE		.code 32
179.macro PROLOGUE name
180.endm
181#endif
182
183.macro FUNC_START name
184	.text
185	.align 2
186	MODE
187	.globl SYM (\name)
188	.cfi_sections .debug_frame
189	.cfi_startproc
190	TYPE (\name)
191SYM (\name):
192	PROLOGUE \name
193.endm
194
195.macro FUNC_END name
196	RET
197	.cfi_endproc
198	SIZE (\name)
199.endm
200
201/* --------------------------------------------------------------------
202                 int setjmp (jmp_buf);
203   -------------------------------------------------------------------- */
204
205	FUNC_START setjmp
206
207#if __ARM_FEATURE_PAC_DEFAULT
208# if __ARM_FEATURE_BTI_DEFAULT
209	pacbti	ip, lr, sp
210# else
211	pac	ip, lr, sp
212# endif /* __ARM_FEATURE_BTI_DEFAULT */
213	mov r3, ip
214	str r3, [r0, #JUMPBUF_PAC]
215	.cfi_register 143, 12
216#else
217# if __ARM_FEATURE_BTI_DEFAULT
218	bti
219# endif /* __ARM_FEATURE_BTI_DEFAULT */
220#endif /* __ARM_FEATURE_PAC_DEFAULT */
221
222	/* Save all the callee-preserved registers into the jump buffer.  */
223#ifdef __thumb2__
224	mov		ip, sp
225	stmia		r0!, { r4-r10, fp, ip, lr }
226#else
227	stmia		r0!, { r4-r10, fp, sp, lr }
228#endif
229#if defined __ARM_FP || defined __ARM_FEATURE_MVE
230	vstm		r0, { d8-d15 }
231#endif
232
233	/* When setting up the jump buffer return 0.  */
234	mov		r0, #0
235#if __ARM_FEATURE_PAC_DEFAULT
236	mov ip, r3
237	aut ip, lr, sp
238#endif /* __ARM_FEATURE_PAC_DEFAULT */
239
240	FUNC_END setjmp
241
242/* --------------------------------------------------------------------
243		volatile void longjmp (jmp_buf, int);
244   -------------------------------------------------------------------- */
245
246	FUNC_START longjmp
247
248#if __ARM_FEATURE_BTI_DEFAULT
249	bti
250#endif /* __ARM_FEATURE_BTI_DEFAULT */
251
252#if __ARM_FEATURE_PAC_DEFAULT
253	/* Keep original jmpbuf address for retrieving pac-code
254	   for authentication.  */
255	mov	r2, r0
256#endif /* __ARM_FEATURE_PAC_DEFAULT */
257
258	/* If we have stack extension code it ought to be handled here.  */
259
260	/* Restore the registers, retrieving the state when setjmp() was called.  */
261#ifdef __thumb2__
262	ldmia		r0!, { r4-r10, fp, ip, lr }
263	mov		sp, ip
264#else
265	ldmia		r0!, { r4-r10, fp, sp, lr }
266#endif
267#if defined __ARM_FP || defined __ARM_FEATURE_MVE
268	vldm		r0, { d8-d15 }
269#endif
270
271	/* Put the return value into the integer result register.
272	   But if it is zero then return 1 instead.  */
273	movs		r0, r1
274	it		eq
275	moveq		r0, #1
276
277#if __ARM_FEATURE_PAC_DEFAULT
278	ldr ip, [r2, #JUMPBUF_PAC]
279	aut ip, lr, sp
280#endif /* __ARM_FEATURE_PAC_DEFAULT */
281
282	FUNC_END longjmp
283#endif
284