xref: /plan9-contrib/sys/src/9/bcm/lexception.s (revision 5c47fe09a0cc86dfb02c0ea4a2b6aec7eda2361f)
15d9de2d3SDavid du Colombier/*
25d9de2d3SDavid du Colombier * arm exception handlers
35d9de2d3SDavid du Colombier */
45d9de2d3SDavid du Colombier#include "arm.s"
55d9de2d3SDavid du Colombier
65d9de2d3SDavid du Colombier/*
75d9de2d3SDavid du Colombier *  exception vectors, copied by trapinit() to somewhere useful
85d9de2d3SDavid du Colombier */
95d9de2d3SDavid du ColombierTEXT vectors(SB), 1, $-4
105d9de2d3SDavid du Colombier	MOVW	0x18(R15), R15		/* reset */
115d9de2d3SDavid du Colombier	MOVW	0x18(R15), R15		/* undefined instr. */
125d9de2d3SDavid du Colombier	MOVW	0x18(R15), R15		/* SWI & SMC */
135d9de2d3SDavid du Colombier	MOVW	0x18(R15), R15		/* prefetch abort */
145d9de2d3SDavid du Colombier	MOVW	0x18(R15), R15		/* data abort */
155d9de2d3SDavid du Colombier	MOVW	0x18(R15), R15		/* reserved */
165d9de2d3SDavid du Colombier	MOVW	0x18(R15), R15		/* IRQ */
175d9de2d3SDavid du Colombier	MOVW	0x18(R15), R15		/* FIQ */
185d9de2d3SDavid du Colombier
195d9de2d3SDavid du ColombierTEXT vtable(SB), 1, $-4
205d9de2d3SDavid du Colombier	WORD	$_vsvc(SB)		/* reset, in svc mode already */
215d9de2d3SDavid du Colombier	WORD	$_vund(SB)		/* undefined, switch to svc mode */
225d9de2d3SDavid du Colombier	WORD	$_vsvc(SB)		/* swi, in svc mode already */
235d9de2d3SDavid du Colombier	WORD	$_vpabt(SB)		/* prefetch abort, switch to svc mode */
245d9de2d3SDavid du Colombier	WORD	$_vdabt(SB)		/* data abort, switch to svc mode */
255d9de2d3SDavid du Colombier	WORD	$_vsvc(SB)		/* reserved */
265d9de2d3SDavid du Colombier	WORD	$_virq(SB)		/* IRQ, switch to svc mode */
275d9de2d3SDavid du Colombier	WORD	$_vfiq(SB)		/* FIQ, switch to svc mode */
285d9de2d3SDavid du Colombier
295d9de2d3SDavid du ColombierTEXT _vsvc(SB), 1, $-4			/* SWI */
30*5c47fe09SDavid du Colombier	CLREX
315d9de2d3SDavid du Colombier	MOVW.W	R14, -4(R13)		/* ureg->pc = interrupted PC */
325d9de2d3SDavid du Colombier	MOVW	SPSR, R14		/* ureg->psr = SPSR */
335d9de2d3SDavid du Colombier	MOVW.W	R14, -4(R13)		/* ... */
345d9de2d3SDavid du Colombier	MOVW	$PsrMsvc, R14		/* ureg->type = PsrMsvc */
355d9de2d3SDavid du Colombier	MOVW.W	R14, -4(R13)		/* ... */
365d9de2d3SDavid du Colombier
375d9de2d3SDavid du Colombier	/* avoid the ambiguity described in notes/movm.w. */
385d9de2d3SDavid du Colombier	MOVM.DB.S [R0-R14], (R13)	/* save user level registers */
395d9de2d3SDavid du Colombier	SUB	$(15*4), R13		/* r13 now points to ureg */
405d9de2d3SDavid du Colombier
415d9de2d3SDavid du Colombier	MOVW	$setR12(SB), R12	/* Make sure we've got the kernel's SB loaded */
425d9de2d3SDavid du Colombier
43*5c47fe09SDavid du Colombier	/* get R(MACH) for this cpu */
44*5c47fe09SDavid du Colombier	CPUID(R1)
45*5c47fe09SDavid du Colombier	SLL	$2, R1			/* convert to word index */
46*5c47fe09SDavid du Colombier	MOVW	$machaddr(SB), R2
47*5c47fe09SDavid du Colombier	ADD	R1, R2
48*5c47fe09SDavid du Colombier	MOVW	(R2), R(MACH)		/* m = machaddr[cpuid] */
49*5c47fe09SDavid du Colombier	CMP	$0, R(MACH)
50*5c47fe09SDavid du Colombier	MOVW.EQ	$MACHADDR, R0		/* paranoia: use MACHADDR if 0 */
51*5c47fe09SDavid du Colombier
52*5c47fe09SDavid du Colombier	MOVW	8(R(MACH)), R(USER)		/* up */
535d9de2d3SDavid du Colombier
545d9de2d3SDavid du Colombier	MOVW	R13, R0			/* first arg is pointer to ureg */
555d9de2d3SDavid du Colombier	SUB	$8, R13			/* space for argument+link */
565d9de2d3SDavid du Colombier
575d9de2d3SDavid du Colombier	BL	syscall(SB)
585d9de2d3SDavid du Colombier
595d9de2d3SDavid du Colombier	ADD	$(8+4*15), R13		/* make r13 point to ureg->type */
605d9de2d3SDavid du Colombier	MOVW	8(R13), R14		/* restore link */
615d9de2d3SDavid du Colombier	MOVW	4(R13), R0		/* restore SPSR */
625d9de2d3SDavid du Colombier	MOVW	R0, SPSR		/* ... */
635d9de2d3SDavid du Colombier	MOVM.DB.S (R13), [R0-R14]	/* restore registers */
645d9de2d3SDavid du Colombier	ADD	$8, R13			/* pop past ureg->{type+psr} */
655d9de2d3SDavid du Colombier	RFE				/* MOVM.IA.S.W (R13), [R15] */
665d9de2d3SDavid du Colombier
675d9de2d3SDavid du ColombierTEXT _vund(SB), 1, $-4			/* undefined */
685d9de2d3SDavid du Colombier	MOVM.IA	[R0-R4], (R13)		/* free some working space */
695d9de2d3SDavid du Colombier	MOVW	$PsrMund, R0
705d9de2d3SDavid du Colombier	B	_vswitch
715d9de2d3SDavid du Colombier
725d9de2d3SDavid du ColombierTEXT _vpabt(SB), 1, $-4			/* prefetch abort */
735d9de2d3SDavid du Colombier	MOVM.IA	[R0-R4], (R13)		/* free some working space */
745d9de2d3SDavid du Colombier	MOVW	$PsrMabt, R0		/* r0 = type */
755d9de2d3SDavid du Colombier	B	_vswitch
765d9de2d3SDavid du Colombier
775d9de2d3SDavid du ColombierTEXT _vdabt(SB), 1, $-4			/* data abort */
785d9de2d3SDavid du Colombier	MOVM.IA	[R0-R4], (R13)		/* free some working space */
795d9de2d3SDavid du Colombier	MOVW	$(PsrMabt+1), R0	/* r0 = type */
805d9de2d3SDavid du Colombier	B	_vswitch
815d9de2d3SDavid du Colombier
825d9de2d3SDavid du ColombierTEXT _virq(SB), 1, $-4			/* IRQ */
835d9de2d3SDavid du Colombier	MOVM.IA	[R0-R4], (R13)		/* free some working space */
845d9de2d3SDavid du Colombier	MOVW	$PsrMirq, R0		/* r0 = type */
855d9de2d3SDavid du Colombier	B	_vswitch
865d9de2d3SDavid du Colombier
875d9de2d3SDavid du Colombier	/*
885d9de2d3SDavid du Colombier	 *  come here with type in R0 and R13 pointing above saved [r0-r4].
895d9de2d3SDavid du Colombier	 *  we'll switch to SVC mode and then call trap.
905d9de2d3SDavid du Colombier	 */
915d9de2d3SDavid du Colombier_vswitch:
92*5c47fe09SDavid du Colombier	CLREX
935d9de2d3SDavid du Colombier	MOVW	SPSR, R1		/* save SPSR for ureg */
945d9de2d3SDavid du Colombier	MOVW	R14, R2			/* save interrupted pc for ureg */
955d9de2d3SDavid du Colombier	MOVW	R13, R3			/* save pointer to where the original [R0-R4] are */
965d9de2d3SDavid du Colombier
975d9de2d3SDavid du Colombier	/*
985d9de2d3SDavid du Colombier	 * switch processor to svc mode.  this switches the banked registers
995d9de2d3SDavid du Colombier	 * (r13 [sp] and r14 [link]) to those of svc mode.
1005d9de2d3SDavid du Colombier	 */
1015d9de2d3SDavid du Colombier	MOVW	CPSR, R14
1025d9de2d3SDavid du Colombier	BIC	$PsrMask, R14
1035d9de2d3SDavid du Colombier	ORR	$(PsrDirq|PsrMsvc), R14
1045d9de2d3SDavid du Colombier	MOVW	R14, CPSR		/* switch! */
1055d9de2d3SDavid du Colombier
1065d9de2d3SDavid du Colombier	AND.S	$0xf, R1, R4		/* interrupted code kernel or user? */
1075d9de2d3SDavid du Colombier	BEQ	_userexcep
1085d9de2d3SDavid du Colombier
1095d9de2d3SDavid du Colombier	/* here for trap from SVC mode */
1105d9de2d3SDavid du Colombier	MOVM.DB.W [R0-R2], (R13)	/* set ureg->{type, psr, pc}; r13 points to ureg->type  */
1115d9de2d3SDavid du Colombier	MOVM.IA	  (R3), [R0-R4]		/* restore [R0-R4] from previous mode's stack */
1125d9de2d3SDavid du Colombier
1135d9de2d3SDavid du Colombier	/*
1145d9de2d3SDavid du Colombier	 * avoid the ambiguity described in notes/movm.w.
1155d9de2d3SDavid du Colombier	 * In order to get a predictable value in R13 after the stores,
1165d9de2d3SDavid du Colombier	 * separate the store-multiple from the stack-pointer adjustment.
1175d9de2d3SDavid du Colombier	 * We'll assume that the old value of R13 should be stored on the stack.
1185d9de2d3SDavid du Colombier	 */
1195d9de2d3SDavid du Colombier	/* save kernel level registers, at end r13 points to ureg */
1205d9de2d3SDavid du Colombier	MOVM.DB	[R0-R14], (R13)
1215d9de2d3SDavid du Colombier	SUB	$(15*4), R13		/* SP now points to saved R0 */
1225d9de2d3SDavid du Colombier
1235d9de2d3SDavid du Colombier	MOVW	$setR12(SB), R12	/* Make sure we've got the kernel's SB loaded */
1245d9de2d3SDavid du Colombier
1255d9de2d3SDavid du Colombier	MOVW	R13, R0			/* first arg is pointer to ureg */
1265d9de2d3SDavid du Colombier	SUB	$(4*2), R13		/* space for argument+link (for debugger) */
1275d9de2d3SDavid du Colombier	MOVW	$0xdeaddead, R11	/* marker */
1285d9de2d3SDavid du Colombier
1295d9de2d3SDavid du Colombier	BL	trap(SB)
1305d9de2d3SDavid du Colombier
131*5c47fe09SDavid du Colombier	MOVW	$setR12(SB), R12	/* reload kernel's SB (ORLY?) */
1325d9de2d3SDavid du Colombier	ADD	$(4*2+4*15), R13	/* make r13 point to ureg->type */
133*5c47fe09SDavid du Colombier	/*
134*5c47fe09SDavid du Colombier	 * if we interrupted a previous trap's handler and are now
135*5c47fe09SDavid du Colombier	 * returning to it, we need to propagate the current R(MACH) (R10)
136*5c47fe09SDavid du Colombier	 * by overriding the saved one on the stack, since we may have
137*5c47fe09SDavid du Colombier	 * been rescheduled and be on a different processor now than
138*5c47fe09SDavid du Colombier	 * at entry.
139*5c47fe09SDavid du Colombier	 */
140*5c47fe09SDavid du Colombier	MOVW	R(MACH), (-(15-MACH)*4)(R13) /* restore current cpu's MACH */
1415d9de2d3SDavid du Colombier	MOVW	8(R13), R14		/* restore link */
1425d9de2d3SDavid du Colombier	MOVW	4(R13), R0		/* restore SPSR */
1435d9de2d3SDavid du Colombier	MOVW	R0, SPSR		/* ... */
1445d9de2d3SDavid du Colombier
1455d9de2d3SDavid du Colombier	MOVM.DB (R13), [R0-R14]		/* restore registers */
1465d9de2d3SDavid du Colombier
1475d9de2d3SDavid du Colombier	ADD	$(4*2), R13		/* pop past ureg->{type+psr} to pc */
1485d9de2d3SDavid du Colombier	RFE				/* MOVM.IA.S.W (R13), [R15] */
1495d9de2d3SDavid du Colombier
1505d9de2d3SDavid du Colombier	/* here for trap from USER mode */
1515d9de2d3SDavid du Colombier_userexcep:
1525d9de2d3SDavid du Colombier	MOVM.DB.W [R0-R2], (R13)	/* set ureg->{type, psr, pc}; r13 points to ureg->type  */
1535d9de2d3SDavid du Colombier	MOVM.IA	  (R3), [R0-R4]		/* restore [R0-R4] from previous mode's stack */
1545d9de2d3SDavid du Colombier
1555d9de2d3SDavid du Colombier	/* avoid the ambiguity described in notes/movm.w. */
1565d9de2d3SDavid du Colombier	MOVM.DB.S [R0-R14], (R13)	/* save kernel level registers */
1575d9de2d3SDavid du Colombier	SUB	$(15*4), R13		/* r13 now points to ureg */
1585d9de2d3SDavid du Colombier
1595d9de2d3SDavid du Colombier	MOVW	$setR12(SB), R12	/* Make sure we've got the kernel's SB loaded */
1605d9de2d3SDavid du Colombier
161*5c47fe09SDavid du Colombier	/* get R(MACH) for this cpu */
162*5c47fe09SDavid du Colombier	CPUID(R1)
163*5c47fe09SDavid du Colombier	SLL	$2, R1			/* convert to word index */
164*5c47fe09SDavid du Colombier	MOVW	$machaddr(SB), R2
165*5c47fe09SDavid du Colombier	ADD	R1, R2
166*5c47fe09SDavid du Colombier	MOVW	(R2), R(MACH)		/* m = machaddr[cpuid] */
167*5c47fe09SDavid du Colombier	CMP	$0, R(MACH)
168*5c47fe09SDavid du Colombier	MOVW.EQ	$MACHADDR, R(MACH)		/* paranoia: use MACHADDR if 0 */
169*5c47fe09SDavid du Colombier
170*5c47fe09SDavid du Colombier	MOVW	8(R(MACH)), R(USER)		/* up */
1715d9de2d3SDavid du Colombier
1725d9de2d3SDavid du Colombier	MOVW	R13, R0			/* first arg is pointer to ureg */
1735d9de2d3SDavid du Colombier	SUB	$(4*2), R13		/* space for argument+link (for debugger) */
1745d9de2d3SDavid du Colombier
1755d9de2d3SDavid du Colombier	BL	trap(SB)
1765d9de2d3SDavid du Colombier
1775d9de2d3SDavid du Colombier	ADD	$(4*2+4*15), R13	/* make r13 point to ureg->type */
1785d9de2d3SDavid du Colombier	MOVW	8(R13), R14		/* restore link */
1795d9de2d3SDavid du Colombier	MOVW	4(R13), R0		/* restore SPSR */
1805d9de2d3SDavid du Colombier	MOVW	R0, SPSR		/* ... */
1815d9de2d3SDavid du Colombier	MOVM.DB.S (R13), [R0-R14]	/* restore registers */
1825d9de2d3SDavid du Colombier	ADD	$(4*2), R13		/* pop past ureg->{type+psr} */
1835d9de2d3SDavid du Colombier	RFE				/* MOVM.IA.S.W (R13), [R15] */
1845d9de2d3SDavid du Colombier
1855d9de2d3SDavid du ColombierTEXT _vfiq(SB), 1, $-4			/* FIQ */
186*5c47fe09SDavid du Colombier	CLREX
1875d9de2d3SDavid du Colombier	MOVW	$PsrMfiq, R8		/* trap type */
1885d9de2d3SDavid du Colombier	MOVW	SPSR, R9		/* interrupted psr */
1895d9de2d3SDavid du Colombier	MOVW	R14, R10		/* interrupted pc */
1905d9de2d3SDavid du Colombier	MOVM.DB.W [R8-R10], (R13)	/* save in ureg */
191*5c47fe09SDavid du Colombier	MOVM.DB.S [R0-R14], (R13)	/* save interrupted regs */
192*5c47fe09SDavid du Colombier	SUB	$(15*4), R13
1935d9de2d3SDavid du Colombier	MOVW	$setR12(SB), R12	/* Make sure we've got the kernel's SB loaded */
194*5c47fe09SDavid du Colombier	/* get R(MACH) for this cpu */
195*5c47fe09SDavid du Colombier	CPUID(R1)
196*5c47fe09SDavid du Colombier	SLL	$2, R1			/* convert to word index */
197*5c47fe09SDavid du Colombier	MOVW	$machaddr(SB), R2
198*5c47fe09SDavid du Colombier	ADD	R1, R2
199*5c47fe09SDavid du Colombier	MOVW	(R2), R(MACH)		/* m = machaddr[cpuid] */
200*5c47fe09SDavid du Colombier	CMP	$0, R(MACH)
201*5c47fe09SDavid du Colombier	MOVW.EQ	$MACHADDR, R(MACH)		/* paranoia: use MACHADDR if 0 */
202*5c47fe09SDavid du Colombier
203*5c47fe09SDavid du Colombier	MOVW	8(R(MACH)), R(USER)		/* up */
2045d9de2d3SDavid du Colombier	MOVW	R13, R0			/* first arg is pointer to ureg */
2055d9de2d3SDavid du Colombier	SUB	$(4*2), R13		/* space for argument+link (for debugger) */
2065d9de2d3SDavid du Colombier
2075d9de2d3SDavid du Colombier	BL	fiq(SB)
2085d9de2d3SDavid du Colombier
2095d9de2d3SDavid du Colombier	ADD	$(8+4*15), R13		/* make r13 point to ureg->type */
2105d9de2d3SDavid du Colombier	MOVW	8(R13), R14		/* restore link */
2115d9de2d3SDavid du Colombier	MOVW	4(R13), R0		/* restore SPSR */
2125d9de2d3SDavid du Colombier	MOVW	R0, SPSR		/* ... */
2135d9de2d3SDavid du Colombier	MOVM.DB.S (R13), [R0-R14]	/* restore registers */
2145d9de2d3SDavid du Colombier	ADD	$8, R13			/* pop past ureg->{type+psr} */
2155d9de2d3SDavid du Colombier	RFE				/* MOVM.IA.S.W (R13), [R15] */
2165d9de2d3SDavid du Colombier
2175d9de2d3SDavid du Colombier/*
2185d9de2d3SDavid du Colombier *  set the stack value for the mode passed in R0
2195d9de2d3SDavid du Colombier */
2205d9de2d3SDavid du ColombierTEXT setr13(SB), 1, $-4
2215d9de2d3SDavid du Colombier	MOVW	4(FP), R1
2225d9de2d3SDavid du Colombier
2235d9de2d3SDavid du Colombier	MOVW	CPSR, R2
2245d9de2d3SDavid du Colombier	BIC	$PsrMask, R2, R3
225*5c47fe09SDavid du Colombier	ORR	$(PsrDirq|PsrDfiq), R3
2265d9de2d3SDavid du Colombier	ORR	R0, R3
2275d9de2d3SDavid du Colombier	MOVW	R3, CPSR		/* switch to new mode */
2285d9de2d3SDavid du Colombier
2295d9de2d3SDavid du Colombier	MOVW	R13, R0			/* return old sp */
2305d9de2d3SDavid du Colombier	MOVW	R1, R13			/* install new one */
2315d9de2d3SDavid du Colombier
2325d9de2d3SDavid du Colombier	MOVW	R2, CPSR		/* switch back to old mode */
2335d9de2d3SDavid du Colombier	RET
234