exception.S revision 262987
1/* $NetBSD: exception.S,v 1.13 2003/10/31 16:30:15 scw Exp $ */ 2 3/*- 4 * Copyright (c) 1994-1997 Mark Brinicombe. 5 * Copyright (c) 1994 Brini. 6 * All rights reserved. 7 * 8 * This code is derived from software written for Brini by Mark Brinicombe 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by Brini. 21 * 4. The name of the company nor the name of the author may be used to 22 * endorse or promote products derived from this software without specific 23 * prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED 26 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 27 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 28 * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 * 37 * RiscBSD kernel project 38 * 39 * exception.S 40 * 41 * Low level handlers for exception vectors 42 * 43 * Created : 24/09/94 44 * 45 * Based on kate/display/abort.s 46 * 47 */ 48 49#include "assym.s" 50 51#include <machine/asm.h> 52#include <machine/armreg.h> 53#include <machine/asmacros.h> 54__FBSDID("$FreeBSD: head/sys/arm/arm/exception.S 262987 2014-03-10 22:52:32Z ian $"); 55 56 .text 57 .align 0 58 59/* 60 * ASM macros for pushing and pulling trapframes from the stack 61 * 62 * These macros are used to handle the irqframe and trapframe structures 63 * defined above. 64 */ 65 66/* 67 * PUSHFRAME - macro to push a trap frame on the stack in the current mode 68 * Since the current mode is used, the SVC lr field is not defined. 69 * 70 * NOTE: r13 and r14 are stored separately as a work around for the 71 * SA110 rev 2 STM^ bug 72 */ 73#ifdef ARM_TP_ADDRESS 74#define PUSHFRAME \ 75 sub sp, sp, #4; /* Align the stack */ \ 76 str lr, [sp, #-4]!; /* Push the return address */ \ 77 sub sp, sp, #(4*17); /* Adjust the stack pointer */ \ 78 stmia sp, {r0-r12}; /* Push the user mode registers */ \ 79 add r0, sp, #(4*13); /* Adjust the stack pointer */ \ 80 stmia r0, {r13-r14}^; /* Push the user mode registers */ \ 81 mov r0, r0; /* NOP for previous instruction */ \ 82 mrs r0, spsr; /* Put the SPSR on the stack */ \ 83 str r0, [sp, #-4]!; \ 84 ldr r0, =ARM_RAS_START; \ 85 mov r1, #0; \ 86 str r1, [r0]; \ 87 mov r1, #0xffffffff; \ 88 str r1, [r0, #4]; 89#else 90#define PUSHFRAME \ 91 sub sp, sp, #4; /* Align the stack */ \ 92 str lr, [sp, #-4]!; /* Push the return address */ \ 93 sub sp, sp, #(4*17); /* Adjust the stack pointer */ \ 94 stmia sp, {r0-r12}; /* Push the user mode registers */ \ 95 add r0, sp, #(4*13); /* Adjust the stack pointer */ \ 96 stmia r0, {r13-r14}^; /* Push the user mode registers */ \ 97 mov r0, r0; /* NOP for previous instruction */ \ 98 mrs r0, spsr; /* Put the SPSR on the stack */ \ 99 str r0, [sp, #-4]!; 100#endif 101 102/* 103 * PULLFRAME - macro to pull a trap frame from the stack in the current mode 104 * Since the current mode is used, the SVC lr field is ignored. 105 */ 106 107#ifdef ARM_TP_ADDRESS 108#define PULLFRAME \ 109 ldr r0, [sp], #0x0004; /* Get the SPSR from stack */ \ 110 msr spsr_fsxc, r0; \ 111 ldmia sp, {r0-r14}^; /* Restore registers (usr mode) */ \ 112 mov r0, r0; /* NOP for previous instruction */ \ 113 add sp, sp, #(4*17); /* Adjust the stack pointer */ \ 114 ldr lr, [sp], #0x0004; /* Pull the return address */ \ 115 add sp, sp, #4 /* Align the stack */ 116#else 117#define PULLFRAME \ 118 ldr r0, [sp], #0x0004; /* Get the SPSR from stack */ \ 119 msr spsr_fsxc, r0; \ 120 clrex; \ 121 ldmia sp, {r0-r14}^; /* Restore registers (usr mode) */ \ 122 mov r0, r0; /* NOP for previous instruction */ \ 123 add sp, sp, #(4*17); /* Adjust the stack pointer */ \ 124 ldr lr, [sp], #0x0004; /* Pull the return address */ \ 125 add sp, sp, #4 /* Align the stack */ 126#endif 127 128/* 129 * PUSHFRAMEINSVC - macro to push a trap frame on the stack in SVC32 mode 130 * This should only be used if the processor is not currently in SVC32 131 * mode. The processor mode is switched to SVC mode and the trap frame is 132 * stored. The SVC lr field is used to store the previous value of 133 * lr in SVC mode. 134 * 135 * NOTE: r13 and r14 are stored separately as a work around for the 136 * SA110 rev 2 STM^ bug 137 */ 138#ifdef ARM_TP_ADDRESS 139#define PUSHFRAMEINSVC \ 140 stmdb sp, {r0-r3}; /* Save 4 registers */ \ 141 mov r0, lr; /* Save xxx32 r14 */ \ 142 mov r1, sp; /* Save xxx32 sp */ \ 143 mrs r3, spsr; /* Save xxx32 spsr */ \ 144 mrs r2, cpsr; /* Get the CPSR */ \ 145 bic r2, r2, #(PSR_MODE); /* Fix for SVC mode */ \ 146 orr r2, r2, #(PSR_SVC32_MODE); \ 147 msr cpsr_c, r2; /* Punch into SVC mode */ \ 148 mov r2, sp; /* Save SVC sp */ \ 149 bic sp, sp, #7; /* Align sp to an 8-byte addrress */ \ 150 sub sp, sp, #4; /* Pad trapframe to keep alignment */ \ 151 str r0, [sp, #-4]!; /* Push return address */ \ 152 str lr, [sp, #-4]!; /* Push SVC lr */ \ 153 str r2, [sp, #-4]!; /* Push SVC sp */ \ 154 msr spsr_fsxc, r3; /* Restore correct spsr */ \ 155 ldmdb r1, {r0-r3}; /* Restore 4 regs from xxx mode */ \ 156 sub sp, sp, #(4*15); /* Adjust the stack pointer */ \ 157 stmia sp, {r0-r12}; /* Push the user mode registers */ \ 158 add r0, sp, #(4*13); /* Adjust the stack pointer */ \ 159 stmia r0, {r13-r14}^; /* Push the user mode registers */ \ 160 mov r0, r0; /* NOP for previous instruction */ \ 161 ldr r5, =ARM_RAS_START; /* Check if there's any RAS */ \ 162 ldr r4, [r5, #4]; /* reset it to point at the */ \ 163 cmp r4, #0xffffffff; /* end of memory if necessary; */ \ 164 movne r1, #0xffffffff; /* leave value in r4 for later */ \ 165 strne r1, [r5, #4]; /* comparision against PC. */ \ 166 ldr r3, [r5]; /* Retrieve global RAS_START */ \ 167 cmp r3, #0; /* and reset it if non-zero. */ \ 168 movne r1, #0; /* If non-zero RAS_START and */ \ 169 strne r1, [r5]; /* PC was lower than RAS_END, */ \ 170 ldrne r1, [r0, #16]; /* adjust the saved PC so that */ \ 171 cmpne r4, r1; /* execution later resumes at */ \ 172 strhi r3, [r0, #16]; /* the RAS_START location. */ \ 173 mrs r0, spsr; \ 174 str r0, [sp, #-4]! 175#else 176#define PUSHFRAMEINSVC \ 177 stmdb sp, {r0-r3}; /* Save 4 registers */ \ 178 mov r0, lr; /* Save xxx32 r14 */ \ 179 mov r1, sp; /* Save xxx32 sp */ \ 180 mrs r3, spsr; /* Save xxx32 spsr */ \ 181 mrs r2, cpsr; /* Get the CPSR */ \ 182 bic r2, r2, #(PSR_MODE); /* Fix for SVC mode */ \ 183 orr r2, r2, #(PSR_SVC32_MODE); \ 184 msr cpsr_c, r2; /* Punch into SVC mode */ \ 185 mov r2, sp; /* Save SVC sp */ \ 186 bic sp, sp, #7; /* Align sp to an 8-byte addrress */ \ 187 sub sp, sp, #4; /* Pad trapframe to keep alignment */ \ 188 str r0, [sp, #-4]!; /* Push return address */ \ 189 str lr, [sp, #-4]!; /* Push SVC lr */ \ 190 str r2, [sp, #-4]!; /* Push SVC sp */ \ 191 msr spsr_fsxc, r3; /* Restore correct spsr */ \ 192 ldmdb r1, {r0-r3}; /* Restore 4 regs from xxx mode */ \ 193 sub sp, sp, #(4*15); /* Adjust the stack pointer */ \ 194 stmia sp, {r0-r12}; /* Push the user mode registers */ \ 195 add r0, sp, #(4*13); /* Adjust the stack pointer */ \ 196 stmia r0, {r13-r14}^; /* Push the user mode registers */ \ 197 mov r0, r0; /* NOP for previous instruction */ \ 198 mrs r0, spsr; /* Put the SPSR on the stack */ \ 199 str r0, [sp, #-4]! 200#endif 201 202/* 203 * PULLFRAMEFROMSVCANDEXIT - macro to pull a trap frame from the stack 204 * in SVC32 mode and restore the saved processor mode and PC. 205 * This should be used when the SVC lr register needs to be restored on 206 * exit. 207 */ 208 209#ifdef ARM_TP_ADDRESS 210#define PULLFRAMEFROMSVCANDEXIT \ 211 ldr r0, [sp], #0x0004; /* Get the SPSR from stack */ \ 212 msr spsr_fsxc, r0; /* restore SPSR */ \ 213 ldmia sp, {r0-r14}^; /* Restore registers (usr mode) */ \ 214 mov r0, r0; /* NOP for previous instruction */ \ 215 add sp, sp, #(4*15); /* Adjust the stack pointer */ \ 216 ldmia sp, {sp, lr, pc}^ /* Restore lr and exit */ 217#else 218#define PULLFRAMEFROMSVCANDEXIT \ 219 ldr r0, [sp], #0x0004; /* Get the SPSR from stack */ \ 220 msr spsr_fsxc, r0; /* restore SPSR */ \ 221 clrex; \ 222 ldmia sp, {r0-r14}^; /* Restore registers (usr mode) */ \ 223 mov r0, r0; /* NOP for previous instruction */ \ 224 add sp, sp, #(4*15); /* Adjust the stack pointer */ \ 225 ldmia sp, {sp, lr, pc}^ /* Restore lr and exit */ 226#endif 227 228#if defined(__ARM_EABI__) 229/* 230 * Unwind hints so we can unwind past functions that use 231 * PULLFRAMEFROMSVCANDEXIT. They are run in reverse order. 232 * As the last thing we do is restore the stack pointer 233 * we can ignore the padding at the end of struct trapframe. 234 */ 235#define UNWINDSVCFRAME \ 236 .save {r13-r15}; /* Restore sp, lr, pc */ \ 237 .pad #(2*4); /* Skip user sp and lr */ \ 238 .save {r0-r12}; /* Restore r0-r12 */ \ 239 .pad #(4) /* Skip spsr */ 240#else 241#define UNWINDSVCFRAME 242#endif 243 244#define DO_AST \ 245 ldr r0, [sp] /* Get the SPSR from stack */ ;\ 246 mrs r4, cpsr /* save CPSR */ ;\ 247 orr r1, r4, #(I32_bit|F32_bit) ;\ 248 msr cpsr_c, r1 /* Disable interrupts */ ;\ 249 and r0, r0, #(PSR_MODE) /* Returning to USR mode? */ ;\ 250 teq r0, #(PSR_USR32_MODE) ;\ 251 bne 2f /* Nope, get out now */ ;\ 252 bic r4, r4, #(I32_bit|F32_bit) ;\ 2531: GET_CURTHREAD_PTR(r5) ;\ 254 ldr r1, [r5, #(TD_FLAGS)] ;\ 255 and r1, r1, #(TDF_ASTPENDING|TDF_NEEDRESCHED) ;\ 256 teq r1, #0x00000000 ;\ 257 beq 2f /* Nope. Just bail */ ;\ 258 msr cpsr_c, r4 /* Restore interrupts */ ;\ 259 mov r0, sp ;\ 260 bl _C_LABEL(ast) /* ast(frame) */ ;\ 261 orr r0, r4, #(I32_bit|F32_bit) ;\ 262 msr cpsr_c, r0 ;\ 263 b 1b ;\ 2642: 265 266 267 268 269/* 270 * reset_entry: 271 * 272 * Handler for Reset exception. 273 */ 274ASENTRY_NP(reset_entry) 275 adr r0, Lreset_panicmsg 276 bl _C_LABEL(panic) 277 /* NOTREACHED */ 278Lreset_panicmsg: 279 .asciz "Reset vector called, LR = 0x%08x" 280 .balign 4 281END(reset_entry) 282 283/* 284 * swi_entry 285 * 286 * Handler for the Software Interrupt exception. 287 */ 288ASENTRY_NP(swi_entry) 289 STOP_UNWINDING /* Don't unwind into user mode. */ 290 291 PUSHFRAME 292 293 mov r0, sp /* Pass the frame to any function */ 294 bl _C_LABEL(swi_handler) /* It's a SWI ! */ 295 296 /* 297 * The fork_trampoline() code in swtch.S aranges for the MI fork_exit() 298 * to return to swi_exit here, to return to userland. The net effect is 299 * that a newly created thread appears to return from a SWI just like 300 * the parent thread that created it. 301 */ 302ASENTRY_NP(swi_exit) 303 304 DO_AST /* Deliver signals. */ 305 306 PULLFRAME 307 movs pc, lr /* Exit */ 308 309END(swi_exit) 310END(swi_entry) 311 312/* 313 * prefetch_abort_entry: 314 * 315 * Handler for the Prefetch Abort exception. 316 */ 317ASENTRY_NP(prefetch_abort_entry) 318#ifdef __XSCALE__ 319 nop /* Make absolutely sure any pending */ 320 nop /* imprecise aborts have occurred. */ 321#endif 322 sub lr, lr, #0x00000004 /* Adjust the lr */ 323 324 PUSHFRAMEINSVC 325 ldr r1, Lprefetch_abort_handler_address 326 adr lr, exception_exit 327 mov r0, sp /* pass the stack pointer as r0 */ 328 ldr pc, [r1] 329 330Lprefetch_abort_handler_address: 331 .word _C_LABEL(prefetch_abort_handler_address) 332 333 .data 334 .global _C_LABEL(prefetch_abort_handler_address) 335 336_C_LABEL(prefetch_abort_handler_address): 337 .word prefetch_abort_handler 338 339END(prefetch_abort_entry) 340 341/* 342 * data_abort_entry: 343 * 344 * Handler for the Data Abort exception. 345 */ 346ASENTRY_NP(data_abort_entry) 347#ifdef __XSCALE__ 348 nop /* Make absolutely sure any pending */ 349 nop /* imprecise aborts have occurred. */ 350#endif 351 352 sub lr, lr, #0x00000008 /* Adjust the lr */ 353 PUSHFRAMEINSVC /* Push trap frame and switch */ 354 /* to SVC32 mode */ 355 ldr r1, Ldata_abort_handler_address 356 adr lr, exception_exit 357 mov r0, sp /* pass the stack pointer as r0 */ 358 ldr pc, [r1] 359Ldata_abort_handler_address: 360 .word _C_LABEL(data_abort_handler_address) 361 362 .data 363 .global _C_LABEL(data_abort_handler_address) 364_C_LABEL(data_abort_handler_address): 365 .word data_abort_handler 366 367END(data_abort_entry) 368 369/* 370 * addr_exception_entry: 371 * 372 * Handler for the Address Exception exception. 373 * 374 * NOTE: This exception isn't really used on arm32. We 375 * print a warning message to the console and then treat 376 * it like a Data Abort. 377 */ 378ASENTRY_NP(addr_exception_entry) 379 mrs r1, cpsr 380 mrs r2, spsr 381 mov r3, lr 382 adr r0, Laddr_exception_msg 383 bl _C_LABEL(printf) /* XXX CLOBBERS LR!! */ 384 b data_abort_entry 385Laddr_exception_msg: 386 .asciz "Address Exception CPSR=0x%08x SPSR=0x%08x LR=0x%08x\n" 387 .balign 4 388END(addr_exception_entry) 389 390/* 391 * General exception exit handler 392 * (Placed here to be within range of all the references to it) 393 * 394 * It exits straight away if not returning to USR mode. 395 * This loops around delivering any pending ASTs. 396 * Interrupts are disabled at suitable points to avoid ASTs 397 * being posted between testing and exit to user mode. 398 * 399 * This function uses PULLFRAMEFROMSVCANDEXIT and DO_AST and can 400 * only be called if the exception handler used PUSHFRAMEINSVC. 401 * 402 * For EABI, don't try to unwind any further than this. This is a 403 * stopgap measure to avoid getting stuck in a loop in the unwinder, 404 * which happens because we don't yet provide the proper unwind info 405 * here that describes which registers are being restored. 406 */ 407 408ASENTRY_NP(exception_exit) 409 UNWINDSVCFRAME 410 DO_AST 411 PULLFRAMEFROMSVCANDEXIT 412END(exception_exit) 413 414ASENTRY_NP(irq_entry) 415 sub lr, lr, #0x00000004 /* Adjust the lr */ 416 PUSHFRAMEINSVC /* Push an interrupt frame */ 417 mov r0, sp /* arg for dispatcher */ 418 419 adr lr, exception_exit 420 mov r1, #0 421 b _C_LABEL(arm_irq_handler) 422END(irq_entry) 423 424/* 425 * undefined_entry: 426 * 427 * Handler for the Undefined Instruction exception. 428 * 429 * We indirect the undefined vector via the handler address 430 * in the data area. Entry to the undefined handler must 431 * look like direct entry from the vector. 432 */ 433ASENTRY_NP(undefined_entry) 434 435 sub lr, lr, #0x00000004 /* Adjust the lr */ 436 PUSHFRAMEINSVC /* Push trap frame and switch */ 437 /* to SVC32 mode */ 438 ldr r1, Lundefined_handler_address 439 adr lr, exception_exit 440 mov r0, sp /* pass the stack pointer as r0 */ 441 ldr pc, [r1] 442END(undefined_entry) 443 444ASENTRY_NP(undefinedinstruction_bounce) 445 b undefinedinstruction 446END(undefinedinstruction_bounce) 447 448Lundefined_handler_address: 449 .word _C_LABEL(undefined_handler_address) 450 451 .data 452 .global _C_LABEL(undefined_handler_address) 453_C_LABEL(undefined_handler_address): 454 .word undefinedinstruction_bounce 455 456/* 457 * Entry point for FIQ interrupts. 458 * 459 * We don't currently support FIQ handlers very much. Something can 460 * install itself in the FIQ vector using code (that may or may not work 461 * these days) in fiq.c. If nobody does that and an FIQ happens, this 462 * default handler just disables FIQs and otherwise ignores it. 463 */ 464ASENTRY_NP(fiq_entry) 465 mrs r8, cpsr /* FIQ handling isn't supported, */ 466 bic r8, #(F32_bit) /* just disable FIQ and return. */ 467 msr cpsr_c, r8 /* The r8 we trash here is the */ 468 subs pc, lr, #4 /* banked FIQ-mode r8. */ 469END(fiq_entry) 470 471/* 472 * page0 and page0_data -- An image of the ARM vectors which is copied to 473 * the ARM vectors page (high or low) as part of CPU initialization. The 474 * code that does the copy assumes that page0_data holds one 32-bit word 475 * of data for each of the predefined ARM vectors. It also assumes that 476 * page0_data follows the vectors in page0, but other stuff can appear 477 * between the two. We currently leave room between the two for some fiq 478 * handler code to be copied in. 479 */ 480 .global _C_LABEL(page0), _C_LABEL(page0_data) 481 482_C_LABEL(page0): 483 ldr pc, .Lreset_entry 484 ldr pc, .Lundefined_entry 485 ldr pc, .Lswi_entry 486 ldr pc, .Lprefetch_abort_entry 487 ldr pc, .Ldata_abort_entry 488 ldr pc, .Laddr_exception_entry 489 ldr pc, .Lirq_entry 490.fiqv: ldr pc, .Lfiq_entry 491 .space 256 /* room for some fiq handler code */ 492 493_C_LABEL(page0_data): 494.Lreset_entry: .word reset_entry 495.Lundefined_entry: .word undefined_entry 496.Lswi_entry: .word swi_entry 497.Lprefetch_abort_entry: .word prefetch_abort_entry 498.Ldata_abort_entry: .word data_abort_entry 499.Laddr_exception_entry: .word addr_exception_entry 500.Lirq_entry: .word irq_entry 501.Lfiq_entry: .word fiq_entry 502 503/* 504 * These items are used by the code in fiq.c to install what it calls the 505 * "null" handler. It's actually our default vector entry that just jumps 506 * to the default handler which just disables FIQs and returns. 507 */ 508 .global _C_LABEL(fiq_nullhandler_code), _C_LABEL(fiq_nullhandler_size) 509 510_C_LABEL(fiq_nullhandler_code): 511 .word .fiqv 512_C_LABEL(fiq_nullhandler_size): 513 .word 4 514 515 516