1/* setjmp/longjmp functions for Xtensa.
2
3   Copyright (c) 2001-2006 by Tensilica Inc.
4
5   Permission is hereby granted, free of charge, to any person obtaining
6   a copy of this software and associated documentation files (the
7   "Software"), to deal in the Software without restriction, including
8   without limitation the rights to use, copy, modify, merge, publish,
9   distribute, sublicense, and/or sell copies of the Software, and to
10   permit persons to whom the Software is furnished to do so, subject to
11   the following conditions:
12
13   The above copyright notice and this permission notice shall be included
14   in all copies or substantial portions of the Software.
15
16   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
23
24/* Windowed ABI:
25
26   This implementation relies heavily on the Xtensa register window
27   mechanism.  Setjmp flushes all the windows except its own to the
28   stack and then copies registers from the save areas on the stack
29   into the jmp_buf structure, along with the return address of the call
30   to setjmp.  Longjmp invalidates all the windows except its own, and
31   then sets things up so that it will return to the right place,
32   using a window underflow to automatically restore the registers.
33
34   Note that it would probably be sufficient to only copy the
35   registers from setjmp's caller into jmp_buf.  However, we also copy
36   the save area located at the stack pointer of setjmp's caller.
37   This save area will typically remain intact until the longjmp call.
38   The one exception is when there is an intervening alloca in
39   setjmp's caller.  This is certainly an unusual situation and is
40   likely to cause problems in any case (the storage allocated on the
41   stack cannot be safely accessed following the longjmp).  As bad as
42   it is, on most systems this situation would not necessarily lead to
43   a catastrophic failure.  If we did not preserve the extra save area
44   on Xtensa, however, it would.  When setjmp's caller returns after a
45   longjmp, there will be a window underflow; an invalid return
46   address or stack pointer in the save area will almost certainly
47   lead to a crash.  Keeping a copy of the extra save area in the
48   jmp_buf avoids this with only a small additional cost.  If setjmp
49   and longjmp are ever time-critical, this could be removed.
50
51
52   Call0 ABI:
53
54   Much like other ABIs, this version just saves the necessary registers
55   to the stack and restores them later.  Much less needs to be done.  */
56
57#include <picolibc.h>
58
59#include "xtensa-asm.h"
60
61#define SYS_nop	0
62
63
64#if XCHAL_HAVE_WINDOWED && !__XTENSA_CALL0_ABI__
65
66/* int setjmp (jmp_buf env) */
67
68	.text
69	.align	4
70	.literal_position
71	.global	setjmp
72	.type	setjmp, @function
73setjmp:
74	entry	sp, 16
75
76	/* Flush registers.  */
77	mov	a4, a2			// save a2 (jmp_buf)
78	movi	a2, SYS_nop
79	syscall
80	mov	a2, a4			// restore a2
81
82	/* Copy the register save area at (sp - 16).  */
83	addi	a5, a1, -16
84	l32i	a3, a5, 0
85	l32i	a4, a5, 4
86	s32i	a3, a2, 0
87	s32i	a4, a2, 4
88	l32i	a3, a5, 8
89	l32i	a4, a5, 12
90	s32i	a3, a2, 8
91	s32i	a4, a2, 12
92
93	/* Copy 0-8 words from the register overflow area.  */
94	extui	a3, a0, 30, 2
95	blti	a3, 2, .Lendsj
96	l32i	a7, a1, 4
97	slli	a4, a3, 4
98	sub	a5, a7, a4
99	addi	a6, a2, 16
100	addi	a7, a7, -16		// a7 = end of register overflow area
101.Lsjloop:
102	l32i	a3, a5, 0
103	l32i	a4, a5, 4
104	s32i	a3, a6, 0
105	s32i	a4, a6, 4
106	l32i	a3, a5, 8
107	l32i	a4, a5, 12
108	s32i	a3, a6, 8
109	s32i	a4, a6, 12
110	addi	a5, a5, 16
111	addi	a6, a6, 16
112	blt	a5, a7, .Lsjloop
113.Lendsj:
114
115	/* Copy the register save area at sp.  */
116	l32i	a3, a1, 0
117	l32i	a4, a1, 4
118	s32i	a3, a2, 48
119	s32i	a4, a2, 52
120	l32i	a3, a1, 8
121	l32i	a4, a1, 12
122	s32i	a3, a2, 56
123	s32i	a4, a2, 60
124
125	/* Save the return address, including the window size bits.  */
126	s32i	a0, a2, 64
127
128	movi	a2, 0
129	retw
130	.size	setjmp, . - setjmp
131
132
133/* void longjmp (jmp_buf env, int val) */
134
135	.align	4
136	.literal_position
137	.global	longjmp
138	.type	longjmp, @function
139longjmp:
140	entry	sp, 16
141	/*  a2 == &env, a3 == val  */
142
143#if XCHAL_MAYHAVE_ERRATUM_XEA1KWIN
144  /* Using this register triggers early any overflow that a kernel-mode
145     level-one interrupt might otherwise cause.  */
146# define AR_WB	a15
147#else
148  /* Using this register is more efficient; it triggers less overflows.  */
149# define AR_WB	a5
150#endif
151	/* Invalidate all but the current window;
152	   set WindowStart to (1 << WindowBase).  */
153	rsr	AR_WB, WINDOWBASE
154	movi	a4, 1
155	ssl	AR_WB
156	sll	a4, a4
157	wsr	a4, WINDOWSTART
158	rsync
159
160	/* Return to the return address of the setjmp, using the
161	   window size bits from the setjmp call so that the caller
162	   will be able to find the return value that we put in a2.  */
163
164	l32i	a0, a2, 64
165
166	/* Copy the first 4 saved registers from jmp_buf into the save area
167	   at the current sp so that the values will be restored to registers
168	   when longjmp returns.  */
169
170	addi	a7, a1, -16
171	l32i	a4, a2, 0
172	l32i	a5, a2, 4
173	s32i	a4, a7, 0
174	s32i	a5, a7, 4
175	l32i	a4, a2, 8
176	l32i	a5, a2, 12
177	s32i	a4, a7, 8
178	s32i	a5, a7, 12
179
180	/* Copy the remaining 0-8 saved registers.  */
181	extui	a7, a0, 30, 2
182	blti	a7, 2, .Lendlj
183	l32i	a8, a2, 52
184	slli	a4, a7, 4
185	sub	a6, a8, a4
186	addi	a5, a2, 16
187	addi	a8, a8, -16		// a8 = end of register overflow area
188.Lljloop:
189	l32i	a7, a5, 0
190	l32i	a4, a5, 4
191	s32i	a7, a6, 0
192	s32i	a4, a6, 4
193	l32i	a7, a5, 8
194	l32i	a4, a5, 12
195	s32i	a7, a6, 8
196	s32i	a4, a6, 12
197	addi	a5, a5, 16
198	addi	a6, a6, 16
199	blt	a6, a8, .Lljloop
200.Lendlj:
201
202	/* The 4 words saved from the register save area at the target's
203	   sp are copied back to the target procedure's save area.  The
204	   only point of this is to prevent a catastrophic failure in
205	   case the contents were moved by an alloca after calling
206	   setjmp.  This is a bit paranoid but it doesn't cost much.  */
207
208	l32i	a7, a2, 4		// load the target stack pointer
209	addi	a7, a7, -16		// find the destination save area
210	l32i	a4, a2, 48
211	l32i	a5, a2, 52
212	s32i	a4, a7, 0
213	s32i	a5, a7, 4
214	l32i	a4, a2, 56
215	l32i	a5, a2, 60
216	s32i	a4, a7, 8
217	s32i	a5, a7, 12
218
219	/* Return val ? val : 1.  */
220	movi	a2, 1
221	movnez	a2, a3, a3
222
223	retw
224	.size	longjmp, . - longjmp
225
226#else /* CALL0 ABI */
227
228	.text
229	.align	4
230	.literal_position
231	.global	setjmp
232	.type	setjmp, @function
233setjmp:
234	s32i	a0, a2, 0
235	s32i	a1, a2, 4
236	s32i	a12, a2, 8
237	s32i	a13, a2, 12
238	s32i	a14, a2, 16
239	s32i	a15, a2, 20
240	movi	a2, 0
241	ret
242	.size	setjmp, . - setjmp
243
244	.align	4
245	.literal_position
246	.global	longjmp
247	.type	longjmp, @function
248longjmp:
249	l32i	a0, a2, 0
250	l32i	a12, a2, 8
251	l32i	a13, a2, 12
252	l32i	a14, a2, 16
253	l32i	a15, a2, 20
254	l32i	a1, a2, 4
255	/* Return val ? val : 1.  */
256	movi	a2, 1
257	movnez	a2, a3, a3
258
259	ret
260	.size	longjmp, .-longjmp
261
262#endif /* CALL0 ABI */
263