exception.S revision 1.11 1 /* $NetBSD: exception.S,v 1.11 2003/10/26 11:34:29 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 #include "opt_ipkdb.h"
49 #include "opt_compat_netbsd.h"
50 #include "opt_execfmt.h"
51 #include "opt_multiprocessor.h"
52 #include <machine/asm.h>
53 #include <machine/cpu.h>
54 #include <machine/frame.h>
55 #include "assym.h"
56
57 .text
58 .align 0
59
60 .Lastpending:
61 .word _C_LABEL(astpending)
62 #if defined(COMPAT_15) && defined(EXEC_AOUT)
63 .Lcpufuncs:
64 .word _C_LABEL(cpufuncs)
65 #ifndef MULTIPROCESSOR
66 .Lcurpcb:
67 .word _C_LABEL(curpcb)
68 .Lcpu_info_store:
69 .word _C_LABEL(cpu_info_store)
70 #define GET_CURPCB \
71 ldr r1, .Lcurpcb ;\
72 ldr r1, [r1]
73 #define GET_CPUINFO \
74 ldr r0, .Lcpu_info_store
75 #else
76 .Lcpu_info:
77 .word _C_LABEL(cpu_info)
78 #define GET_CURPCB \
79 ldr r4, .Lcpu_info ;\
80 bl _C_LABEL(cpu_number) ;\
81 ldr r0, [r4, r0, lsl #2] ;\
82 ldr r1, [r0, #CI_CURPCB]
83 #define GET_CPUINFO /* nothing to do */
84 #endif
85 #define ENABLE_ALIGNMENT_FAULTS \
86 GET_CURPCB ;\
87 cmp r1, #0x00 /* curpcb NULL? ;\
88 ldrne r1, [r1, #PCB_FLAGS] /* Fetch curpcb->pcb_flags */ ;\
89 tstne r1, #PCB_NOALIGNFLT ;\
90 beq 1f /* Alignment faults already enabled */ ;\
91 GET_CPUINFO ;\
92 ldr r2, .Lcpufuncs ;\
93 ldr r1, [r0, #CI_CTRL] /* Fetch control register */ ;\
94 mov r0, #-1 ;\
95 mov lr, pc ;\
96 ldr pc, [r2, #CF_CONTROL] /* Enable alignment faults */ ;\
97 1:
98 #endif /* COMPAT_15 && EXEC_AOUT */
99
100
101 /*
102 * General exception exit handler
103 *
104 * It exits straight away if not returning to USR mode.
105 * This loops around delivering any pending ASTs.
106 * Interrupts are disabled at suitable points to avoid ASTs
107 * being posted between testing and exit to user mode.
108 *
109 * This function uses PULLFRAMEFROMSVCANDEXIT thus should
110 * only be called if the exception handler used PUSHFRAMEINSVC
111 */
112
113 exception_exit:
114 mrs r4, cpsr /* Get CPSR */
115
116 ldr r0, [sp] /* Get the SPSR from stack */
117 and r0, r0, #(PSR_MODE) /* Test for USR32 mode before the AST */
118 teq r0, #(PSR_USR32_MODE)
119 bne .Ldo_exit /* Not USR mode so no AST delivery */
120
121 ldr r5, .Lastpending /* Get address of astpending */
122 #if defined(COMPAT_15) && defined(EXEC_AOUT) && !defined(MULTIPROCESSOR)
123 ldr r6, .Lcurpcb
124 ldr r7, .Lcpu_info_store
125 #endif
126
127 Lexception_exit_loop:
128 orr r0, r4, #(I32_bit) /* Block IRQs */
129 msr cpsr_all, r0
130
131 ldr r1, [r5] /* Do we have an AST pending */
132 teq r1, #0x00000000
133 bne .Ldo_ast
134
135 #if defined(COMPAT_15) && defined(EXEC_AOUT)
136 /* Disable alignment faults for the process, if necessary. */
137 #ifdef MULTIPROCESSOR
138 ldr r7, .Lcpu_info
139 bl _C_LABEL(cpu_number)
140 ldr r7, [r7, r0, lsl #2]
141 ldr r1, [r7, #CI_CURPCB]
142 #else
143 ldr r1, [r6]
144 #endif
145 cmp r1, #0x00 /* curpcb NULL? */
146 ldrne r1, [r1, #PCB_FLAGS] /* Fetch curpcb->pcb_flags */
147 tstne r1, #PCB_NOALIGNFLT
148 beq 1f /* Keep alignment faults enabled */
149 ldr r1, [r7, #CI_CTRL] /* Fetch control register */
150 ldr r2, .Lcpufuncs
151 mov r0, #-1
152 bic r1, r1, #CPU_CONTROL_AFLT_ENABLE /* Disable alignment faults */
153 mov lr, pc
154 ldr pc, [r2, #CF_CONTROL] /* Set the new control register value */
155 1:
156 #endif
157
158 PULLFRAMEFROMSVCANDEXIT /* No AST so exit */
159
160 .Ldo_ast:
161 mov r1, #0x00000000 /* Clear ast pending */
162 str r1, [r5]
163
164 msr cpsr_all, r4 /* Restore interrupts */
165
166 mov r0, sp /* arg 0 = trap frame */
167 bl _C_LABEL(ast) /* call the AST handler */
168 b Lexception_exit_loop /* Try and exit again */
169
170 .Ldo_exit:
171 orr r0, r4, #(I32_bit) /* Disable interrupts */
172 msr cpsr_all, r0
173
174 PULLFRAMEFROMSVCANDEXIT /* Restore the trap frame and exit */
175
176 /*
177 * reset_entry:
178 *
179 * Handler for Reset exception.
180 */
181 ASENTRY_NP(reset_entry)
182 adr r0, Lreset_panicmsg
183 mov r1, lr
184 bl _C_LABEL(panic)
185 /* NOTREACHED */
186 Lreset_panicmsg:
187 .asciz "Reset vector called, LR = 0x%08x"
188 .balign 4
189
190 /*
191 * swi_entry
192 *
193 * Handler for the Software Interrupt exception.
194 */
195 ASENTRY_NP(swi_entry)
196 PUSHFRAME
197
198 #if defined(COMPAT_15) && defined(EXEC_AOUT)
199 ENABLE_ALIGNMENT_FAULTS
200 #endif
201
202 mov r0, sp /* Pass the frame to any function */
203
204 bl _C_LABEL(swi_handler) /* It's a SWI ! */
205
206 ldr r5, .Lastpending /* Get address of astpending */
207 mrs r4, cpsr /* Get CPSR */
208 #if defined(COMPAT_15) && defined(EXEC_AOUT) && !defined(MULTIPROCESSOR)
209 ldr r6, .Lcurpcb
210 ldr r7, .Lcpu_info_store
211 #endif
212
213 .Lswi_exit_loop:
214 orr r0, r4, #(I32_bit) /* Disable IRQs */
215 msr cpsr_all, r0
216
217 ldr r1, [r5] /* Do we have an AST pending */
218 teq r1, #0x00000000
219 bne .Ldo_swi_ast
220
221 #if defined(COMPAT_15) && defined(EXEC_AOUT)
222 /* Disable alignment faults for the process, if necessary. */
223 #ifdef MULTIPROCESSOR
224 ldr r7, .Lcpu_info
225 bl _C_LABEL(cpu_number)
226 ldr r7, [r7, r0, lsl #2]
227 ldr r1, [r7, #CI_CURPCB]
228 #else
229 ldr r1, [r6]
230 #endif
231 cmp r1, #0x00 /* curpcb NULL? */
232 ldrne r1, [r1, #PCB_FLAGS] /* Fetch curpcb->pcb_flags */
233 tstne r1, #PCB_NOALIGNFLT
234 beq 1f /* Keep alignment faults enabled */
235 ldr r1, [r7, #CI_CTRL] /* Fetch control register */
236 ldr r2, .Lcpufuncs
237 mov r0, #-1
238 bic r1, r1, #CPU_CONTROL_AFLT_ENABLE /* Disable alignment faults */
239 mov lr, pc
240 ldr pc, [r2, #CF_CONTROL] /* Set the new control register value */
241 1:
242 #endif
243 PULLFRAME
244 movs pc, lr /* Exit */
245
246 .Ldo_swi_ast:
247 mov r1, #0x00000000 /* Clear ast pending */
248 str r1, [r5]
249
250 msr cpsr_all, r4 /* Restore interrupts */
251
252 mov r0, sp /* arg 0 = trap frame */
253 bl _C_LABEL(ast) /* call the AST handler */
254 b .Lswi_exit_loop /* Try and exit again */
255
256 /*
257 * prefetch_abort_entry:
258 *
259 * Handler for the Prefetch Abort exception.
260 */
261 ASENTRY_NP(prefetch_abort_entry)
262 sub lr, lr, #0x00000004 /* Adjust the lr */
263
264 PUSHFRAMEINSVC
265
266 #if defined(COMPAT_15) && defined(EXEC_AOUT)
267 ENABLE_ALIGNMENT_FAULTS
268 #endif
269
270 mov r0, sp /* pass the stack pointer as r0 */
271
272 adr lr, exception_exit
273 ldr r1, Lprefetch_abort_handler_address
274 ldr pc, [r1]
275
276 Lprefetch_abort_handler_address:
277 .word _C_LABEL(prefetch_abort_handler_address)
278
279 .data
280 .global _C_LABEL(prefetch_abort_handler_address)
281
282 _C_LABEL(prefetch_abort_handler_address):
283 .word abortprefetch
284
285 .text
286 abortprefetch:
287 adr r0, abortprefetchmsg
288 b _C_LABEL(panic)
289
290 abortprefetchmsg:
291 .asciz "abortprefetch"
292 .align 0
293
294 /*
295 * data_abort_entry:
296 *
297 * Handler for the Data Abort exception.
298 */
299 ASENTRY_NP(data_abort_entry)
300 sub lr, lr, #0x00000008 /* Adjust the lr */
301
302 PUSHFRAMEINSVC /* Push trap frame and switch */
303 /* to SVC32 mode */
304
305 #if defined(COMPAT_15) && defined(EXEC_AOUT)
306 and r0, r0, #(PSR_MODE) /* Test for USR32 mode (r0 = spsr_all)*/
307 teq r0, #(PSR_USR32_MODE)
308 bne 99f /* Not USR mode so skip AFLT check */
309 ENABLE_ALIGNMENT_FAULTS
310 99:
311 #endif
312
313 mov r0, sp /* pass the stack pointer as r0 */
314
315 adr lr, exception_exit
316 ldr r1, Ldata_abort_handler_address
317 ldr pc, [r1]
318
319 Ldata_abort_handler_address:
320 .word _C_LABEL(data_abort_handler_address)
321
322 .data
323 .global _C_LABEL(data_abort_handler_address)
324 _C_LABEL(data_abort_handler_address):
325 .word abortdata
326
327 .text
328 abortdata:
329 adr r0, abortdatamsg
330 b _C_LABEL(panic)
331
332 abortdatamsg:
333 .asciz "abortdata"
334 .align 0
335
336 /*
337 * address_exception_entry:
338 *
339 * Handler for the Address Exception exception.
340 *
341 * NOTE: This exception isn't really used on arm32. We
342 * print a warning message to the console and then treat
343 * it like a Data Abort.
344 */
345 ASENTRY_NP(address_exception_entry)
346 mrs r1, cpsr_all
347 mrs r2, spsr_all
348 mov r3, lr
349 adr r0, Laddress_exception_msg
350 bl _C_LABEL(printf) /* XXX CLOBBERS LR!! */
351 b data_abort_entry
352 Laddress_exception_msg:
353 .asciz "Address Exception CPSR=0x%08x SPSR=0x%08x LR=0x%08x\n"
354 .balign 4
355
356 /*
357 * undefined_entry:
358 *
359 * Handler for the Undefined Instruction exception.
360 *
361 * We indirect the undefined vector via the handler address
362 * in the data area. Entry to the undefined handler must
363 * look like direct entry from the vector.
364 */
365 ASENTRY_NP(undefined_entry)
366 #ifdef IPKDB
367 /*
368 * IPKDB must be hooked in at the earliest possible entry point.
369 *
370 */
371 /*
372 * Make room for all registers saving real r0-r7 and r15.
373 * The remaining registers are updated later.
374 */
375 stmfd sp!, {r0,r1} /* psr & spsr */
376 stmfd sp!, {lr} /* pc */
377 stmfd sp!, {r0-r14} /* r0-r7, r8-r14 */
378 /*
379 * Get previous psr.
380 */
381 mrs r7, cpsr_all
382 mrs r0, spsr_all
383 str r0, [sp, #(16*4)]
384 /*
385 * Test for user mode.
386 */
387 tst r0, #0xf
388 bne .Lprenotuser_push
389 add r1, sp, #(8*4)
390 stmia r1,{r8-r14}^ /* store user mode r8-r14*/
391 b .Lgoipkdb
392 /*
393 * Switch to previous mode to get r8-r13.
394 */
395 .Lprenotuser_push:
396 orr r0, r0, #(I32_bit) /* disable interrupts */
397 msr cpsr_all, r0
398 mov r1, r8
399 mov r2, r9
400 mov r3, r10
401 mov r4, r11
402 mov r5, r12
403 mov r6, r13
404 msr cpsr_all, r7 /* back to undefined mode */
405 add r8, sp, #(8*4)
406 stmia r8, {r1-r6} /* r8-r13 */
407 /*
408 * Now back to previous mode to get r14 and spsr.
409 */
410 msr cpsr_all, r0
411 mov r1, r14
412 mrs r2, spsr
413 msr cpsr_all, r7 /* back to undefined mode */
414 str r1, [sp, #(14*4)] /* r14 */
415 str r2, [sp, #(17*4)] /* spsr */
416 /*
417 * Now to IPKDB.
418 */
419 .Lgoipkdb:
420 #if defined(COMPAT_15) && defined(EXEC_AOUT)
421 ENABLE_ALIGNMENT_FAULTS
422 #endif
423 mov r0, sp
424 bl _C_LABEL(ipkdb_trap_glue)
425 ldr r1, .Lipkdb_trap_return
426 str r0,[r1]
427
428 #if defined(COMPAT_15) && defined(EXEC_AOUT)
429 #ifdef MULTIPROCESSOR
430 ldr r7, .Lcpu_info
431 bl _C_LABEL(cpu_number)
432 ldr r7, [r7, r0, lsl #2]
433 ldr r1, [r7, #CI_CURPCB]
434 #else
435 ldr r6, .Lcurpcb
436 ldr r7, .Lcpu_info_store
437 ldr r1, [r6]
438 #endif
439 cmp r1, #0x00 /* curpcb NULL? */
440 ldrne r1, [r1, #PCB_FLAGS] /* Fetch curpcb->pcb_flags */
441 tstne r1, #PCB_NOALIGNFLT
442 beq 1f /* Keep alignment faults enabled */
443 ldr r1, [r7, #CI_CTRL] /* Fetch control register */
444 ldr r2, .Lcpufuncs
445 mov r0, #-1
446 bic r1, r1, #CPU_CONTROL_AFLT_ENABLE /* Disable alignment faults */
447 mov lr, pc
448 ldr pc, [r2, #CF_CONTROL] /* Set the new control register value */
449 1:
450 #endif
451
452 /*
453 * Have to load all registers from the stack.
454 *
455 * Start with spsr and pc.
456 */
457 ldr r0, [sp, #(16*4)] /* spsr */
458 ldr r1, [sp, #(15*4)] /* r15 */
459 msr spsr_all, r0
460 mov r14, r1
461 /*
462 * Test for user mode.
463 */
464 tst r0, #0xf
465 bne .Lprenotuser_pull
466 add r1, sp, #(8*4)
467 ldmia r1, {r8-r14}^ /* load user mode r8-r14 */
468 b .Lpull_r0r7
469 .Lprenotuser_pull:
470 /*
471 * Now previous mode spsr and r14.
472 */
473 ldr r1, [sp, #(17*4)] /* spsr */
474 ldr r2, [sp, #(14*4)] /* r14 */
475 orr r0, r0, #(I32_bit)
476 msr cpsr_all, r0 /* switch to previous mode */
477 msr spsr_all, r1
478 mov r14, r2
479 msr cpsr_all, r7 /* back to undefined mode */
480 /*
481 * Now r8-r13.
482 */
483 add r8, sp, #(8*4)
484 ldmia r8, {r1-r6} /* r8-r13 */
485 msr cpsr_all, r0
486 mov r8, r1
487 mov r9, r2
488 mov r10, r3
489 mov r11, r4
490 mov r12, r5
491 mov r13, r6
492 msr cpsr_all, r7
493 .Lpull_r0r7:
494 /*
495 * Now the rest of the registers.
496 */
497 ldr r1,Lipkdb_trap_return
498 ldr r0,[r1]
499 tst r0,r0
500 ldmfd sp!, {r0-r7} /* r0-r7 */
501 add sp, sp, #(10*4) /* adjust sp */
502
503 /*
504 * Did IPKDB handle it?
505 */
506 movnes pc, lr /* return */
507
508 #endif
509 stmfd sp!, {r0, r1}
510 ldr r0, Lundefined_handler_indirection
511 ldr r1, [sp], #0x0004
512 str r1, [r0, #0x0000]
513 ldr r1, [sp], #0x0004
514 str r1, [r0, #0x0004]
515 ldmia r0, {r0, r1, pc}
516
517 #ifdef IPKDB
518 Lipkdb_trap_return:
519 .word Lipkdb_trap_return_data
520 #endif
521
522 Lundefined_handler_indirection:
523 .word Lundefined_handler_indirection_data
524
525 /*
526 * assembly bounce code for calling the kernel
527 * undefined instruction handler. This uses
528 * a standard trap frame and is called in SVC mode.
529 */
530
531 ENTRY_NP(undefinedinstruction_bounce)
532 PUSHFRAMEINSVC
533 #if defined(COMPAT_15) && defined(EXEC_AOUT)
534 and r0, r0, #(PSR_MODE) /* Test for USR32 mode (r0 = spsr_all)*/
535 teq r0, #(PSR_USR32_MODE)
536 bne 99f /* Not USR mode so skip AFLT check */
537 ENABLE_ALIGNMENT_FAULTS
538 99:
539 #endif
540 mov r0, sp
541 bl _C_LABEL(undefinedinstruction)
542
543 b exception_exit
544
545 .data
546 .align 0
547
548 #ifdef IPKDB
549 Lipkdb_trap_return_data:
550 .word 0
551 #endif
552
553 /*
554 * Indirection data
555 * 2 words use for preserving r0 and r1
556 * 3rd word contains the undefined handler address.
557 */
558
559 Lundefined_handler_indirection_data:
560 .word 0
561 .word 0
562
563 .global _C_LABEL(undefined_handler_address)
564 _C_LABEL(undefined_handler_address):
565 .word _C_LABEL(undefinedinstruction_bounce)
566