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