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