| 1 | /*
|
| 2 | * arch/xtensa/kernel/vectors.S
|
| 3 | *
|
| 4 | * This file contains all exception vectors (user, kernel, and double),
|
| 5 | * as well as the window vectors (overflow and underflow), and the debug
|
| 6 | * vector. These are the primary vectors executed by the processor if an
|
| 7 | * exception occurs.
|
| 8 | *
|
| 9 | * This file is subject to the terms and conditions of the GNU General
|
| 10 | * Public License. See the file "COPYING" in the main directory of
|
| 11 | * this archive for more details.
|
| 12 | *
|
| 13 | * Copyright (C) 2005 - 2008 Tensilica, Inc.
|
| 14 | *
|
| 15 | * Chris Zankel <chris@zankel.net>
|
| 16 | *
|
| 17 | */
|
| 18 |
|
| 19 | /*
|
| 20 | * We use a two-level table approach. The user and kernel exception vectors
|
| 21 | * use a first-level dispatch table to dispatch the exception to a registered
|
| 22 | * fast handler or the default handler, if no fast handler was registered.
|
| 23 | * The default handler sets up a C-stack and dispatches the exception to a
|
| 24 | * registerd C handler in the second-level dispatch table.
|
| 25 | *
|
| 26 | * Fast handler entry condition:
|
| 27 | *
|
| 28 | * a0: trashed, original value saved on stack (PT_AREG0)
|
| 29 | * a1: a1
|
| 30 | * a2: new stack pointer, original value in depc
|
| 31 | * a3: dispatch table
|
| 32 | * depc: a2, original value saved on stack (PT_DEPC)
|
| 33 | * excsave_1: a3
|
| 34 | *
|
| 35 | * The value for PT_DEPC saved to stack also functions as a boolean to
|
| 36 | * indicate that the exception is either a double or a regular exception:
|
| 37 | *
|
| 38 | * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception
|
| 39 | * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
|
| 40 | *
|
| 41 | * Note: Neither the kernel nor the user exception handler generate literals.
|
| 42 | *
|
| 43 | */
|
| 44 |
|
| 45 | |
| 46 | |
| 47 | |
| 48 | |
| 49 | |
| 50 | |
| 51 | |
| 52 | |
| 53 | |
| 54 | |
| 55 |
|
| 56 | |
| 57 |
|
| 58 |
|
| 59 | /*
|
| 60 | * User exception vector. (Exceptions with PS.UM == 1, PS.EXCM == 0)
|
| 61 | *
|
| 62 | * We get here when an exception occurred while we were in userland.
|
| 63 | * We switch to the kernel stack and jump to the first level handler
|
| 64 | * associated to the exception cause.
|
| 65 | *
|
| 66 | * Note: the saved kernel stack pointer (EXC_TABLE_KSTK) is already
|
| 67 | * decremented by PT_USER_SIZE.
|
| 68 | */
|
| 69 |
|
| 70 | .section .UserExceptionVector.text, "ax"
|
| 71 |
|
| 72 | ENTRY(_UserExceptionVector)
|
| 73 |
|
| 74 | xsr a3, excsave1 |
| 75 | wsr a2, depc |
| 76 | l32i a2, a3, EXC_TABLE_KSTK |
| 77 | s32i a0, a2, PT_AREG0 |
| 78 | rsr a0, exccause |
| 79 | s32i a0, a2, PT_DEPC |
| 80 | addx4 a0, a0, a3 |
| 81 | l32i a0, a0, EXC_TABLE_FAST_USER |
| 82 | xsr a3, excsave1 |
| 83 | jx a0
|
| 84 |
|
| 85 | ENDPROC(_UserExceptionVector)
|
| 86 |
|
| 87 | /*
|
| 88 | * Kernel exception vector. (Exceptions with PS.UM == 0, PS.EXCM == 0)
|
| 89 | *
|
| 90 | * We get this exception when we were already in kernel space.
|
| 91 | * We decrement the current stack pointer (kernel) by PT_KERNEL_SIZE and
|
| 92 | * jump to the first-level handler associated with the exception cause.
|
| 93 | *
|
| 94 | * Note: we need to preserve space for the spill region.
|
| 95 | */
|
| 96 |
|
| 97 | .section .KernelExceptionVector.text, "ax"
|
| 98 |
|
| 99 | ENTRY(_KernelExceptionVector)
|
| 100 |
|
| 101 | xsr a3, excsave1 |
| 102 | wsr a2, depc |
| 103 | addi a2, a1, -16 - PT_KERNEL_SIZE |
| 104 | s32i a0, a2, PT_AREG0 |
| 105 | rsr a0, exccause |
| 106 | s32i a0, a2, PT_DEPC |
| 107 | addx4 a0, a0, a3 |
| 108 | l32i a0, a0, EXC_TABLE_FAST_KERNEL |
| 109 | xsr a3, excsave1 |
| 110 | jx a0
|
| 111 |
|
| 112 | ENDPROC(_KernelExceptionVector)
|
| 113 |
|
| 114 | /*
|
| 115 | * Double exception vector (Exceptions with PS.EXCM == 1)
|
| 116 | * We get this exception when another exception occurs while were are
|
| 117 | * already in an exception, such as window overflow/underflow exception,
|
| 118 | * or 'expected' exceptions, for example memory exception when we were trying
|
| 119 | * to read data from an invalid address in user space.
|
| 120 | *
|
| 121 | * Note that this vector is never invoked for level-1 interrupts, because such
|
| 122 | * interrupts are disabled (masked) when PS.EXCM is set.
|
| 123 | *
|
| 124 | * We decode the exception and take the appropriate action. However, the
|
| 125 | * double exception vector is much more careful, because a lot more error
|
| 126 | * cases go through the double exception vector than through the user and
|
| 127 | * kernel exception vectors.
|
| 128 | *
|
| 129 | * Occasionally, the kernel expects a double exception to occur. This usually
|
| 130 | * happens when accessing user-space memory with the user's permissions
|
| 131 | * (l32e/s32e instructions). The kernel state, though, is not always suitable
|
| 132 | * for immediate transfer of control to handle_double, where "normal" exception
|
| 133 | * processing occurs. Also in kernel mode, TLB misses can occur if accessing
|
| 134 | * vmalloc memory, possibly requiring repair in a double exception handler.
|
| 135 | *
|
| 136 | * The variable at TABLE_FIXUP offset from the pointer in EXCSAVE_1 doubles as
|
| 137 | * a boolean variable and a pointer to a fixup routine. If the variable
|
| 138 | * EXC_TABLE_FIXUP is non-zero, this handler jumps to that address. A value of
|
| 139 | * zero indicates to use the default kernel/user exception handler.
|
| 140 | * There is only one exception, when the value is identical to the exc_table
|
| 141 | * label, the kernel is in trouble. This mechanism is used to protect critical
|
| 142 | * sections, mainly when the handler writes to the stack to assert the stack
|
| 143 | * pointer is valid. Once the fixup/default handler leaves that area, the
|
| 144 | * EXC_TABLE_FIXUP variable is reset to the fixup handler or zero.
|
| 145 | *
|
| 146 | * Procedures wishing to use this mechanism should set EXC_TABLE_FIXUP to the
|
| 147 | * nonzero address of a fixup routine before it could cause a double exception
|
| 148 | * and reset it before it returns.
|
| 149 | *
|
| 150 | * Some other things to take care of when a fast exception handler doesn't
|
| 151 | * specify a particular fixup handler but wants to use the default handlers:
|
| 152 | *
|
| 153 | * - The original stack pointer (in a1) must not be modified. The fast
|
| 154 | * exception handler should only use a2 as the stack pointer.
|
| 155 | *
|
| 156 | * - If the fast handler manipulates the stack pointer (in a2), it has to
|
| 157 | * register a valid fixup handler and cannot use the default handlers.
|
| 158 | *
|
| 159 | * - The handler can use any other generic register from a3 to a15, but it
|
| 160 | * must save the content of these registers to stack (PT_AREG3...PT_AREGx)
|
| 161 | *
|
| 162 | * - These registers must be saved before a double exception can occur.
|
| 163 | *
|
| 164 | * - If we ever implement handling signals while in double exceptions, the
|
| 165 | * number of registers a fast handler has saved (excluding a0 and a1) must
|
| 166 | * be written to PT_AREG1. (1 if only a3 is used, 2 for a3 and a4, etc. )
|
| 167 | *
|
| 168 | * The fixup handlers are special handlers:
|
| 169 | *
|
| 170 | * - Fixup entry conditions differ from regular exceptions:
|
| 171 | *
|
| 172 | * a0: DEPC
|
| 173 | * a1: a1
|
| 174 | * a2: trashed, original value in EXC_TABLE_DOUBLE_SAVE
|
| 175 | * a3: exctable
|
| 176 | * depc: a0
|
| 177 | * excsave_1: a3
|
| 178 | *
|
| 179 | * - When the kernel enters the fixup handler, it still assumes it is in a
|
| 180 | * critical section, so EXC_TABLE_FIXUP variable is set to exc_table.
|
| 181 | * The fixup handler, therefore, has to re-register itself as the fixup
|
| 182 | * handler before it returns from the double exception.
|
| 183 | *
|
| 184 | * - Fixup handler can share the same exception frame with the fast handler.
|
| 185 | * The kernel stack pointer is not changed when entering the fixup handler.
|
| 186 | *
|
| 187 | * - Fixup handlers can jump to the default kernel and user exception
|
| 188 | * handlers. Before it jumps, though, it has to setup a exception frame
|
| 189 | * on stack. Because the default handler resets the register fixup handler
|
| 190 | * the fixup handler must make sure that the default handler returns to
|
| 191 | * it instead of the exception address, so it can re-register itself as
|
| 192 | * the fixup handler.
|
| 193 | *
|
| 194 | * In case of a critical condition where the kernel cannot recover, we jump
|
| 195 | * to unrecoverable_exception with the following entry conditions.
|
| 196 | * All registers a0...a15 are unchanged from the last exception, except:
|
| 197 | *
|
| 198 | * a0: last address before we jumped to the unrecoverable_exception.
|
| 199 | * excsave_1: a0
|
| 200 | *
|
| 201 | *
|
| 202 | * See the handle_alloca_user and spill_registers routines for example clients.
|
| 203 | *
|
| 204 | * FIXME: Note: we currently don't allow signal handling coming from a double
|
| 205 | * exception, so the item markt with (*) is not required.
|
| 206 | */
|
| 207 |
|
| 208 | .section .DoubleExceptionVector.text, "ax"
|
| 209 |
|
| 210 | ENTRY(_DoubleExceptionVector)
|
| 211 |
|
| 212 | xsr a3, excsave1
|
| 213 | s32i a2, a3, EXC_TABLE_DOUBLE_SAVE
|
| 214 |
|
| 215 | /* Check for kernel double exception (usually fatal). */
|
| 216 |
|
| 217 | rsr a2, ps
|
| 218 | _bbsi.l a2, PS_UM_BIT, 1f
|
| 219 | j .Lksp
|
| 220 |
|
| 221 | .align 4
|
| 222 | .literal_position
|
| 223 | 1:
|
| 224 | /* Check if we are currently handling a window exception. */
|
| 225 | /* Note: We don't need to indicate that we enter a critical section. */
|
| 226 |
|
| 227 | xsr a0, depc |
| 228 |
|
| 229 | |
| 230 | movi a2, WINDOW_VECTORS_VADDR
|
| 231 | _bltu a0, a2, .Lfixup
|
| 232 | addi a2, a2, WINDOW_VECTORS_SIZE
|
| 233 | _bgeu a0, a2, .Lfixup
|
| 234 |
|
| 235 | /* Window overflow/underflow exception. Get stack pointer. */
|
| 236 |
|
| 237 | l32i a2, a3, EXC_TABLE_KSTK
|
| 238 |
|
| 239 | /* Check for overflow/underflow exception, jump if overflow. */
|
| 240 |
|
| 241 | bbci.l a0, 6, _DoubleExceptionVector_WindowOverflow
|
| 242 |
|
| 243 | /*
|
| 244 | * Restart window underflow exception.
|
| 245 | * Currently:
|
| 246 | * depc = orig a0,
|
| 247 | * a0 = orig DEPC,
|
| 248 | * a2 = new sp based on KSTK from exc_table
|
| 249 | * a3 = excsave_1
|
| 250 | * excsave_1 = orig a3
|
| 251 | *
|
| 252 | * We return to the instruction in user space that caused the window
|
| 253 | * underflow exception. Therefore, we change window base to the value
|
| 254 | * before we entered the window underflow exception and prepare the
|
| 255 | * registers to return as if we were coming from a regular exception
|
| 256 | * by changing depc (in a0).
|
| 257 | * Note: We can trash the current window frame (a0...a3) and depc!
|
| 258 | */
|
| 259 | _DoubleExceptionVector_WindowUnderflow:
|
| 260 | xsr a3, excsave1
|
| 261 | wsr a2, depc |
| 262 | rsr a0, ps
|
| 263 | extui a0, a0, PS_OWB_SHIFT, PS_OWB_WIDTH
|
| 264 | wsr a0, windowbase
|
| 265 | rsync
|
| 266 |
|
| 267 | /* We are now in the previous window frame. Save registers again. */
|
| 268 |
|
| 269 | xsr a2, depc |
| 270 | s32i a0, a2, PT_AREG0
|
| 271 | xsr a3, excsave1
|
| 272 | rsr a0, exccause
|
| 273 | s32i a0, a2, PT_DEPC |
| 274 | addx4 a0, a0, a3
|
| 275 | xsr a3, excsave1
|
| 276 | l32i a0, a0, EXC_TABLE_FAST_USER
|
| 277 | jx a0
|
| 278 |
|
| 279 | |
| 280 | j .Lfixup
|
| 281 | |
| 282 |
|
| 283 | /*
|
| 284 | * We only allow the ITLB miss exception if we are in kernel space.
|
| 285 | * All other exceptions are unexpected and thus unrecoverable!
|
| 286 | */
|
| 287 |
|
| 288 | |
| 289 | .extern fast_second_level_miss_double_kernel
|
| 290 |
|
| 291 | .Lksp: /* a0: a0, a1: a1, a2: a2, a3: trashed, depc: depc, excsave: a3 */
|
| 292 |
|
| 293 | rsr a3, exccause
|
| 294 | beqi a3, EXCCAUSE_ITLB_MISS, 1f
|
| 295 | addi a3, a3, -EXCCAUSE_DTLB_MISS
|
| 296 | bnez a3, .Lunrecoverable
|
| 297 | 1: movi a3, fast_second_level_miss_double_kernel
|
| 298 | jx a3
|
| 299 | |
| 300 | .equ .Lksp, .Lunrecoverable
|
| 301 | |
| 302 |
|
| 303 | /* Critical! We can't handle this situation. PANIC! */
|
| 304 |
|
| 305 | .extern unrecoverable_exception
|
| 306 |
|
| 307 | .Lunrecoverable_fixup:
|
| 308 | l32i a2, a3, EXC_TABLE_DOUBLE_SAVE
|
| 309 | xsr a0, depc
|
| 310 |
|
| 311 | .Lunrecoverable:
|
| 312 | rsr a3, excsave1
|
| 313 | wsr a0, excsave1
|
| 314 | call0 unrecoverable_exception
|
| 315 |
|
| 316 | .Lfixup:/* Check for a fixup handler or if we were in a critical section. */
|
| 317 |
|
| 318 | /* a0: depc, a1: a1, a2: trash, a3: exctable, depc: a0, excsave1: a3 */
|
| 319 |
|
| 320 | /* Enter critical section. */
|
| 321 |
|
| 322 | l32i a2, a3, EXC_TABLE_FIXUP
|
| 323 | s32i a3, a3, EXC_TABLE_FIXUP
|
| 324 | beq a2, a3, .Lunrecoverable_fixup # critical section
|
| 325 | beqz a2, .Ldflt # no handler was registered
|
| 326 |
|
| 327 | /* a0: depc, a1: a1, a2: trash, a3: exctable, depc: a0, excsave: a3 */
|
| 328 |
|
| 329 | jx a2
|
| 330 |
|
| 331 | .Ldflt: /* Get stack pointer. */
|
| 332 |
|
| 333 | l32i a2, a3, EXC_TABLE_DOUBLE_SAVE
|
| 334 | addi a2, a2, -PT_USER_SIZE
|
| 335 |
|
| 336 | /* a0: depc, a1: a1, a2: kstk, a3: exctable, depc: a0, excsave: a3 */
|
| 337 |
|
| 338 | s32i a0, a2, PT_DEPC
|
| 339 | l32i a0, a3, EXC_TABLE_DOUBLE_SAVE
|
| 340 | xsr a0, depc
|
| 341 | s32i a0, a2, PT_AREG0
|
| 342 |
|
| 343 | /* a0: avail, a1: a1, a2: kstk, a3: exctable, depc: a2, excsave: a3 */
|
| 344 |
|
| 345 | rsr a0, exccause
|
| 346 | addx4 a0, a0, a3
|
| 347 | xsr a3, excsave1
|
| 348 | l32i a0, a0, EXC_TABLE_FAST_USER
|
| 349 | jx a0
|
| 350 |
|
| 351 | #ifdef SUPPORT_WINDOWED
|
| 352 | /*
|
| 353 | * Restart window OVERFLOW exception.
|
| 354 | * Currently:
|
| 355 | * depc = orig a0,
|
| 356 | * a0 = orig DEPC,
|
| 357 | * a2 = new sp based on KSTK from exc_table
|
| 358 | * a3 = EXCSAVE_1
|
| 359 | * excsave_1 = orig a3
|
| 360 | *
|
| 361 | * We return to the instruction in user space that caused the window
|
| 362 | * overflow exception. Therefore, we change window base to the value
|
| 363 | * before we entered the window overflow exception and prepare the
|
| 364 | * registers to return as if we were coming from a regular exception
|
| 365 | * by changing DEPC (in a0).
|
| 366 | *
|
| 367 | * NOTE: We CANNOT trash the current window frame (a0...a3), but we
|
| 368 | * can clobber depc.
|
| 369 | *
|
| 370 | * The tricky part here is that overflow8 and overflow12 handlers
|
| 371 | * save a0, then clobber a0. To restart the handler, we have to restore
|
| 372 | * a0 if the double exception was past the point where a0 was clobbered.
|
| 373 | *
|
| 374 | * To keep things simple, we take advantage of the fact all overflow
|
| 375 | * handlers save a0 in their very first instruction. If DEPC was past
|
| 376 | * that instruction, we can safely restore a0 from where it was saved
|
| 377 | * on the stack.
|
| 378 | *
|
| 379 | * a0: depc, a1: a1, a2: kstk, a3: exc_table, depc: a0, excsave1: a3
|
| 380 | */
|
| 381 | _DoubleExceptionVector_WindowOverflow:
|
| 382 | extui a2, a0, 0, 6 # get offset into 64-byte vector handler
|
| 383 | beqz a2, 1f # if at start of vector, don't restore
|
| 384 |
|
| 385 | addi a0, a0, -128
|
| 386 | bbsi.l a0, 8, 1f |
| 387 |
|
| 388 | /*
|
| 389 | * This fixup handler is for the extremely unlikely case where the
|
| 390 | * overflow handler's reference thru a0 gets a hardware TLB refill
|
| 391 | * that bumps out the (distinct, aliasing) TLB entry that mapped its
|
| 392 | * prior references thru a9/a13, and where our reference now thru
|
| 393 | * a9/a13 gets a 2nd-level miss exception (not hardware TLB refill).
|
| 394 | */
|
| 395 | movi a2, window_overflow_restore_a0_fixup
|
| 396 | s32i a2, a3, EXC_TABLE_FIXUP
|
| 397 | l32i a2, a3, EXC_TABLE_DOUBLE_SAVE
|
| 398 | xsr a3, excsave1
|
| 399 |
|
| 400 | bbsi.l a0, 7, 2f
|
| 401 |
|
| 402 | /*
|
| 403 | * Restore a0 as saved by _WindowOverflow8().
|
| 404 | */
|
| 405 |
|
| 406 | l32e a0, a9, -16
|
| 407 | wsr a0, depc # replace the saved a0
|
| 408 | j 3f
|
| 409 |
|
| 410 | 2:
|
| 411 | /*
|
| 412 | * Restore a0 as saved by _WindowOverflow12().
|
| 413 | */
|
| 414 |
|
| 415 | l32e a0, a13, -16
|
| 416 | wsr a0, depc # replace the saved a0
|
| 417 | 3:
|
| 418 | xsr a3, excsave1
|
| 419 | movi a0, 0
|
| 420 | s32i a0, a3, EXC_TABLE_FIXUP
|
| 421 | s32i a2, a3, EXC_TABLE_DOUBLE_SAVE
|
| 422 | 1:
|
| 423 | /*
|
| 424 | * Restore WindowBase while leaving all address registers restored.
|
| 425 | * We have to use ROTW for this, because WSR.WINDOWBASE requires
|
| 426 | * an address register (which would prevent restore).
|
| 427 | *
|
| 428 | * Window Base goes from 0 ... 7 (Module 8)
|
| 429 | * Window Start is 8 bits; Ex: (0b1010 1010):0x55 from series of call4s
|
| 430 | */
|
| 431 |
|
| 432 | rsr a0, ps
|
| 433 | extui a0, a0, PS_OWB_SHIFT, PS_OWB_WIDTH
|
| 434 | rsr a2, windowbase
|
| 435 | sub a0, a2, a0
|
| 436 | extui a0, a0, 0, 3
|
| 437 |
|
| 438 | l32i a2, a3, EXC_TABLE_DOUBLE_SAVE
|
| 439 | xsr a3, excsave1
|
| 440 | beqi a0, 1, .L1pane
|
| 441 | beqi a0, 3, .L3pane
|
| 442 |
|
| 443 | rsr a0, depc
|
| 444 | rotw -2
|
| 445 |
|
| 446 | /*
|
| 447 | * We are now in the user code's original window frame.
|
| 448 | * Process the exception as a user exception as if it was
|
| 449 | * taken by the user code.
|
| 450 | *
|
| 451 | * This is similar to the user exception vector,
|
| 452 | * except that PT_DEPC isn't set to EXCCAUSE.
|
| 453 | */
|
| 454 | 1:
|
| 455 | xsr a3, excsave1
|
| 456 | wsr a2, depc
|
| 457 | l32i a2, a3, EXC_TABLE_KSTK
|
| 458 | s32i a0, a2, PT_AREG0
|
| 459 | rsr a0, exccause
|
| 460 |
|
| 461 | s32i a0, a2, PT_DEPC
|
| 462 |
|
| 463 | _DoubleExceptionVector_handle_exception:
|
| 464 | addi a0, a0, -EXCCAUSE_UNALIGNED
|
| 465 | beqz a0, 2f
|
| 466 | addx4 a0, a0, a3
|
| 467 | l32i a0, a0, EXC_TABLE_FAST_USER + 4 * EXCCAUSE_UNALIGNED
|
| 468 | xsr a3, excsave1
|
| 469 | jx a0
|
| 470 | 2:
|
| 471 | movi a0, user_exception
|
| 472 | xsr a3, excsave1
|
| 473 | jx a0
|
| 474 |
|
| 475 | .L1pane:
|
| 476 | rsr a0, depc
|
| 477 | rotw -1
|
| 478 | j 1b
|
| 479 |
|
| 480 | .L3pane:
|
| 481 | rsr a0, depc
|
| 482 | rotw -3
|
| 483 | j 1b
|
| 484 | #endif
|
| 485 |
|
| 486 | ENDPROC(_DoubleExceptionVector)
|
| 487 |
|
| 488 | #ifdef SUPPORT_WINDOWED
|
| 489 |
|
| 490 | /*
|
| 491 | * Fixup handler for TLB miss in double exception handler for window owerflow.
|
| 492 | * We get here with windowbase set to the window that was being spilled and
|
| 493 | * a0 trashed. a0 bit 7 determines if this is a call8 (bit clear) or call12
|
| 494 | * (bit set) window.
|
| 495 | *
|
| 496 | * We do the following here:
|
| 497 | * - go to the original window retaining a0 value;
|
| 498 | * - set up exception stack to return back to appropriate a0 restore code
|
| 499 | * (we'll need to rotate window back and there's no place to save this
|
| 500 | * information, use different return address for that);
|
| 501 | * - handle the exception;
|
| 502 | * - go to the window that was being spilled;
|
| 503 | * - set up window_overflow_restore_a0_fixup as a fixup routine;
|
| 504 | * - reload a0;
|
| 505 | * - restore the original window;
|
| 506 | * - reset the default fixup routine;
|
| 507 | * - return to user. By the time we get to this fixup handler all information
|
| 508 | * about the conditions of the original double exception that happened in
|
| 509 | * the window overflow handler is lost, so we just return to userspace to
|
| 510 | * retry overflow from start.
|
| 511 | *
|
| 512 | * a0: value of depc, original value in depc
|
| 513 | * a2: trashed, original value in EXC_TABLE_DOUBLE_SAVE
|
| 514 | * a3: exctable, original value in excsave1
|
| 515 | */
|
| 516 |
|
| 517 | __XTENSA_HANDLER
|
| 518 | .literal_position
|
| 519 |
|
| 520 | ENTRY(window_overflow_restore_a0_fixup)
|
| 521 |
|
| 522 | rsr a0, ps
|
| 523 | extui a0, a0, PS_OWB_SHIFT, PS_OWB_WIDTH
|
| 524 | rsr a2, windowbase
|
| 525 | sub a0, a2, a0
|
| 526 | extui a0, a0, 0, 3
|
| 527 | l32i a2, a3, EXC_TABLE_DOUBLE_SAVE
|
| 528 | xsr a3, excsave1
|
| 529 |
|
| 530 | _beqi a0, 1, .Lhandle_1
|
| 531 | _beqi a0, 3, .Lhandle_3
|
| 532 |
|
| 533 | .macro overflow_fixup_handle_exception_pane n
|
| 534 |
|
| 535 | rsr a0, depc
|
| 536 | rotw -\n
|
| 537 |
|
| 538 | xsr a3, excsave1
|
| 539 | wsr a2, depc
|
| 540 | l32i a2, a3, EXC_TABLE_KSTK
|
| 541 | s32i a0, a2, PT_AREG0
|
| 542 |
|
| 543 | movi a0, .Lrestore_\n
|
| 544 | s32i a0, a2, PT_DEPC
|
| 545 | rsr a0, exccause
|
| 546 | j _DoubleExceptionVector_handle_exception
|
| 547 |
|
| 548 | .endm
|
| 549 |
|
| 550 | overflow_fixup_handle_exception_pane 2
|
| 551 | .Lhandle_1:
|
| 552 | overflow_fixup_handle_exception_pane 1
|
| 553 | .Lhandle_3:
|
| 554 | overflow_fixup_handle_exception_pane 3
|
| 555 |
|
| 556 | .macro overflow_fixup_restore_a0_pane n
|
| 557 |
|
| 558 | rotw \n
|
| 559 | /* Need to preserve a0 value here to be able to handle exception
|
| 560 | * that may occur on a0 reload from stack. It may occur because
|
| 561 | * TLB miss handler may not be atomic and pointer to page table
|
| 562 | * may be lost before we get here. There are no free registers,
|
| 563 | * so we need to use EXC_TABLE_DOUBLE_SAVE area.
|
| 564 | */
|
| 565 | xsr a3, excsave1
|
| 566 | s32i a2, a3, EXC_TABLE_DOUBLE_SAVE
|
| 567 | movi a2, window_overflow_restore_a0_fixup
|
| 568 | s32i a2, a3, EXC_TABLE_FIXUP
|
| 569 | l32i a2, a3, EXC_TABLE_DOUBLE_SAVE
|
| 570 | xsr a3, excsave1
|
| 571 | bbsi.l a0, 7, 1f
|
| 572 | l32e a0, a9, -16
|
| 573 | j 2f
|
| 574 | 1:
|
| 575 | l32e a0, a13, -16
|
| 576 | 2:
|
| 577 | rotw -\n
|
| 578 |
|
| 579 | .endm
|
| 580 |
|
| 581 | .Lrestore_2:
|
| 582 | overflow_fixup_restore_a0_pane 2
|
| 583 |
|
| 584 | .Lset_default_fixup:
|
| 585 | xsr a3, excsave1
|
| 586 | s32i a2, a3, EXC_TABLE_DOUBLE_SAVE
|
| 587 | movi a2, 0
|
| 588 | s32i a2, a3, EXC_TABLE_FIXUP
|
| 589 | l32i a2, a3, EXC_TABLE_DOUBLE_SAVE
|
| 590 | xsr a3, excsave1
|
| 591 | rfe
|
| 592 |
|
| 593 | .Lrestore_1:
|
| 594 | overflow_fixup_restore_a0_pane 1
|
| 595 | j .Lset_default_fixup
|
| 596 | .Lrestore_3:
|
| 597 | overflow_fixup_restore_a0_pane 3
|
| 598 | j .Lset_default_fixup
|
| 599 |
|
| 600 | ENDPROC(window_overflow_restore_a0_fixup)
|
| 601 |
|
| 602 | #endif
|
| 603 |
|
| 604 | /*
|
| 605 | * Debug interrupt vector
|
| 606 | *
|
| 607 | * There is not much space here, so simply jump to another handler.
|
| 608 | * EXCSAVE[DEBUGLEVEL] has been set to that handler.
|
| 609 | */
|
| 610 |
|
| 611 | .section .DebugInterruptVector.text, "ax"
|
| 612 |
|
| 613 | ENTRY(_DebugInterruptVector)
|
| 614 |
|
| 615 | xsr a3, SREG_EXCSAVE + XCHAL_DEBUGLEVEL
|
| 616 | s32i a0, a3, DT_DEBUG_SAVE
|
| 617 | l32i a0, a3, DT_DEBUG_EXCEPTION
|
| 618 | jx a0
|
| 619 |
|
| 620 | ENDPROC(_DebugInterruptVector)
|
| 621 |
|
| 622 |
|
| 623 |
|
| 624 | /*
|
| 625 | * Medium priority level interrupt vectors
|
| 626 | *
|
| 627 | * Each takes less than 16 (0x10) bytes, no literals, by placing
|
| 628 | * the extra 8 bytes that would otherwise be required in the window
|
| 629 | * vectors area where there is space. With relocatable vectors,
|
| 630 | * all vectors are within ~ 4 kB range of each other, so we can
|
| 631 | * simply jump (J) to another vector without having to use JX.
|
| 632 | *
|
| 633 | * common_exception code gets current IRQ level in PS.INTLEVEL
|
| 634 | * and preserves it for the IRQ handling time.
|
| 635 | */
|
| 636 |
|
| 637 | .macro irq_entry_level level
|
| 638 |
|
| 639 | .if XCHAL_EXCM_LEVEL >= \level
|
| 640 | .section .Level\level\()InterruptVector.text, "ax"
|
| 641 | ENTRY(_Level\level\()InterruptVector)
|
| 642 | wsr a0, excsave2
|
| 643 | rsr a0, epc\level
|
| 644 | wsr a0, epc1
|
| 645 | .if \level <= LOCKLEVEL
|
| 646 | movi a0, EXCCAUSE_LEVEL1_INTERRUPT
|
| 647 | .else
|
| 648 | movi a0, EXCCAUSE_MAPPED_NMI
|
| 649 | .endif
|
| 650 | wsr a0, exccause
|
| 651 | rsr a0, eps\level
|
| 652 | # branch to user or kernel vector
|
| 653 | j _SimulateUserKernelVectorException
|
| 654 | .endif
|
| 655 |
|
| 656 | .endm
|
| 657 |
|
| 658 | irq_entry_level 2
|
| 659 | irq_entry_level 3
|
| 660 | irq_entry_level 4
|
| 661 | irq_entry_level 5
|
| 662 | irq_entry_level 6
|
| 663 |
|
| 664 | #if XCHAL_EXCM_LEVEL >= 2
|
| 665 | /*
|
| 666 | * Continuation of medium priority interrupt dispatch code.
|
| 667 | * On entry here, a0 contains PS, and EPC2 contains saved a0:
|
| 668 | */
|
| 669 | __XTENSA_HANDLER
|
| 670 | .align 4
|
| 671 | _SimulateUserKernelVectorException:
|
| 672 | addi a0, a0, (1 << PS_EXCM_BIT)
|
| 673 | #if !XTENSA_FAKE_NMI
|
| 674 | wsr a0, ps
|
| 675 | #endif
|
| 676 | bbsi.l a0, PS_UM_BIT, 1f # branch if user mode
|
| 677 | xsr a0, excsave2 # restore a0
|
| 678 | j _KernelExceptionVector # simulate kernel vector exception
|
| 679 | 1: xsr a0, excsave2 # restore a0
|
| 680 | j _UserExceptionVector # simulate user vector exception
|
| 681 | #endif
|
| 682 |
|
| 683 |
|
| 684 | /* Window overflow and underflow handlers.
|
| 685 | * The handlers must be 64 bytes apart, first starting with the underflow
|
| 686 | * handlers underflow-4 to underflow-12, then the overflow handlers
|
| 687 | * overflow-4 to overflow-12.
|
| 688 | *
|
| 689 | * Note: We rerun the underflow handlers if we hit an exception, so
|
| 690 | * we try to access any page that would cause a page fault early.
|
| 691 | */
|
| 692 |
|
| 693 | #define ENTRY_ALIGN64(name) \
|
| 694 | .globl name; \
|
| 695 | .align 64; \
|
| 696 | name:
|
| 697 |
|
| 698 | .section .WindowVectors.text, "ax"
|
| 699 |
|
| 700 |
|
| 701 | #ifdef SUPPORT_WINDOWED
|
| 702 |
|
| 703 | /* 4-Register Window Overflow Vector (Handler) */
|
| 704 |
|
| 705 | ENTRY_ALIGN64(_WindowOverflow4)
|
| 706 |
|
| 707 | s32e a0, a5, -16
|
| 708 | s32e a1, a5, -12
|
| 709 | s32e a2, a5, -8
|
| 710 | s32e a3, a5, -4
|
| 711 | rfwo
|
| 712 |
|
| 713 | ENDPROC(_WindowOverflow4)
|
| 714 |
|
| 715 | /* 4-Register Window Underflow Vector (Handler) */
|
| 716 |
|
| 717 | ENTRY_ALIGN64(_WindowUnderflow4)
|
| 718 |
|
| 719 | l32e a0, a5, -16
|
| 720 | l32e a1, a5, -12
|
| 721 | l32e a2, a5, -8
|
| 722 | l32e a3, a5, -4
|
| 723 | rfwu
|
| 724 |
|
| 725 | ENDPROC(_WindowUnderflow4)
|
| 726 |
|
| 727 | /* 8-Register Window Overflow Vector (Handler) */
|
| 728 |
|
| 729 | ENTRY_ALIGN64(_WindowOverflow8)
|
| 730 |
|
| 731 | s32e a0, a9, -16
|
| 732 | l32e a0, a1, -12
|
| 733 | s32e a2, a9, -8
|
| 734 | s32e a1, a9, -12
|
| 735 | s32e a3, a9, -4
|
| 736 | s32e a4, a0, -32
|
| 737 | s32e a5, a0, -28
|
| 738 | s32e a6, a0, -24
|
| 739 | s32e a7, a0, -20
|
| 740 | rfwo
|
| 741 |
|
| 742 | ENDPROC(_WindowOverflow8)
|
| 743 |
|
| 744 | /* 8-Register Window Underflow Vector (Handler) */
|
| 745 |
|
| 746 | ENTRY_ALIGN64(_WindowUnderflow8)
|
| 747 |
|
| 748 | l32e a1, a9, -12
|
| 749 | l32e a0, a9, -16
|
| 750 | l32e a7, a1, -12
|
| 751 | l32e a2, a9, -8
|
| 752 | l32e a4, a7, -32
|
| 753 | l32e a3, a9, -4
|
| 754 | l32e a5, a7, -28
|
| 755 | l32e a6, a7, -24
|
| 756 | l32e a7, a7, -20
|
| 757 | rfwu
|
| 758 |
|
| 759 | ENDPROC(_WindowUnderflow8)
|
| 760 |
|
| 761 | /* 12-Register Window Overflow Vector (Handler) */
|
| 762 |
|
| 763 | ENTRY_ALIGN64(_WindowOverflow12)
|
| 764 |
|
| 765 | s32e a0, a13, -16
|
| 766 | l32e a0, a1, -12
|
| 767 | s32e a1, a13, -12
|
| 768 | s32e a2, a13, -8
|
| 769 | s32e a3, a13, -4
|
| 770 | s32e a4, a0, -48
|
| 771 | s32e a5, a0, -44
|
| 772 | s32e a6, a0, -40
|
| 773 | s32e a7, a0, -36
|
| 774 | s32e a8, a0, -32
|
| 775 | s32e a9, a0, -28
|
| 776 | s32e a10, a0, -24
|
| 777 | s32e a11, a0, -20
|
| 778 | rfwo
|
| 779 |
|
| 780 | ENDPROC(_WindowOverflow12)
|
| 781 |
|
| 782 | /* 12-Register Window Underflow Vector (Handler) */
|
| 783 |
|
| 784 | ENTRY_ALIGN64(_WindowUnderflow12)
|
| 785 |
|
| 786 | l32e a1, a13, -12
|
| 787 | l32e a0, a13, -16
|
| 788 | l32e a11, a1, -12
|
| 789 | l32e a2, a13, -8
|
| 790 | l32e a4, a11, -48
|
| 791 | l32e a8, a11, -32
|
| 792 | l32e a3, a13, -4
|
| 793 | l32e a5, a11, -44
|
| 794 | l32e a6, a11, -40
|
| 795 | l32e a7, a11, -36
|
| 796 | l32e a9, a11, -28
|
| 797 | l32e a10, a11, -24
|
| 798 | l32e a11, a11, -20
|
| 799 | rfwu
|
| 800 |
|
| 801 | ENDPROC(_WindowUnderflow12)
|
| 802 |
|
| 803 | #endif
|
| 804 |
|
| 805 | .text
|