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