exception.S revision 278529
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 "opt_kdtrace.h" 52#include <machine/asm.h> 53#include <machine/armreg.h> 54#include <machine/asmacros.h> 55__FBSDID("$FreeBSD: head/sys/arm/arm/exception.S 278529 2015-02-10 19:41:30Z gnn $"); 56 57#ifdef KDTRACE_HOOKS 58 .bss 59 .align 4 60 .global _C_LABEL(dtrace_invop_jump_addr) 61_C_LABEL(dtrace_invop_jump_addr): 62 .word 0 63 .word 0 64 65 .global _C_LABEL(dtrace_invop_calltrap_addr) 66_C_LABEL(dtrace_invop_calltrap_addr): 67 .word 0 68 .word 0 69#endif 70 71 .text 72 .align 2 73 74/* 75 * ASM macros for pushing and pulling trapframes from the stack 76 * 77 * These macros are used to handle the irqframe and trapframe structures 78 * defined above. 79 */ 80 81/* 82 * PUSHFRAME - macro to push a trap frame on the stack in the current mode 83 * Since the current mode is used, the SVC lr field is not defined. 84 * 85 * NOTE: r13 and r14 are stored separately as a work around for the 86 * SA110 rev 2 STM^ bug 87 */ 88#ifdef ARM_TP_ADDRESS 89#define PUSHFRAME \ 90 sub sp, sp, #4; /* Align the stack */ \ 91 str lr, [sp, #-4]!; /* Push the return address */ \ 92 sub sp, sp, #(4*17); /* Adjust the stack pointer */ \ 93 stmia sp, {r0-r12}; /* Push the user mode registers */ \ 94 add r0, sp, #(4*13); /* Adjust the stack pointer */ \ 95 stmia r0, {r13-r14}^; /* Push the user mode registers */ \ 96 mov r0, r0; /* NOP for previous instruction */ \ 97 mrs r0, spsr; /* Put the SPSR on the stack */ \ 98 str r0, [sp, #-4]!; \ 99 ldr r0, =ARM_RAS_START; \ 100 mov r1, #0; \ 101 str r1, [r0]; \ 102 mov r1, #0xffffffff; \ 103 str r1, [r0, #4]; 104#else 105#define PUSHFRAME \ 106 sub sp, sp, #4; /* Align the stack */ \ 107 str lr, [sp, #-4]!; /* Push the return address */ \ 108 sub sp, sp, #(4*17); /* Adjust the stack pointer */ \ 109 stmia sp, {r0-r12}; /* Push the user mode registers */ \ 110 add r0, sp, #(4*13); /* Adjust the stack pointer */ \ 111 stmia r0, {r13-r14}^; /* Push the user mode registers */ \ 112 mov r0, r0; /* NOP for previous instruction */ \ 113 mrs r0, spsr; /* Put the SPSR on the stack */ \ 114 str r0, [sp, #-4]!; 115#endif 116 117/* 118 * PULLFRAME - macro to pull a trap frame from the stack in the current mode 119 * Since the current mode is used, the SVC lr field is ignored. 120 */ 121 122#ifdef ARM_TP_ADDRESS 123#define PULLFRAME \ 124 ldr r0, [sp], #4; /* Get the SPSR from stack */ \ 125 msr spsr_fsxc, r0; \ 126 ldmia sp, {r0-r14}^; /* Restore registers (usr mode) */ \ 127 mov r0, r0; /* NOP for previous instruction */ \ 128 add sp, sp, #(4*17); /* Adjust the stack pointer */ \ 129 ldr lr, [sp], #4; /* Pull the return address */ \ 130 add sp, sp, #4 /* Align the stack */ 131#else 132#define PULLFRAME \ 133 ldr r0, [sp], #4 ; /* Get the SPSR from stack */ \ 134 msr spsr_fsxc, r0; \ 135 clrex; \ 136 ldmia sp, {r0-r14}^; /* Restore registers (usr mode) */ \ 137 mov r0, r0; /* NOP for previous instruction */ \ 138 add sp, sp, #(4*17); /* Adjust the stack pointer */ \ 139 ldr lr, [sp], #4; /* Pull the return address */ \ 140 add sp, sp, #4 /* Align the stack */ 141#endif 142 143/* 144 * PUSHFRAMEINSVC - macro to push a trap frame on the stack in SVC32 mode 145 * This should only be used if the processor is not currently in SVC32 146 * mode. The processor mode is switched to SVC mode and the trap frame is 147 * stored. The SVC lr field is used to store the previous value of 148 * lr in SVC mode. 149 * 150 * NOTE: r13 and r14 are stored separately as a work around for the 151 * SA110 rev 2 STM^ bug 152 */ 153#ifdef ARM_TP_ADDRESS 154#define PUSHFRAMEINSVC \ 155 stmdb sp, {r0-r3}; /* Save 4 registers */ \ 156 mov r0, lr; /* Save xxx32 r14 */ \ 157 mov r1, sp; /* Save xxx32 sp */ \ 158 mrs r3, spsr; /* Save xxx32 spsr */ \ 159 mrs r2, cpsr; /* Get the CPSR */ \ 160 bic r2, r2, #(PSR_MODE); /* Fix for SVC mode */ \ 161 orr r2, r2, #(PSR_SVC32_MODE); \ 162 msr cpsr_c, r2; /* Punch into SVC mode */ \ 163 mov r2, sp; /* Save SVC sp */ \ 164 bic sp, sp, #7; /* Align sp to an 8-byte addrress */ \ 165 sub sp, sp, #4; /* Pad trapframe to keep alignment */ \ 166 str r0, [sp, #-4]!; /* Push return address */ \ 167 str lr, [sp, #-4]!; /* Push SVC lr */ \ 168 str r2, [sp, #-4]!; /* Push SVC sp */ \ 169 msr spsr_fsxc, r3; /* Restore correct spsr */ \ 170 ldmdb r1, {r0-r3}; /* Restore 4 regs from xxx mode */ \ 171 sub sp, sp, #(4*15); /* Adjust the stack pointer */ \ 172 stmia sp, {r0-r12}; /* Push the user mode registers */ \ 173 add r0, sp, #(4*13); /* Adjust the stack pointer */ \ 174 stmia r0, {r13-r14}^; /* Push the user mode registers */ \ 175 mov r0, r0; /* NOP for previous instruction */ \ 176 ldr r5, =ARM_RAS_START; /* Check if there's any RAS */ \ 177 ldr r4, [r5, #4]; /* reset it to point at the */ \ 178 cmp r4, #0xffffffff; /* end of memory if necessary; */ \ 179 movne r1, #0xffffffff; /* leave value in r4 for later */ \ 180 strne r1, [r5, #4]; /* comparision against PC. */ \ 181 ldr r3, [r5]; /* Retrieve global RAS_START */ \ 182 cmp r3, #0; /* and reset it if non-zero. */ \ 183 movne r1, #0; /* If non-zero RAS_START and */ \ 184 strne r1, [r5]; /* PC was lower than RAS_END, */ \ 185 ldrne r1, [r0, #16]; /* adjust the saved PC so that */ \ 186 cmpne r4, r1; /* execution later resumes at */ \ 187 strhi r3, [r0, #16]; /* the RAS_START location. */ \ 188 mrs r0, spsr; \ 189 str r0, [sp, #-4]! 190#else 191#define PUSHFRAMEINSVC \ 192 stmdb sp, {r0-r3}; /* Save 4 registers */ \ 193 mov r0, lr; /* Save xxx32 r14 */ \ 194 mov r1, sp; /* Save xxx32 sp */ \ 195 mrs r3, spsr; /* Save xxx32 spsr */ \ 196 mrs r2, cpsr; /* Get the CPSR */ \ 197 bic r2, r2, #(PSR_MODE); /* Fix for SVC mode */ \ 198 orr r2, r2, #(PSR_SVC32_MODE); \ 199 msr cpsr_c, r2; /* Punch into SVC mode */ \ 200 mov r2, sp; /* Save SVC sp */ \ 201 bic sp, sp, #7; /* Align sp to an 8-byte addrress */ \ 202 sub sp, sp, #4; /* Pad trapframe to keep alignment */ \ 203 str r0, [sp, #-4]!; /* Push return address */ \ 204 str lr, [sp, #-4]!; /* Push SVC lr */ \ 205 str r2, [sp, #-4]!; /* Push SVC sp */ \ 206 msr spsr_fsxc, r3; /* Restore correct spsr */ \ 207 ldmdb r1, {r0-r3}; /* Restore 4 regs from xxx mode */ \ 208 sub sp, sp, #(4*15); /* Adjust the stack pointer */ \ 209 stmia sp, {r0-r12}; /* Push the user mode registers */ \ 210 add r0, sp, #(4*13); /* Adjust the stack pointer */ \ 211 stmia r0, {r13-r14}^; /* Push the user mode registers */ \ 212 mov r0, r0; /* NOP for previous instruction */ \ 213 mrs r0, spsr; /* Put the SPSR on the stack */ \ 214 str r0, [sp, #-4]! 215#endif 216 217/* 218 * PULLFRAMEFROMSVCANDEXIT - macro to pull a trap frame from the stack 219 * in SVC32 mode and restore the saved processor mode and PC. 220 * This should be used when the SVC lr register needs to be restored on 221 * exit. 222 */ 223 224#ifdef ARM_TP_ADDRESS 225#define PULLFRAMEFROMSVCANDEXIT \ 226 ldr r0, [sp], #4; /* Get the SPSR from stack */ \ 227 msr spsr_fsxc, r0; /* restore SPSR */ \ 228 ldmia sp, {r0-r14}^; /* Restore registers (usr mode) */ \ 229 mov r0, r0; /* NOP for previous instruction */ \ 230 add sp, sp, #(4*15); /* Adjust the stack pointer */ \ 231 ldmia sp, {sp, lr, pc}^ /* Restore lr and exit */ 232#else 233#define PULLFRAMEFROMSVCANDEXIT \ 234 ldr r0, [sp], #4; /* Get the SPSR from stack */ \ 235 msr spsr_fsxc, r0; /* restore SPSR */ \ 236 clrex; \ 237 ldmia sp, {r0-r14}^; /* Restore registers (usr mode) */ \ 238 mov r0, r0; /* NOP for previous instruction */ \ 239 add sp, sp, #(4*15); /* Adjust the stack pointer */ \ 240 ldmia sp, {sp, lr, pc}^ /* Restore lr and exit */ 241#endif 242 243#if defined(__ARM_EABI__) 244/* 245 * Unwind hints so we can unwind past functions that use 246 * PULLFRAMEFROMSVCANDEXIT. They are run in reverse order. 247 * As the last thing we do is restore the stack pointer 248 * we can ignore the padding at the end of struct trapframe. 249 */ 250#define UNWINDSVCFRAME \ 251 .save {r13-r15}; /* Restore sp, lr, pc */ \ 252 .pad #(2*4); /* Skip user sp and lr */ \ 253 .save {r0-r12}; /* Restore r0-r12 */ \ 254 .pad #(4) /* Skip spsr */ 255#else 256#define UNWINDSVCFRAME 257#endif 258 259#define DO_AST \ 260 ldr r0, [sp]; /* Get the SPSR from stack */ \ 261 mrs r4, cpsr; /* save CPSR */ \ 262 orr r1, r4, #(PSR_I|PSR_F); \ 263 msr cpsr_c, r1; /* Disable interrupts */ \ 264 and r0, r0, #(PSR_MODE); /* Returning to USR mode? */ \ 265 teq r0, #(PSR_USR32_MODE); \ 266 bne 2f; /* Nope, get out now */ \ 267 bic r4, r4, #(PSR_I|PSR_F); \ 2681: GET_CURTHREAD_PTR(r5); \ 269 ldr r1, [r5, #(TD_FLAGS)]; \ 270 and r1, r1, #(TDF_ASTPENDING|TDF_NEEDRESCHED); \ 271 teq r1, #0; \ 272 beq 2f; /* Nope. Just bail */ \ 273 msr cpsr_c, r4; /* Restore interrupts */ \ 274 mov r0, sp; \ 275 bl _C_LABEL(ast); /* ast(frame) */ \ 276 orr r0, r4, #(PSR_I|PSR_F); \ 277 msr cpsr_c, r0; \ 278 b 1b; \ 2792: 280 281 282/* 283 * Entry point for a Software Interrupt (SWI). 284 * 285 * The hardware switches to svc32 mode on a swi, so we're already on the 286 * right stack; just build a trapframe and call the handler. 287 */ 288ASENTRY_NP(swi_entry) 289 PUSHFRAME /* Build the trapframe on the */ 290 mov r0, sp /* scv32 stack, pass it to the */ 291 bl _C_LABEL(swi_handler) /* swi handler. */ 292 /* 293 * The fork_trampoline() code in swtch.S aranges for the MI fork_exit() 294 * to return to swi_exit here, to return to userland. The net effect is 295 * that a newly created thread appears to return from a SWI just like 296 * the parent thread that created it. 297 */ 298ASEENTRY_NP(swi_exit) 299 DO_AST /* Handle pending signals. */ 300 PULLFRAME /* Deallocate trapframe. */ 301 movs pc, lr /* Return to userland. */ 302 STOP_UNWINDING /* Don't unwind into user mode. */ 303EEND(swi_exit) 304END(swi_entry) 305 306/* 307 * Standard exception exit handler. 308 * 309 * This is used to return from all exceptions except SWI. It uses DO_AST and 310 * PULLFRAMEFROMSVCANDEXIT and can only be called if the exception entry code 311 * used PUSHFRAMEINSVC. 312 * 313 * If the return is to user mode, this uses DO_AST to deliver any pending 314 * signals and/or handle TDF_NEEDRESCHED first. 315 */ 316ASENTRY_NP(exception_exit) 317 DO_AST /* Handle pending signals. */ 318 PULLFRAMEFROMSVCANDEXIT /* Return. */ 319 UNWINDSVCFRAME /* Special unwinding for exceptions. */ 320END(exception_exit) 321 322/* 323 * Entry point for a Prefetch Abort exception. 324 * 325 * The hardware switches to the abort mode stack; we switch to svc32 before 326 * calling the handler, then return directly to the original mode/stack 327 * on exit (without transitioning back through the abort mode stack). 328 */ 329ASENTRY_NP(prefetch_abort_entry) 330#ifdef __XSCALE__ 331 nop /* Make absolutely sure any pending */ 332 nop /* imprecise aborts have occurred. */ 333#endif 334 sub lr, lr, #4 /* Adjust the lr. Transition to scv32 */ 335 PUSHFRAMEINSVC /* mode stack, build trapframe there. */ 336 adr lr, exception_exit /* Return from handler via standard */ 337 mov r0, sp /* exception exit routine. Pass the */ 338 mov r1, #1 /* Type flag */ 339 b _C_LABEL(abort_handler) 340END(prefetch_abort_entry) 341 342/* 343 * Entry point for a Data Abort exception. 344 * 345 * The hardware switches to the abort mode stack; we switch to svc32 before 346 * calling the handler, then return directly to the original mode/stack 347 * on exit (without transitioning back through the abort mode stack). 348 */ 349ASENTRY_NP(data_abort_entry) 350#ifdef __XSCALE__ 351 nop /* Make absolutely sure any pending */ 352 nop /* imprecise aborts have occurred. */ 353#endif 354 sub lr, lr, #8 /* Adjust the lr. Transition to scv32 */ 355 PUSHFRAMEINSVC /* mode stack, build trapframe there. */ 356 adr lr, exception_exit /* Exception exit routine */ 357 mov r0, sp /* Trapframe to the handler */ 358 mov r1, #0 /* Type flag */ 359 b _C_LABEL(abort_handler) 360END(data_abort_entry) 361 362/* 363 * Entry point for an Undefined Instruction exception. 364 * 365 * The hardware switches to the undefined mode stack; we switch to svc32 before 366 * calling the handler, then return directly to the original mode/stack 367 * on exit (without transitioning back through the undefined mode stack). 368 */ 369ASENTRY_NP(undefined_entry) 370 sub lr, lr, #4 /* Adjust the lr. Transition to scv32 */ 371 PUSHFRAMEINSVC /* mode stack, build trapframe there. */ 372 adr lr, exception_exit /* Return from handler via standard */ 373 mov r0, sp /* exception exit routine. Pass the */ 374 b undefinedinstruction /* trapframe to the handler. */ 375END(undefined_entry) 376 377/* 378 * Entry point for a normal IRQ. 379 * 380 * The hardware switches to the IRQ mode stack; we switch to svc32 before 381 * calling the handler, then return directly to the original mode/stack 382 * on exit (without transitioning back through the IRQ mode stack). 383 */ 384ASENTRY_NP(irq_entry) 385 sub lr, lr, #4 /* Adjust the lr. Transition to scv32 */ 386 PUSHFRAMEINSVC /* mode stack, build trapframe there. */ 387 adr lr, exception_exit /* Return from handler via standard */ 388 mov r0, sp /* exception exit routine. Pass the */ 389 b _C_LABEL(arm_irq_handler)/* trapframe to the handler. */ 390END(irq_entry) 391 392/* 393 * Entry point for an FIQ interrupt. 394 * 395 * We don't currently support FIQ handlers very much. Something can 396 * install itself in the FIQ vector using code (that may or may not work 397 * these days) in fiq.c. If nobody does that and an FIQ happens, this 398 * default handler just disables FIQs and otherwise ignores it. 399 */ 400ASENTRY_NP(fiq_entry) 401 mrs r8, cpsr /* FIQ handling isn't supported, */ 402 bic r8, #(PSR_F) /* just disable FIQ and return. */ 403 msr cpsr_c, r8 /* The r8 we trash here is the */ 404 subs pc, lr, #4 /* banked FIQ-mode r8. */ 405END(fiq_entry) 406 407/* 408 * Entry point for an Address Exception exception. 409 * This is an arm26 exception that should never happen. 410 */ 411ASENTRY_NP(addr_exception_entry) 412 mov r3, lr 413 mrs r2, spsr 414 mrs r1, cpsr 415 adr r0, Laddr_exception_msg 416 b _C_LABEL(panic) 417Laddr_exception_msg: 418 .asciz "Address Exception CPSR=0x%08x SPSR=0x%08x LR=0x%08x\n" 419 .balign 4 420END(addr_exception_entry) 421 422/* 423 * Entry point for the system Reset vector. 424 * This should never happen, so panic. 425 */ 426ASENTRY_NP(reset_entry) 427 mov r1, lr 428 adr r0, Lreset_panicmsg 429 b _C_LABEL(panic) 430 /* NOTREACHED */ 431Lreset_panicmsg: 432 .asciz "Reset vector called, LR = 0x%08x" 433 .balign 4 434END(reset_entry) 435 436/* 437 * page0 and page0_data -- An image of the ARM vectors which is copied to 438 * the ARM vectors page (high or low) as part of CPU initialization. The 439 * code that does the copy assumes that page0_data holds one 32-bit word 440 * of data for each of the predefined ARM vectors. It also assumes that 441 * page0_data follows the vectors in page0, but other stuff can appear 442 * between the two. We currently leave room between the two for some fiq 443 * handler code to be copied in. 444 */ 445 .global _C_LABEL(page0), _C_LABEL(page0_data) 446 447_C_LABEL(page0): 448 ldr pc, .Lreset_entry 449 ldr pc, .Lundefined_entry 450 ldr pc, .Lswi_entry 451 ldr pc, .Lprefetch_abort_entry 452 ldr pc, .Ldata_abort_entry 453 ldr pc, .Laddr_exception_entry 454 ldr pc, .Lirq_entry 455.fiqv: ldr pc, .Lfiq_entry 456 .space 256 /* room for some fiq handler code */ 457 458_C_LABEL(page0_data): 459.Lreset_entry: .word reset_entry 460.Lundefined_entry: .word undefined_entry 461.Lswi_entry: .word swi_entry 462.Lprefetch_abort_entry: .word prefetch_abort_entry 463.Ldata_abort_entry: .word data_abort_entry 464.Laddr_exception_entry: .word addr_exception_entry 465.Lirq_entry: .word irq_entry 466.Lfiq_entry: .word fiq_entry 467 468/* 469 * These items are used by the code in fiq.c to install what it calls the 470 * "null" handler. It's actually our default vector entry that just jumps 471 * to the default handler which just disables FIQs and returns. 472 */ 473 .global _C_LABEL(fiq_nullhandler_code), _C_LABEL(fiq_nullhandler_size) 474 475_C_LABEL(fiq_nullhandler_code): 476 .word .fiqv 477_C_LABEL(fiq_nullhandler_size): 478 .word 4 479 480 481