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