1/*
2 * Copyright 2006, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 *
5 * Copyright 2003, Travis Geiselbrecht. All rights reserved.
6 * Distributed under the terms of the NewOS License.
7 */
8#define FUNCTION(x) .global x; .type x,@function; x
9#define LOCAL_FUNCTION(x) .type x,@function; x
10
11/*	General exception handling concept:
12
13	The PPC architecture specifies entry point offsets for the various
14	exceptions in the first two physical pages. We put a short piece of code
15	(VEC_ENTRY()) into each exception vector. It calls exception_vector_common,
16	which is defined in the unused space at the beginning of the first physical
17	page. It re-enables address translation and calls ppc_exception_tail which
18	lies in the kernel. It dumps an iframe and invokes ppc_exception_entry()
19	(arch_int.cpp), which handles the exception and returns eventually.
20	The registers are restored from the iframe and we return from the
21	interrupt.
22
23	algorithm overview:
24
25	* VEC_ENTRY
26	* exception_vector_common
27	* ppc_exception_tail
28		- dump iframe
29		- ppc_exception_entry()
30		- restore registers and return from interrupt
31
32	Here we use the following SPRG registers, which are at the disposal of the
33	operating system:
34	* SPRG0: Physical address pointer to a struct cpu_exception_context
35			 for the current CPU. The structure contains helpful pointers
36			 as well as some scratch memory for temporarily saving registers.
37	* SPRG1: Scratch.
38
39	struct cpu_exception_context (defined in arch_int.h):
40	offset 0:  virtual address of the exception handler routine in the kernel
41	offset 4:  virtual address of the exception context
42	offset 8:  kernel stack for the current thread
43	offset 12: start of scratch memory for saving registers etc.
44
45	algorithm in detail:
46
47	* VEC_ENTRY
48		- save r1 in SPRG1 and load cpu_exception_context into r1
49		- save r0, save LR in r0
50	* exception_vector_common
51		- params:
52			. r0: old LR
53			. r1: exception context (physical address)
54			. SPRG1: original r1
55		- save r0-3
56		- load virtual exception context address to r1
57		- turn on BAT for exception vector code
58		- turn on address translation
59		- get exception vector offset from LR
60	* ppc_exception_tail
61		- params:
62			. r1: exception context (virtual address)
63			. r3: exception vector offset
64			. SPRG1: original r1
65		- turn off BAT
66		- get kernel stack pointer
67		- dump iframe
68		- ppc_exception_entry()
69		- restore registers and return from interrupt
70 */
71
72
73/* exception vector definitions */
74
75/* code in each exception vector */
76#define VEC_ENTRY() \
77	mtsprg1	%r1					; /* temporarily save r1 in SPRG1 */		\
78	mfsprg0	%r1					; /* ppc_cpu_exception_context* -> r1 */	\
79	stw		%r0, 16(%r1)		; /* save r0 */								\
80	mflr	%r0					; /* save LR in r0 */						\
81	bl		exception_vector_common	; /* continue with the common part */
82
83/* defines an exception vector */
84#define DEFINE_VECTOR(offset, name) 	\
85.skip	offset - (. - __irqvec_start);	\
86FUNCTION(name):							\
87	VEC_ENTRY()
88
89
90.global __irqvec_start
91__irqvec_start:
92	.long	0
93
94/* Called by the exception vector code.
95 * LR:    Points to the end of the exception vector code we're coming from.
96 * r0:    original LR
97 * r1:    ppc_cpu_exception_context* (physical address)
98 * SPRG1: original r1
99 */
100exception_vector_common:
101	stw		%r0, 20(%r1)			/* save original LR */
102	stw		%r2, 24(%r1)			/* save r2 */
103	stw		%r3, 28(%r1)			/* save r3 */
104
105	/* load the virtual address of the ppc_cpu_exception_context for this CPU */
106	lwz		%r1, 4(%r1)
107
108	/* Address translation is turned off. We map this code via BAT, turn on
109	   address translation, and continue in the kernel proper. */
110	li		%r0, 0x10|0x2			/* BATL_MC | BATL_PP_RW */
111	mtibatl	0, %r0					/* load lower word of the instruction BAT */
112	li		%r0, 0x2				/* BEPI = 0, BL = 0 (128 KB), BATU_VS */
113	mtibatu	0, %r0					/* load upper word of the instruction BAT */
114	isync
115	sync
116
117	/* turn on address translation */
118	mfsrr1	%r0						/* load saved msr */
119	rlwinm	%r0, %r0, 28, 30, 31	/* extract mmu bits */
120	mfmsr	%r3						/* load the current msr */
121	rlwimi  %r3, %r0, 4, 26, 27		/* merge the mmu bits with the current msr */
122	li		%r0, 1
123	rlwimi  %r3, %r0, 13, 18, 18	/* turn on FPU, too */
124	mtmsr	%r3						/* load new msr (turning the mmu back on) */
125	isync
126
127	/* Get LR -- it points to the end of the exception vector code. We adjust it
128	   to point to the beginning and can use it to identify the vector later. */
129	mflr	%r3
130	subi	%r3, %r3, 20		/* 5 instructions */
131
132	/* jump to kernel code (ppc_exception_tail) */
133	lwz		%r2, 0(%r1)
134	mtlr	%r2
135	blr
136
137
138DEFINE_VECTOR(0x100, system_reset_exception)
139DEFINE_VECTOR(0x200, machine_check_exception)
140DEFINE_VECTOR(0x300, DSI_exception)
141DEFINE_VECTOR(0x400, ISI_exception)
142DEFINE_VECTOR(0x500, external_interrupt_exception)
143DEFINE_VECTOR(0x600, alignment_exception)
144DEFINE_VECTOR(0x700, program_exception)
145DEFINE_VECTOR(0x800, FP_unavailable_exception)
146DEFINE_VECTOR(0x900, decrementer_exception)
147DEFINE_VECTOR(0xc00, system_call_exception)
148DEFINE_VECTOR(0xd00, trace_exception)
149DEFINE_VECTOR(0xe00, FP_assist_exception)
150DEFINE_VECTOR(0xf00, perf_monitor_exception)
151DEFINE_VECTOR(0xf20, altivec_unavailable_exception)
152DEFINE_VECTOR(0x1000, ITLB_miss_exception)
153DEFINE_VECTOR(0x1100, DTLB_miss_on_load_exception)
154DEFINE_VECTOR(0x1200, DTLB_miss_on_store_exception)
155DEFINE_VECTOR(0x1300, instruction_address_breakpoint_exception)
156DEFINE_VECTOR(0x1400, system_management_exception)
157DEFINE_VECTOR(0x1600, altivec_assist_exception)
158DEFINE_VECTOR(0x1700, thermal_management_exception)
159
160.global __irqvec_end
161__irqvec_end:
162
163
164/* This is where exception_vector_common continues. We're in the kernel here.
165   r1:    ppc_cpu_exception_context* (virtual address)
166   r3:    exception vector offset
167   SPRG1: original r1
168 */
169FUNCTION(ppc_exception_tail):
170	/* turn off BAT */
171	li		%r2, 0
172	mtibatu	0, %r2
173	mtibatl	0, %r2
174	isync
175	sync
176
177	/* save CR */
178	mfcr	%r0
179
180	mfsrr1	%r2					/* load saved msr */
181	andi.	%r2, %r2, (1 << 14)	/* see if it was in kernel mode */
182	beq		.kernel				/* yep */
183
184	/* We come from userland. Load the kernel stack top address for the current
185	   userland thread. */
186	mr		%r2, %r1
187	lwz		%r1, 8(%r1)
188	b		.restore_stack_end
189
190.kernel:
191	mr		%r2, %r1
192	mfsprg1	%r1
193
194.restore_stack_end:
195	/* now r2 points to the ppc_cpu_exception_context, r1 to the kernel stack */
196	/* restore the CR, it was messed up in the previous compare */
197	mtcrf	0xff, %r0
198
199	/* align r1 to 8 bytes, so the iframe will be aligned too */
200	rlwinm	%r1, %r1, 0, 0, 28
201
202	/* save the registers */
203	bl		__save_regs
204
205	/* iframe pointer to r4 and a backup to r20 */
206	mr		%r4, %r1
207	mr		%r20, %r1
208
209	/* adjust the stack pointer for ABI compatibility */
210	subi	%r1, %r1, 8				/* make sure there's space for the previous
211									   frame pointer and the return address */
212	rlwinm	%r1, %r1, 0, 0, 27		/* 16 byte align the stack pointer */
213	li		%r0, 0
214	stw		%r0, 0(%r1)				/* previous frame pointer: NULL */
215		/* 4(%r1) is room for the return address to be filled in by the
216		   called function. */
217
218	/* r3: exception vector offset
219	   r4: iframe pointer */
220	bl 		ppc_exception_entry
221
222	/* move the iframe to r1 */
223	mr		%r1, %r20
224
225	b		__restore_regs_and_rfi
226
227
228/* called by ppc_exception_tail
229 * register expectations:
230 *  r1:        stack
231 *  r2:        ppc_cpu_exception_context*
232 *  SPRG1:     original r1
233 *  r0,r3, LR: scrambled, but saved in scratch memory
234 * all other regs should have been unmodified by the exception handler,
235 * and ready to be saved
236 */
237__save_regs:
238	/* Note: The iframe must be 8 byte aligned. The stack pointer we are passed
239	   in r1 is aligned. So we store the floating point registers first and
240	   need to take care that an even number of 4 byte registers is stored,
241	   or insert padding respectively. */
242
243	/* push f0-f31 */
244	stfdu	%f0, -8(%r1)
245	stfdu	%f1, -8(%r1)
246	stfdu	%f2, -8(%r1)
247	stfdu	%f3, -8(%r1)
248	stfdu	%f4, -8(%r1)
249	stfdu	%f5, -8(%r1)
250	stfdu	%f6, -8(%r1)
251	stfdu	%f7, -8(%r1)
252	stfdu	%f8, -8(%r1)
253	stfdu	%f9, -8(%r1)
254	stfdu	%f10, -8(%r1)
255	stfdu	%f11, -8(%r1)
256	stfdu	%f12, -8(%r1)
257	stfdu	%f13, -8(%r1)
258	stfdu	%f14, -8(%r1)
259	stfdu	%f15, -8(%r1)
260	stfdu	%f16, -8(%r1)
261	stfdu	%f17, -8(%r1)
262	stfdu	%f18, -8(%r1)
263	stfdu	%f19, -8(%r1)
264	stfdu	%f20, -8(%r1)
265	stfdu	%f21, -8(%r1)
266	stfdu	%f22, -8(%r1)
267	stfdu	%f23, -8(%r1)
268	stfdu	%f24, -8(%r1)
269	stfdu	%f25, -8(%r1)
270	stfdu	%f26, -8(%r1)
271	stfdu	%f27, -8(%r1)
272	stfdu	%f28, -8(%r1)
273	stfdu	%f29, -8(%r1)
274	stfdu	%f30, -8(%r1)
275	stfdu	%f31, -8(%r1)
276
277	/* push r0-r3 */
278	lwz		%r0, 16(%r2)		/* original r0 */
279	stwu	%r0, -4(%r1)		/* push r0 */
280	mfsprg1	%r0					/* original r1 */
281	stwu	%r0, -4(%r1)		/* push r1 */
282	lwz		%r0, 24(%r2)		/* original r2 */
283	stwu	%r0, -4(%r1)		/* push r2 */
284	lwz		%r0, 28(%r2)		/* original r3 */
285	stwu	%r0, -4(%r1)		/* push r3 */
286
287	/* push r4-r31 */
288	stwu	%r4, -4(%r1)
289	stwu	%r5, -4(%r1)
290	stwu	%r6, -4(%r1)
291	stwu	%r7, -4(%r1)
292	stwu	%r8, -4(%r1)
293	stwu	%r9, -4(%r1)
294	stwu	%r10, -4(%r1)
295	stwu	%r11, -4(%r1)
296	stwu	%r12, -4(%r1)
297	stwu	%r13, -4(%r1)
298	stwu	%r14, -4(%r1)
299	stwu	%r15, -4(%r1)
300	stwu	%r16, -4(%r1)
301	stwu	%r17, -4(%r1)
302	stwu	%r18, -4(%r1)
303	stwu	%r19, -4(%r1)
304	stwu	%r20, -4(%r1)
305	stwu	%r21, -4(%r1)
306	stwu	%r22, -4(%r1)
307	stwu	%r23, -4(%r1)
308	stwu	%r24, -4(%r1)
309	stwu	%r25, -4(%r1)
310	stwu	%r26, -4(%r1)
311	stwu	%r27, -4(%r1)
312	stwu	%r28, -4(%r1)
313	stwu	%r29, -4(%r1)
314	stwu	%r30, -4(%r1)
315	stwu	%r31, -4(%r1)
316
317	/* save some of the other regs */
318	mffs	%f0
319	stfsu	%f0, -4(%r1)		/* push FPSCR */
320	mfctr	%r0
321	stwu	%r0, -4(%r1)		/* push CTR */
322	mfxer	%r0
323	stwu	%r0, -4(%r1)		/* push XER */
324	mfcr	%r0
325	stwu	%r0, -4(%r1)		/* push CR */
326	lwz		%r0, 20(%r2)		/* original LR */
327	stwu	%r0, -4(%r1)		/* push LR */
328	mfspr	%r0, %dsisr
329	stwu	%r0, -4(%r1)		/* push DSISR */
330	mfspr	%r0, %dar
331	stwu	%r0, -4(%r1)		/* push DAR */
332	mfspr	%r0, %srr1
333	stwu	%r0, -4(%r1)		/* push SRR1 */
334	mfspr	%r0, %srr0
335	stwu	%r0, -4(%r1)		/* push SRR0 */
336
337	stwu	%r3, -4(%r1)		/* exception vector offset */
338
339	blr
340
341
342/* called at the tail end of each of the exceptions
343 * r1: iframe pointer
344 */
345__restore_regs_and_rfi:
346	lwzu	%r0, 4(%r1)		/* SRR0 (skip vector offset) */
347	mtspr	%srr0, %r0
348	lwzu	%r0, 4(%r1)		/* SRR1 */
349	mtspr	%srr1, %r0
350	lwzu	%r0, 4(%r1)		/* DAR */
351	mtspr	%dar, %r0
352	lwzu	%r0, 4(%r1)		/* DSISR */
353	mtspr	%dsisr, %r0
354	lwzu	%r0, 4(%r1)		/* LR */
355	mtlr	%r0
356	lwzu	%r0, 4(%r1)		/* CR */
357	mtcr	%r0
358	lwzu	%r0, 4(%r1)		/* XER */
359	mtxer	%r0
360	lwzu	%r0, 4(%r1)		/* CTR */
361	mtctr	%r0
362	lfsu	%f0, 4(%r1)		/* FPSCR */
363	mtfsf	0xff, %f0
364
365	lwzu	%r31, 4(%r1)
366	lwzu	%r30, 4(%r1)
367	lwzu	%r29, 4(%r1)
368	lwzu	%r28, 4(%r1)
369	lwzu	%r27, 4(%r1)
370	lwzu	%r26, 4(%r1)
371	lwzu	%r25, 4(%r1)
372	lwzu	%r24, 4(%r1)
373	lwzu	%r23, 4(%r1)
374	lwzu	%r22, 4(%r1)
375	lwzu	%r21, 4(%r1)
376	lwzu	%r20, 4(%r1)
377	lwzu	%r19, 4(%r1)
378	lwzu	%r18, 4(%r1)
379	lwzu	%r17, 4(%r1)
380	lwzu	%r16, 4(%r1)
381	lwzu	%r15, 4(%r1)
382	lwzu	%r14, 4(%r1)
383	lwzu	%r13, 4(%r1)
384	lwzu	%r12, 4(%r1)
385	lwzu	%r11, 4(%r1)
386	lwzu	%r10, 4(%r1)
387	lwzu	%r9, 4(%r1)
388	lwzu	%r8, 4(%r1)
389	lwzu	%r7, 4(%r1)
390	lwzu	%r6, 4(%r1)
391	lwzu	%r5, 4(%r1)
392	lwzu	%r4, 4(%r1)
393	lwzu	%r3, 4(%r1)
394
395	/* Stop here, before we overwrite r1, and continue with the floating point
396	   registers first. */
397	addi	%r2, %r1, 16		/* skip r3-r0 */
398
399	/* f31-f0 */
400	lfd		%f31, 0(%r2)
401	lfdu	%f30, 8(%r2)
402	lfdu	%f29, 8(%r2)
403	lfdu	%f28, 8(%r2)
404	lfdu	%f27, 8(%r2)
405	lfdu	%f26, 8(%r2)
406	lfdu	%f25, 8(%r2)
407	lfdu	%f24, 8(%r2)
408	lfdu	%f23, 8(%r2)
409	lfdu	%f22, 8(%r2)
410	lfdu	%f21, 8(%r2)
411	lfdu	%f20, 8(%r2)
412	lfdu	%f19, 8(%r2)
413	lfdu	%f18, 8(%r2)
414	lfdu	%f17, 8(%r2)
415	lfdu	%f16, 8(%r2)
416	lfdu	%f15, 8(%r2)
417	lfdu	%f14, 8(%r2)
418	lfdu	%f13, 8(%r2)
419	lfdu	%f12, 8(%r2)
420	lfdu	%f11, 8(%r2)
421	lfdu	%f10, 8(%r2)
422	lfdu	%f9, 8(%r2)
423	lfdu	%f8, 8(%r2)
424	lfdu	%f7, 8(%r2)
425	lfdu	%f6, 8(%r2)
426	lfdu	%f5, 8(%r2)
427	lfdu	%f4, 8(%r2)
428	lfdu	%f3, 8(%r2)
429	lfdu	%f2, 8(%r2)
430	lfdu	%f1, 8(%r2)
431	lfd		%f0, 8(%r2)
432
433	/* r2-r0 */
434	lwzu	%r2, 4(%r1)
435	lwz		%r0, 8(%r1)
436	lwz		%r1, 4(%r1)
437
438	/* return from interrupt */
439	rfi
440