1/*
2 * Copyright (c) 2016 Cadence Design Systems, Inc.
3 * Copyright (c) 2022 Intel Corporation
4 * SPDX-License-Identifier: Apache-2.0
5 */
6#include <xtensa/coreasm.h>
7#include <zephyr/zsr.h>
8
9/* WINDOW OVERFLOW AND UNDERFLOW EXCEPTION VECTORS AND ALLOCA EXCEPTION
10 * HANDLER
11 *
12 * Here is the code for each window overflow/underflow exception vector and
13 * (interspersed) efficient code for handling the alloca exception cause.
14 * Window exceptions are handled entirely in the vector area and are very tight
15 * for performance. The alloca exception is also handled entirely in the window
16 * vector area so comes at essentially no cost in code size. Users should never
17 * need to modify them and Cadence Design Systems recommends they do not.
18 *
19 * Window handlers go at predetermined vector locations according to the Xtensa
20 * hardware configuration, which is ensured by their placement in a special
21 * section known to the Xtensa linker support package (LSP). Since their
22 * offsets in that section are always the same, the LSPs do not define a
23 * section per vector.
24 *
25 * These things are coded for XEA2 only (XEA1 is not supported).
26 *
27 * Note on Underflow Handlers:
28 *
29 * The underflow handler for returning from call[i+1] to call[i] must preserve
30 * all the registers from call[i+1]'s window. In particular, a0 and a1 must be
31 * preserved because the RETW instruction will be reexecuted (and may even
32 * underflow if an intervening exception has flushed call[i]'s registers).
33 * Registers a2 and up may contain return values.
34 */
35
36#if XCHAL_HAVE_WINDOWED
37
38    .section .WindowVectors.text, "ax"
39
40/* Window Overflow Exception for Call4.
41 *
42 * Invoked if a call[i] referenced a register (a4-a15)
43 * that contains data from ancestor call[j];
44 * call[j] had done a call4 to call[j+1].
45 * On entry here:
46 *	window rotated to call[j] start point;
47 *	    a0-a3 are registers to be saved;
48 *	    a4-a15 must be preserved;
49 *	    a5 is call[j+1]'s stack pointer.
50 */
51
52    .org    0x0
53    .global _WindowOverflow4
54_WindowOverflow4:
55
56    s32e    a0, a5, -16     /* save a0 to call[j+1]'s stack frame */
57    s32e    a1, a5, -12     /* save a1 to call[j+1]'s stack frame */
58    s32e    a2, a5,  -8     /* save a2 to call[j+1]'s stack frame */
59    s32e    a3, a5,  -4     /* save a3 to call[j+1]'s stack frame */
60    rfwo                    /* rotates back to call[i] position */
61
62/* Window Underflow Exception for Call4
63 *
64 * Invoked by RETW returning from call[i+1] to call[i]
65 * where call[i]'s registers must be reloaded (not live in ARs);
66 * where call[i] had done a call4 to call[i+1].
67 * On entry here:
68 * 	    window rotated to call[i] start point;
69 * 	    a0-a3 are undefined, must be reloaded with call[i].reg[0..3];
70 * 	    a4-a15 must be preserved (they are call[i+1].reg[0..11]);
71 * 	    a5 is call[i+1]'s stack pointer.
72 */
73
74    .org    0x40
75    .global _WindowUnderflow4
76_WindowUnderflow4:
77
78    l32e    a0, a5, -16     /* restore a0 from call[i+1]'s stack frame */
79    l32e    a1, a5, -12     /* restore a1 from call[i+1]'s stack frame */
80    l32e    a2, a5,  -8     /* restore a2 from call[i+1]'s stack frame */
81    l32e    a3, a5,  -4     /* restore a3 from call[i+1]'s stack frame */
82    rfwu
83
84/* Handle alloca exception generated by interruptee executing 'movsp'.
85 * This uses space between the window vectors, so is essentially
86 * "free".  All interruptee's regs are intact except a0 which is saved
87 * in $ZSR_A0SAVE (assigned at build time, see gen_zsr.py for
88 * details), and PS.EXCM has been set by the exception hardware (can't
89 * be interrupted).  The fact the alloca exception was taken means the
90 * registers associated with the base-save area have been spilled and
91 * will be restored by the underflow handler, so those 4 registers are
92 * available for scratch.  The code is optimized to avoid unaligned
93 * branches and minimize cache misses.
94 */
95
96    .align  4
97    .global _xt_alloca_exc
98_xt_alloca_exc:
99
100    rsr     a0, WINDOWBASE  /* grab WINDOWBASE before rotw changes it */
101    rotw    -1      /* WINDOWBASE goes to a4, new a0-a3 are scratch */
102    rsr     a2, PS
103    extui   a3, a2, XCHAL_PS_OWB_SHIFT, XCHAL_PS_OWB_BITS
104    xor     a3, a3, a4    /* bits changed from old to current windowbase */
105    rsr     a4, ZSR_A0SAVE  /* restore original a0 (now in a4) */
106    slli    a3, a3, XCHAL_PS_OWB_SHIFT
107    xor     a2, a2, a3      /* flip changed bits in old window base */
108    wsr     a2, PS          /* update PS.OWB to new window base */
109    rsync
110
111    _bbci.l a4, 31, _WindowUnderflow4
112    rotw    -1              /* original a0 goes to a8 */
113    _bbci.l a8, 30, _WindowUnderflow8
114    rotw    -1
115    j               _WindowUnderflow12
116
117/* Window Overflow Exception for Call8
118 *
119 * Invoked if a call[i] referenced a register (a4-a15)
120 * that contains data from ancestor call[j];
121 * call[j] had done a call8 to call[j+1].
122 * On entry here:
123 * 	window rotated to call[j] start point;
124 * 	    a0-a7 are registers to be saved;
125 * 	    a8-a15 must be preserved;
126 * 	    a9 is call[j+1]'s stack pointer.
127 */
128
129    .org    0x80
130    .global _WindowOverflow8
131_WindowOverflow8:
132
133    s32e    a0, a9, -16     /* save a0 to call[j+1]'s stack frame */
134    l32e    a0, a1, -12     /* a0 <- call[j-1]'s sp
135                               (used to find end of call[j]'s frame) */
136    s32e    a1, a9, -12     /* save a1 to call[j+1]'s stack frame */
137    s32e    a2, a9,  -8     /* save a2 to call[j+1]'s stack frame */
138    s32e    a3, a9,  -4     /* save a3 to call[j+1]'s stack frame */
139    s32e    a4, a0, -32     /* save a4 to call[j]'s stack frame */
140    s32e    a5, a0, -28     /* save a5 to call[j]'s stack frame */
141    s32e    a6, a0, -24     /* save a6 to call[j]'s stack frame */
142    s32e    a7, a0, -20     /* save a7 to call[j]'s stack frame */
143    rfwo                    /* rotates back to call[i] position */
144
145/*
146 * Window Underflow Exception for Call8
147 *
148 * Invoked by RETW returning from call[i+1] to call[i]
149 * where call[i]'s registers must be reloaded (not live in ARs);
150 * where call[i] had done a call8 to call[i+1].
151 * On entry here:
152 * 	    window rotated to call[i] start point;
153 * 	    a0-a7 are undefined, must be reloaded with call[i].reg[0..7];
154 * 	    a8-a15 must be preserved (they are call[i+1].reg[0..7]);
155 * 	    a9 is call[i+1]'s stack pointer.
156 */
157
158    .org    0xC0
159    .global _WindowUnderflow8
160_WindowUnderflow8:
161
162    l32e    a0, a9, -16     /* restore a0 from call[i+1]'s stack frame */
163    l32e    a1, a9, -12     /* restore a1 from call[i+1]'s stack frame */
164    l32e    a2, a9,  -8     /* restore a2 from call[i+1]'s stack frame */
165    l32e    a7, a1, -12     /* a7 <- call[i-1]'s sp
166                               (used to find end of call[i]'s frame) */
167    l32e    a3, a9,  -4     /* restore a3 from call[i+1]'s stack frame */
168    l32e    a4, a7, -32     /* restore a4 from call[i]'s stack frame */
169    l32e    a5, a7, -28     /* restore a5 from call[i]'s stack frame */
170    l32e    a6, a7, -24     /* restore a6 from call[i]'s stack frame */
171    l32e    a7, a7, -20     /* restore a7 from call[i]'s stack frame */
172    rfwu
173
174/*
175 * Window Overflow Exception for Call12
176 *
177 * Invoked if a call[i] referenced a register (a4-a15)
178 * that contains data from ancestor call[j];
179 * call[j] had done a call12 to call[j+1].
180 * On entry here:
181 * 	window rotated to call[j] start point;
182 * 	    a0-a11 are registers to be saved;
183 * 	    a12-a15 must be preserved;
184 * 	    a13 is call[j+1]'s stack pointer.
185 */
186
187    .org    0x100
188    .global _WindowOverflow12
189_WindowOverflow12:
190
191    s32e    a0,  a13, -16   /* save a0 to call[j+1]'s stack frame */
192    l32e    a0,  a1,  -12   /* a0 <- call[j-1]'s sp
193                               (used to find end of call[j]'s frame) */
194    s32e    a1,  a13, -12   /* save a1 to call[j+1]'s stack frame */
195    s32e    a2,  a13,  -8   /* save a2 to call[j+1]'s stack frame */
196    s32e    a3,  a13,  -4   /* save a3 to call[j+1]'s stack frame */
197    s32e    a4,  a0,  -48   /* save a4 to end of call[j]'s stack frame */
198    s32e    a5,  a0,  -44   /* save a5 to end of call[j]'s stack frame */
199    s32e    a6,  a0,  -40   /* save a6 to end of call[j]'s stack frame */
200    s32e    a7,  a0,  -36   /* save a7 to end of call[j]'s stack frame */
201    s32e    a8,  a0,  -32   /* save a8 to end of call[j]'s stack frame */
202    s32e    a9,  a0,  -28   /* save a9 to end of call[j]'s stack frame */
203    s32e    a10, a0,  -24   /* save a10 to end of call[j]'s stack frame */
204    s32e    a11, a0,  -20   /* save a11 to end of call[j]'s stack frame */
205    rfwo                    /* rotates back to call[i] position */
206
207/*
208 * Window Underflow Exception for Call12
209 *
210 * Invoked by RETW returning from call[i+1] to call[i]
211 * where call[i]'s registers must be reloaded (not live in ARs);
212 * where call[i] had done a call12 to call[i+1].
213 * On entry here:
214 *	window rotated to call[i] start point;
215 *	a0-a11 are undefined, must be reloaded with call[i].reg[0..11];
216 *	a12-a15 must be preserved (they are call[i+1].reg[0..3]);
217 *	a13 is call[i+1]'s stack pointer.
218 */
219
220    .org 0x140
221    .global _WindowUnderflow12
222_WindowUnderflow12:
223
224    l32e    a0,  a13, -16 /* restore a0 from call[i+1]'s stack frame */
225    l32e    a1,  a13, -12 /* restore a1 from call[i+1]'s stack frame */
226    l32e    a2,  a13,  -8 /* restore a2 from call[i+1]'s stack frame */
227    l32e    a11, a1,  -12 /* a11 <- call[i-1]'s sp
228                           * (used to find end of call[i]'s frame) */
229    l32e    a3,  a13,  -4 /* restore a3 from call[i+1]'s stack frame */
230    l32e    a4,  a11, -48 /* restore a4 from end of call[i]'s stack frame */
231    l32e    a5,  a11, -44 /* restore a5 from end of call[i]'s stack frame */
232    l32e    a6,  a11, -40 /* restore a6 from end of call[i]'s stack frame */
233    l32e    a7,  a11, -36 /* restore a7 from end of call[i]'s stack frame */
234    l32e    a8,  a11, -32 /* restore a8 from end of call[i]'s stack frame */
235    l32e    a9,  a11, -28 /* restore a9 from end of call[i]'s stack frame */
236    l32e    a10, a11, -24 /* restore a10 from end of call[i]'s stack
237    		       * frame */
238    l32e    a11, a11, -20   /* restore a11 from end of call[i]'s stack
239    			 * frame */
240    rfwu
241
242#endif /* XCHAL_HAVE_WINDOWED */
243
244