Setting up repository
[linux-libre-firmware.git] / ath9k_htc / sboot / magpie_1_1 / sboot / athos / src / xtos / exc-alloca-handler.S
1 // exc-alloca-handler.S - ALLOCA cause exception assembly-level handler
2 // $Id: //depot/rel/Cottonwood/Xtensa/OS/xtos/exc-alloca-handler.S#3 $
3
4 // Copyright (c) 2002-2010 Tensilica Inc.
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the
8 // "Software"), to deal in the Software without restriction, including
9 // without limitation the rights to use, copy, modify, merge, publish,
10 // distribute, sublicense, and/or sell copies of the Software, and to
11 // permit persons to whom the Software is furnished to do so, subject to
12 // the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be included
15 // in all copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25 /*
26  * Code written to the windowed ABI must use the MOVSP instruction to modify
27  * the stack pointer (except for startup code, which doesn't have a caller).
28  * The compiler uses MOVSP to allocate very large or variable size stack frames.
29  * MOVSP guarantees that the caller frame's a0-a3 registers, stored below the
30  * stack pointer, are moved atomically with respect to interrupts and exceptions
31  * to satisfy windowed ABI requirements.  When user code executes the MOVSP
32  * instruction and the caller frame is on the stack rather than in the register
33  * file, the processor takes an ALLOCA exception.  The ALLOCA exception handler
34  * moves the caller frame's a0-a3 registers to follow the stack pointer.
35  * This file implements this ALLOCA exception handler.
36  *
37  * Code written in C can generate a MOVSP in four situations:
38  *
39  * 1. By calling "alloca":
40  *
41  *   void foo(int array_size) {
42  *     char * bar = alloca(array_size);
43  *     ...
44  *
45  * 2. By using variable sized arrays (a GNU C extension):
46  *
47  *   void foo(int array_size) {
48  *     char bar[array_size];
49  *     ...
50  *
51  * 3. By using nested C functions (also a GNU C extension):
52  *
53  *   void afunction(void) {
54  *     ...
55  *     int anotherfunction(void) {
56  *     }
57  *     ...
58  *
59  * 4. By using very large amounts of stack space in a single function. The exact
60  *    limit is 32,760 bytes (including 16-48 bytes of caller frame overhead).
61  *    Typically, users don't encounter this limit unless they have functions
62  *    that locally declare large arrays, for example:
63  *
64  *   void foo(void) {
65  *     int an_array[8192];              // 32,768 bytes
66  *     int another_array[100];          // 400 bytes
67  *     ...
68  *
69  *
70  * NOTE:  This handler only works when MOVSP's destination register is the stack
71  * pointer "a1" (synonym with "sp"), i.e. "MOVSP a1, <as>".  This is the only
72  * meaningful form of MOVSP in the windowed ABI, and the only form generated
73  * by the compiler and used in assembly.  The code below does not check the
74  * destination register, so other forms of MOVSP cause unexpected behaviour.
75  */
76
77 #include <xtensa/coreasm.h>
78 #include <xtensa/config/specreg.h>
79 #include "xtos-internal.h"
80
81 #define ERROR_CHECKING  1       // define as 0 to save a few bytes
82
83
84 #if XCHAL_HAVE_EXCEPTIONS
85
86 //Vector:
87 //      addi    a1, a1, -ESF_TOTALSIZE  // allocate exception stack frame, etc.
88 //      s32i    a2, a1, UEXC_a2
89 //      s32i    a3, a1, UEXC_a3
90 //      movi    a3, _xtos_exc_handler_table
91 //      rsr     a2, EXCCAUSE
92 //      addx4   a2, a2, a3
93 //      l32i    a2, a2, 0
94 //      s32i    a4, a1, UEXC_a4
95 //      jx      a2              // jump to cause-specific handler
96
97         .global _need_user_vector_      // pull-in real user vector (tiny LSP)
98
99         .text
100         .align  4
101         .global _xtos_alloca_handler
102 _xtos_alloca_handler:
103 #if !XCHAL_HAVE_WINDOWED || defined(__XTENSA_CALL0_ABI__)
104         rfe_rfue
105 #else /* we have windows w/o call0 abi */
106         //  HERE:  a2, a3, a4 have been saved to
107         //  exception stack frame allocated with a1 (sp).
108         //  a2 contains EXCCAUSE.
109         //  (12 cycles from vector to here, assuming cache hits, 5-stage pipe, etc)
110
111         /*
112          *  Skip the MOVSP instruction so we don't execute it again on return:
113          */
114
115         rsr     a3, EPC_1               // load instruction address (PC)
116         s32i    a5, a1, UEXC_a5         // save a5
117         addi    a2, a3, 3               // increment PC to skip MOVSP instruction
118 #if XCHAL_HAVE_LOOPS
119         /*
120          *  If the MOVSP instruction is the last instruction in the body of
121          *  a zero-overhead loop that must be executed again, then decrement
122          *  the loop count and resume execution at the head of the loop.
123          */
124         rsr     a4, LEND
125         rsr     a5, LCOUNT
126         bne     a4, a2, 1f              // done unless next-PC matches LEND
127         beqz    a5, 1f                  // if LCOUNT zero, not in loop
128         addi    a5, a5, -1              // z.o. loopback! decrement LCOUNT...
129         wsr     a5, LCOUNT
130         rsr     a2, LBEG                // PC back to start of loop
131 #endif /*XCHAL_HAVE_LOOPS*/
132 1:      wsr     a2, EPC_1               // update return PC past MOVSP
133
134         /*
135          *  Figure out what register MOVSP is moving from ('s' field, 2nd byte).
136          *  If MOVSP is in an instruction RAM or ROM, we can only access it with
137          *  32-bit loads.  So use shifts to read the byte from a 32-bit load.
138          */
139
140         addi    a3, a3, 1               // advance to byte containing 's' field
141         extui   a2, a3, 0, 2            // get bits 0 and 1 of address of this byte
142         sub     a3, a3, a2              // put address on 32-bit boundary
143         l32i    a3, a3, 0               // get word containing byte (can't use l8ui on IRAM/IROM)
144         rsr     a4, SAR                 // save SAR
145         //  NOTE: possible addition here: verify destination register is indeed a1.
146 # if XCHAL_HAVE_BE
147         ssa8b   a2
148         sll     a3, a3
149         extui   a3, a3, 28, 4           // extract source register number
150 # else
151         ssa8l   a2
152         srl     a3, a3
153         extui   a3, a3, 0, 4            // extract source register number
154 # endif
155         wsr     a4, SAR                 // restore SAR
156         //  (+?? cycles max above = ?? cycles, assuming cache hits, 5-stage pipe, no zoloops, etc)
157
158         movi    a4, .Ljmptable          // jump table
159         mov     a5, a1                  // save the exception stack frame ptr in a5
160         addi    a1, a1, ESF_TOTALSIZE   // restore a1 (in case of MOVSP a1,a1)
161
162 # if XCHAL_HAVE_DENSITY
163         addx4   a4, a3, a4              // index by src reg number * 4
164 #  define ALIGN .align 4                // 4-byte jmptable entries
165 #  define MOV   _mov.n
166 #  define L32I  _l32i.n
167 #  define DONE  _bnez.n a4, .Lmove_save_area    // a4 known non-zero
168 # else
169         addx8   a4, a3, a4              // index by src reg number * 8
170 #  define ALIGN .align 8                // 8-byte jmptable entries
171 #  define MOV   mov
172 #  define L32I  l32i
173 #  define DONE  j .Lmove_save_area
174 # endif
175
176         jx      a4                      // jump into the following table
177
178         ALIGN
179 .Ljmptable:     MOV     a1, a0          ; DONE  // MOVSP a1, a0
180         ALIGN                           ; DONE  // MOVSP a1, a1
181         ALIGN ; L32I    a1, a5, UEXC_a2 ; DONE  // MOVSP a1, a2
182         ALIGN ; L32I    a1, a5, UEXC_a3 ; DONE  // MOVSP a1, a3
183         ALIGN ; L32I    a1, a5, UEXC_a4 ; DONE  // MOVSP a1, a4
184         ALIGN ; L32I    a1, a5, UEXC_a5 ; DONE  // MOVSP a1, a5
185         ALIGN ; MOV     a1, a6          ; DONE  // MOVSP a1, a6
186         ALIGN ; MOV     a1, a7          ; DONE  // MOVSP a1, a7
187         ALIGN ; MOV     a1, a8          ; DONE  // MOVSP a1, a8
188         ALIGN ; MOV     a1, a9          ; DONE  // MOVSP a1, a9
189         ALIGN ; MOV     a1, a10         ; DONE  // MOVSP a1, a10
190         ALIGN ; MOV     a1, a11         ; DONE  // MOVSP a1, a11
191         ALIGN ; MOV     a1, a12         ; DONE  // MOVSP a1, a12
192         ALIGN ; MOV     a1, a13         ; DONE  // MOVSP a1, a13
193         ALIGN ; MOV     a1, a14         ; DONE  // MOVSP a1, a14
194         ALIGN ; MOV     a1, a15                 // MOVSP a1, a15
195
196 .Lmove_save_area:
197         //  Okay.  a1 now contains the new SP value.
198
199 # if ERROR_CHECKING
200         //  Verify it is sensible:
201         extui   a3, a1, 0, 2            // verify that new SP is 4-byte aligned
202         beqz    a3, 1f                  // if so, skip fixup
203
204 //      .global _xtos_misaligned_movsp  // make label visible for debugging
205 //_xtos_misaligned_movsp:
206 #  if XCHAL_HAVE_DEBUG
207         break   1, 15                   // break into debugger (if any)
208 #  endif
209         sub     a1, a1, a3              // FORCE alignment of the new pointer (!)
210 1:
211 # endif
212
213 # if XCHAL_HAVE_XEA2
214         addi    a2, a5, ESF_TOTALSIZE           // compute a2 = old SP
215 # else /*XEA1:*/
216         addi    a2, a5, ESF_TOTALSIZE-16        // compute a2 = old SP's save area
217 # endif
218         //  Does new SP (in a1) overlap with exception stack frame (in a5)?:
219         movi    a4, ESF_TOTALSIZE       // size of exception stack frame
220         sub     a3, a1, a5              // distance from ESF ptr to new SP
221         bgeu    a3, a4, 1f              // does new SP overlap ESF? branch if not
222         //  Move ESF down so it doesn't overlap with the new register save area:
223         //  (a1 = current ESF, a2 = new SP, a4 = ESF_TOTALSIZE)
224         sub     a5, a5, a4              // shift down ESF (by ESF size)
225         l32i    a3, a5, UEXC_a2+ESF_TOTALSIZE
226         l32i    a4, a5, UEXC_a3+ESF_TOTALSIZE
227         s32i    a3, a5, UEXC_a2
228         s32i    a4, a5, UEXC_a3
229         l32i    a3, a5, UEXC_a4+ESF_TOTALSIZE
230         l32i    a4, a5, UEXC_a5+ESF_TOTALSIZE
231         s32i    a3, a5, UEXC_a4
232         s32i    a4, a5, UEXC_a5
233 1:
234
235         //  Move the register save area (from old SP to new SP):
236 # if XCHAL_HAVE_XEA2
237         l32e    a3, a2, -16
238         l32e    a4, a2, -12
239         s32e    a3, a1, -16
240         s32e    a4, a1, -12
241         l32e    a3, a2, -8
242         l32e    a4, a2, -4
243         s32e    a3, a1, -8
244         s32e    a4, a1, -4
245 # else /*XEA1:*/
246         addi    a1, a1, -16             // point to new save area
247         l32i    a3, a2, 0
248         l32i    a4, a2, 4
249         s32i    a3, a1, 0
250         s32i    a4, a1, 4
251         l32i    a3, a2, 8
252         l32i    a4, a2, 12
253         s32i    a3, a1, 8
254         s32i    a4, a1, 12
255         addi    a1, a1, 16              // back to correct new SP
256 # endif /*XEA1*/
257         //  (+?? cycles max above = ?? cycles, assuming cache hits, 5-stage pipe, etc)
258
259         //  Restore a2, a3, a4, a5, and return:
260         l32i    a2, a5, UEXC_a2
261         l32i    a3, a5, UEXC_a3
262         l32i    a4, a5, UEXC_a4
263         l32i    a5, a5, UEXC_a5
264         rfe_rfue
265         //  (+?? cycles max above = ?? cycles, assuming cache hits, 5-stage pipe, etc)
266
267
268 #endif /* !XCHAL_HAVE_WINDOWED || __XTENSA_CALL0_ABI */
269
270         .size   _xtos_alloca_handler, . - _xtos_alloca_handler
271
272 #endif /* XCHAL_HAVE_EXCEPTIONS */
273