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