2 * Copyright 2006, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
5 * Copyright 2003, Travis Geiselbrecht. All rights reserved.
6 * Distributed under the terms of the NewOS License.
10 /* General exception handling concept:
12 The PPC architecture specifies entry point offsets for the various
13 exceptions in the first two physical pages. We put a short piece of code
14 (VEC_ENTRY()) into each exception vector. It calls exception_vector_common,
15 which is defined in the unused space at the beginning of the first physical
16 page. It re-enables address translation and calls ppc_exception_tail which
17 lies in the kernel. It dumps an iframe and invokes ppc_exception_entry()
18 (arch_int.cpp), which handles the exception and returns eventually.
19 The registers are restored from the iframe and we return from the
25 * exception_vector_common
28 - ppc_exception_entry()
29 - restore registers and return from interrupt
31 Here we use the following SPRG registers, which are at the disposal of the
33 * SPRG0: Physical address pointer to a struct cpu_exception_context
34 for the current CPU. The structure contains helpful pointers
35 as well as some scratch memory for temporarily saving registers.
38 struct cpu_exception_context (defined in arch_int.h):
39 offset 0: virtual address of the exception handler routine in the kernel
40 offset 4: virtual address of the exception context
41 offset 8: kernel stack for the current thread
42 offset 12: start of scratch memory for saving registers etc.
47 - save r1 in SPRG1 and load cpu_exception_context into r1
48 - save r0, save LR in r0
49 * exception_vector_common
52 . r1: exception context (physical address)
55 - load virtual exception context address to r1
56 - turn on BAT for exception vector code
57 - turn on address translation
58 - get exception vector offset from LR
61 . r1: exception context (virtual address)
62 . r3: exception vector offset
65 - get kernel stack pointer
67 - ppc_exception_entry()
68 - restore registers and return from interrupt
72 /* exception vector definitions */
74 /* code in each exception vector */
76 mtsprg1 %r1 ; /* temporarily save r1 in SPRG1 */ \
77 mfsprg0 %r1 ; /* ppc_cpu_exception_context* -> r1 */ \
78 stw %r0, 16(%r1) ; /* save r0 */ \
79 mflr %r0 ; /* save LR in r0 */ \
80 bl exception_vector_common ; /* continue with the common part */
82 /* defines an exception vector */
83 #define DEFINE_VECTOR(offset, name) \
84 .skip offset - (. - __irqvec_start); \
89 .global __irqvec_start
93 /* Called by the exception vector code.
94 * LR: Points to the end of the exception vector code we're coming from.
96 * r1: ppc_cpu_exception_context* (physical address)
99 exception_vector_common:
100 stw %r0, 20(%r1) /* save original LR */
101 stw %r2, 24(%r1) /* save r2 */
102 stw %r3, 28(%r1) /* save r3 */
104 /* load the virtual address of the ppc_cpu_exception_context for this CPU */
107 /* Address translation is turned off. We map this code via BAT, turn on
108 address translation, and continue in the kernel proper. */
109 li %r0, 0x10|0x2 /* BATL_MC | BATL_PP_RW */
110 mtibatl 0, %r0 /* load lower word of the instruction BAT */
111 li %r0, 0x2 /* BEPI = 0, BL = 0 (128 KB), BATU_VS */
112 mtibatu 0, %r0 /* load upper word of the instruction BAT */
116 /* turn on address translation */
117 mfsrr1 %r0 /* load saved msr */
118 rlwinm %r0, %r0, 28, 30, 31 /* extract mmu bits */
119 mfmsr %r3 /* load the current msr */
120 rlwimi %r3, %r0, 4, 26, 27 /* merge the mmu bits with the current msr */
122 rlwimi %r3, %r0, 13, 18, 18 /* turn on FPU, too */
123 mtmsr %r3 /* load new msr (turning the mmu back on) */
126 /* Get LR -- it points to the end of the exception vector code. We adjust it
127 to point to the beginning and can use it to identify the vector later. */
129 subi %r3, %r3, 20 /* 5 instructions */
131 /* jump to kernel code (ppc_exception_tail) */
137 DEFINE_VECTOR(0x100, system_reset_exception)
138 DEFINE_VECTOR(0x200, machine_check_exception)
139 DEFINE_VECTOR(0x300, DSI_exception)
140 DEFINE_VECTOR(0x400, ISI_exception)
141 DEFINE_VECTOR(0x500, external_interrupt_exception)
142 DEFINE_VECTOR(0x600, alignment_exception)
143 DEFINE_VECTOR(0x700, program_exception)
144 DEFINE_VECTOR(0x800, FP_unavailable_exception)
145 DEFINE_VECTOR(0x900, decrementer_exception)
146 DEFINE_VECTOR(0xc00, system_call_exception)
147 DEFINE_VECTOR(0xd00, trace_exception)
148 DEFINE_VECTOR(0xe00, FP_assist_exception)
149 DEFINE_VECTOR(0xf00, perf_monitor_exception)
150 DEFINE_VECTOR(0xf20, altivec_unavailable_exception)
151 DEFINE_VECTOR(0x1000, ITLB_miss_exception)
152 DEFINE_VECTOR(0x1100, DTLB_miss_on_load_exception)
153 DEFINE_VECTOR(0x1200, DTLB_miss_on_store_exception)
154 DEFINE_VECTOR(0x1300, instruction_address_breakpoint_exception)
155 DEFINE_VECTOR(0x1400, system_management_exception)
156 DEFINE_VECTOR(0x1600, altivec_assist_exception)
157 DEFINE_VECTOR(0x1700, thermal_management_exception)
163 /* This is where exception_vector_common continues. We're in the kernel here.
164 r1: ppc_cpu_exception_context* (virtual address)
165 r3: exception vector offset
168 FUNCTION(ppc_exception_tail):
179 mfsrr1 %r2 /* load saved msr */
180 andi. %r2, %r2, (1 << 14) /* see if it was in kernel mode */
181 beq .kernel /* yep */
183 /* We come from userland. Load the kernel stack top address for the current
194 /* now r2 points to the ppc_cpu_exception_context, r1 to the kernel stack */
195 /* restore the CR, it was messed up in the previous compare */
198 /* align r1 to 8 bytes, so the iframe will be aligned too */
199 rlwinm %r1, %r1, 0, 0, 28
201 /* save the registers */
204 /* iframe pointer to r4 and a backup to r20 */
208 /* adjust the stack pointer for ABI compatibility */
209 subi %r1, %r1, 8 /* make sure there's space for the previous
210 frame pointer and the return address */
211 rlwinm %r1, %r1, 0, 0, 27 /* 16 byte align the stack pointer */
213 stw %r0, 0(%r1) /* previous frame pointer: NULL */
214 /* 4(%r1) is room for the return address to be filled in by the
217 /* r3: exception vector offset
218 r4: iframe pointer */
219 bl ppc_exception_entry
221 /* move the iframe to r1 */
224 b __restore_regs_and_rfi
227 /* called by ppc_exception_tail
228 * register expectations:
230 * r2: ppc_cpu_exception_context*
232 * r0,r3, LR: scrambled, but saved in scratch memory
233 * all other regs should have been unmodified by the exception handler,
234 * and ready to be saved
237 /* Note: The iframe must be 8 byte aligned. The stack pointer we are passed
238 in r1 is aligned. So we store the floating point registers first and
239 need to take care that an even number of 4 byte registers is stored,
240 or insert padding respectively. */
277 lwz %r0, 16(%r2) /* original r0 */
278 stwu %r0, -4(%r1) /* push r0 */
279 mfsprg1 %r0 /* original r1 */
280 stwu %r0, -4(%r1) /* push r1 */
281 lwz %r0, 24(%r2) /* original r2 */
282 stwu %r0, -4(%r1) /* push r2 */
283 lwz %r0, 28(%r2) /* original r3 */
284 stwu %r0, -4(%r1) /* push r3 */
316 /* save some of the other regs */
318 stfsu %f0, -4(%r1) /* push FPSCR */
320 stwu %r0, -4(%r1) /* push CTR */
322 stwu %r0, -4(%r1) /* push XER */
324 stwu %r0, -4(%r1) /* push CR */
325 lwz %r0, 20(%r2) /* original LR */
326 stwu %r0, -4(%r1) /* push LR */
328 stwu %r0, -4(%r1) /* push DSISR */
330 stwu %r0, -4(%r1) /* push DAR */
332 stwu %r0, -4(%r1) /* push SRR1 */
334 stwu %r0, -4(%r1) /* push SRR0 */
336 stwu %r3, -4(%r1) /* exception vector offset */
341 /* called at the tail end of each of the exceptions
344 __restore_regs_and_rfi:
345 lwzu %r0, 4(%r1) /* SRR0 (skip vector offset) */
347 lwzu %r0, 4(%r1) /* SRR1 */
349 lwzu %r0, 4(%r1) /* DAR */
351 lwzu %r0, 4(%r1) /* DSISR */
353 lwzu %r0, 4(%r1) /* LR */
355 lwzu %r0, 4(%r1) /* CR */
357 lwzu %r0, 4(%r1) /* XER */
359 lwzu %r0, 4(%r1) /* CTR */
361 lfsu %f0, 4(%r1) /* FPSCR */
394 /* Stop here, before we overwrite r1, and continue with the floating point
396 addi %r2, %r1, 16 /* skip r3-r0 */
437 /* return from interrupt */