xref: /plan9/sys/src/9/teg2/lexception.s (revision 3de6a9c0b3d5cf34fc4090d0bf1930d83799a7fd)
1/*
2 * arm exception handlers
3 */
4#include "arm.s"
5
6#undef B					/* B is for 'botch' */
7
8/*
9 *  exception vectors, copied by trapinit() to somewhere useful
10 */
11TEXT vectors(SB), 1, $-4
12	MOVW	0x18(R15), R15		/* reset */
13	MOVW	0x18(R15), R15		/* undefined instr. */
14	MOVW	0x18(R15), R15		/* SWI & SMC */
15	MOVW	0x18(R15), R15		/* prefetch abort */
16	MOVW	0x18(R15), R15		/* data abort */
17	MOVW	0x18(R15), R15		/* hypervisor call */
18	MOVW	0x18(R15), R15		/* IRQ */
19	MOVW	0x18(R15), R15		/* FIQ */
20
21TEXT vtable(SB), 1, $-4
22	WORD	$_vrst-KZERO(SB)	/* reset, in svc mode already */
23	WORD	$_vund(SB)		/* undefined, switch to svc mode */
24	WORD	$_vsvc(SB)		/* swi, in svc mode already */
25	WORD	$_vpabt(SB)		/* prefetch abort, switch to svc mode */
26	WORD	$_vdabt(SB)		/* data abort, switch to svc mode */
27	WORD	$_vhype(SB)		/* hypervisor call */
28	WORD	$_virq(SB)		/* IRQ, switch to svc mode */
29	WORD	$_vfiq(SB)		/* FIQ, switch to svc mode */
30
31/*
32 * reset - start additional cpus
33 */
34TEXT _vrst(SB), 1, $-4
35	/* running in the zero segment (pc is lower 256MB) */
36	CPSMODE(PsrMsvc)		/* should be redundant */
37	CPSID
38	CPSAE
39	SETEND(0)			/* force little-endian */
40	BARRIERS
41	SETZSB
42	MOVW	$PsrMsvc, SPSR
43	MOVW	$0, R14
44
45	/* invalidate i-cache and branch-target cache */
46	MTCP	CpSC, 0, PC, C(CpCACHE), C(CpCACHEinvi), CpCACHEall
47	BARRIERS
48
49	BL	cpureset(SB)
50spin:
51	B	spin
52
53/*
54 * system call
55 */
56TEXT _vsvc(SB), 1, $-4			/* SWI */
57	CLREX
58	BARRIERS
59	/* stack is m->stack */
60	MOVW.W	R14, -4(R13)		/* ureg->pc = interrupted PC */
61	MOVW	SPSR, R14		/* ureg->psr = SPSR */
62	MOVW.W	R14, -4(R13)		/* ... */
63	MOVW	$PsrMsvc, R14		/* ureg->type = PsrMsvc */
64	MOVW.W	R14, -4(R13)		/* ... */
65
66	/* avoid the ambiguity described in notes/movm.w. */
67	MOVM.DB.S [R0-R14], (R13)	/* save user level registers */
68	SUB	$(NREGS*4), R13		/* r13 now points to ureg */
69
70	MOVW	$setR12(SB), R12	/* Make sure we've got the kernel's SB loaded */
71
72	/*
73	 * set up m and up registers since user registers could contain anything
74	 */
75	CPUID(R1)
76	SLL	$2, R1			/* convert to word index */
77	MOVW	$machaddr(SB), R2
78	ADD	R1, R2
79	MOVW	(R2), R(MACH)		/* m = machaddr[cpuid] */
80	CMP	$0, R(MACH)
81	MOVW.EQ	$MACHADDR, R0		/* paranoia: use MACHADDR if 0 */
82	MOVW	8(R(MACH)), R(USER)	/* up = m->proc */
83
84	MOVW	((NREGS+1)*4)(R13), R2	/* saved SPSR (user mode) */
85
86	MOVW	R13, R0			/* first arg is pointer to ureg */
87	SUB	$8, R13			/* space for argument+link */
88
89	BL	syscall(SB)
90	/*
91	 * caller saves on plan 9, so registers other than 9, 10, 13 & 14
92	 * may have been trashed when we get here.
93	 */
94
95	MOVW	$setR12(SB), R12	/* reload kernel's SB */
96
97	ADD	$(8+4*NREGS), R13	/* make r13 point to ureg->type */
98
99	MOVW	8(R13), R14		/* restore link */
100	MOVW	4(R13), R0		/* restore SPSR */
101/*
102 * return from user-mode exception.
103 * expects new SPSR in R0.  R13 must point to ureg->type.
104 */
105_rfue:
106TEXT rfue(SB), 1, $-4
107	MOVW	R0, SPSR		/* ... */
108
109	/*
110	 * order on stack is type, psr, pc, but RFEV7 needs pc, psr.
111	 * step on type and previous word to hold temporary values.
112	 * we could instead change the order in which psr & pc are pushed.
113	 */
114	MOVW	4(R13), R1		/* psr */
115	MOVW	8(R13), R2		/* pc */
116	MOVW	R2, 4(R13)		/* pc */
117	MOVW	R1, 8(R13)		/* psr */
118
119	MOVM.DB.S (R13), [R0-R14]	/* restore user registers */
120	ADD	$4, R13			/* pop type, sp -> pc */
121	RFEV7W(13)
122
123
124TEXT _vund(SB), 1, $-4			/* undefined */
125	/* sp is m->sund */
126	MOVM.IA	[R0-R4], (R13)		/* free some working space */
127	MOVW	$PsrMund, R0
128	B	_vswitch
129
130TEXT _vpabt(SB), 1, $-4			/* prefetch abort */
131	/* sp is m->sabt */
132	MOVM.IA	[R0-R4], (R13)		/* free some working space */
133	MOVW	$PsrMabt, R0		/* r0 = type */
134	B	_vswitch
135
136TEXT _vdabt(SB), 1, $-4			/* data abort */
137	/* sp is m->sabt */
138	MOVM.IA	[R0-R4], (R13)		/* free some working space */
139	MOVW	$(PsrMabt+1), R0	/* r0 = type */
140	B	_vswitch
141
142TEXT _virq(SB), 1, $-4			/* IRQ */
143	/* sp is m->sirq */
144	MOVM.IA	[R0-R4], (R13)		/* free some working space */
145	MOVW	$PsrMirq, R0		/* r0 = type */
146	B	_vswitch
147
148	/*
149	 *  come here with type in R0 and R13 pointing above saved [r0-r4].
150	 *  we'll switch to SVC mode and then call trap.
151	 */
152_vswitch:
153// TEXT _vswtch(SB), 1, $-4		/* make symbol visible to debuggers */
154	CLREX
155	BARRIERS
156	MOVW	SPSR, R1		/* save SPSR for ureg */
157	/*
158	 * R12 needs to be set before using PsrMbz, so BIGENDCHECK code has
159	 * been moved below.
160	 */
161	MOVW	R14, R2			/* save interrupted pc for ureg */
162	MOVW	R13, R3			/* save pointer to where the original [R0-R4] are */
163
164	/*
165	 * switch processor to svc mode.  this switches the banked registers
166	 * (r13 [sp] and r14 [link]) to those of svc mode (so we must be sure
167	 * to never get here already in svc mode).
168	 */
169	CPSMODE(PsrMsvc)		/* switch! */
170	CPSID
171
172	AND.S	$0xf, R1, R4		/* interrupted code kernel or user? */
173	BEQ	_userexcep
174
175	/*
176	 * here for trap from SVC mode
177	 */
178
179	/* push ureg->{type, psr, pc} onto Msvc stack.
180	 * r13 points to ureg->type after.
181	 */
182	MOVM.DB.W [R0-R2], (R13)
183	MOVM.IA	  (R3), [R0-R4]		/* restore [R0-R4] from previous mode's stack */
184
185	/*
186	 * avoid the ambiguity described in notes/movm.w.
187	 * In order to get a predictable value in R13 after the stores,
188	 * separate the store-multiple from the stack-pointer adjustment.
189	 * We'll assume that the old value of R13 should be stored on the stack.
190	 */
191	/* save kernel level registers, at end r13 points to ureg */
192	MOVM.DB	[R0-R14], (R13)
193	SUB	$(NREGS*4), R13		/* SP now points to saved R0 */
194
195	MOVW	$setR12(SB), R12	/* Make sure we've got the kernel's SB loaded */
196	/* previous mode was svc, so the saved spsr should be sane. */
197	MOVW	((NREGS+1)*4)(R13), R1
198
199	MOVM.IA	(R13), [R0-R8]		/* restore a few user registers */
200
201	MOVW	R13, R0			/* first arg is pointer to ureg */
202	SUB	$(4*2), R13		/* space for argument+link (for debugger) */
203	MOVW	$0xdeaddead, R11	/* marker */
204
205	BL	trap(SB)		/* trap(ureg) */
206	/*
207	 * caller saves on plan 9, so registers other than 9, 10, 13 & 14
208	 * may have been trashed when we get here.
209	 */
210
211	MOVW	$setR12(SB), R12	/* reload kernel's SB */
212
213	ADD	$(4*2+4*NREGS), R13	/* make r13 point to ureg->type */
214
215	/*
216	 * if we interrupted a previous trap's handler and are now
217	 * returning to it, we need to propagate the current R(MACH) (R10)
218	 * by overriding the saved one on the stack, since we may have
219	 * been rescheduled and be on a different processor now than
220	 * at entry.
221	 */
222	MOVW	R(MACH), (-(NREGS-MACH)*4)(R13) /* restore current cpu's MACH */
223
224	MOVW	8(R13), R14		/* restore link */
225	MOVW	4(R13), R0		/* restore SPSR */
226
227	/* return from kernel-mode exception */
228	MOVW	R0, SPSR		/* ... */
229
230	/*
231	 * order on stack is type, psr, pc, but RFEV7 needs pc, psr.
232	 * step on type and previous word to hold temporary values.
233	 * we could instead change the order in which psr & pc are pushed.
234	 */
235	MOVW	4(R13), R1		/* psr */
236	MOVW	8(R13), R2		/* pc */
237	MOVW	R2, 4(R13)		/* pc */
238	MOVW	R1, 8(R13)		/* psr */
239
240	/* restore kernel regs other than SP; we're using it */
241	SUB	$(NREGS*4), R13
242	MOVM.IA.W (R13), [R0-R12]
243	ADD	$4, R13			/* skip saved kernel SP */
244	MOVM.IA.W (R13), [R14]
245	ADD	$4, R13			/* pop type, sp -> pc */
246	BARRIERS
247	RFEV7W(13)
248
249	/*
250	 * here for trap from USER mode
251	 */
252_userexcep:
253	MOVM.DB.W [R0-R2], (R13)	/* set ureg->{type, psr, pc}; r13 points to ureg->type  */
254	MOVM.IA	  (R3), [R0-R4]		/* restore [R0-R4] from previous mode's stack */
255
256	/* avoid the ambiguity described in notes/movm.w. */
257	MOVM.DB.S [R0-R14], (R13)	/* save kernel level registers */
258	SUB	$(NREGS*4), R13		/* r13 now points to ureg */
259
260	MOVW	$setR12(SB), R12	/* Make sure we've got the kernel's SB loaded */
261
262	/*
263	 * set up m and up registers since user registers could contain anything
264	 */
265	CPUID(R1)
266	SLL	$2, R1			/* convert to word index */
267	MOVW	$machaddr(SB), R2
268	ADD	R1, R2
269	MOVW	(R2), R(MACH)		/* m = machaddr[cpuid] */
270	CMP	$0, R(MACH)
271	MOVW.EQ	$MACHADDR, R0		/* paranoia: use MACHADDR if 0 */
272	MOVW	8(R(MACH)), R(USER)	/* up = m->proc */
273
274	MOVW	((NREGS+1)*4)(R13), R2	/* saved SPSR */
275
276	MOVW	R13, R0			/* first arg is pointer to ureg */
277	SUB	$(4*2), R13		/* space for argument+link (for debugger) */
278
279	BL	trap(SB)		/* trap(ureg) */
280	/*
281	 * caller saves on plan 9, so registers other than 9, 10, 13 & 14
282	 * may have been trashed when we get here.
283	 */
284
285	ADD	$(4*2+4*NREGS), R13	/* make r13 point to ureg->type */
286
287	MOVW	8(R13), R14		/* restore link */
288	MOVW	4(R13), R0		/* restore SPSR */
289
290	MOVW	4(R13), R0		/* restore SPSR */
291	B	_rfue
292
293
294TEXT _vfiq(SB), 1, $-4			/* FIQ */
295	PUTC('?')
296	PUTC('f')
297	PUTC('i')
298	PUTC('q')
299	RFE				/* FIQ is special, ignore it for now */
300
301TEXT _vhype(SB), 1, $-4
302	PUTC('?')
303	PUTC('h')
304	PUTC('y')
305	PUTC('p')
306	RFE
307
308/*
309 *  set the stack value for the mode passed in R0
310 */
311TEXT setr13(SB), 1, $-4
312	MOVW	4(FP), R1
313
314	MOVW	CPSR, R2
315	BIC	$(PsrMask|PsrMbz), R2, R3
316	ORR	$(PsrDirq|PsrDfiq), R3
317	ORR	R0, R3
318
319	MOVW	R3, CPSR		/* switch to new mode */
320
321	MOVW	R13, R0			/* return old sp */
322	MOVW	R1, R13			/* install new one */
323
324	MOVW	R2, CPSR		/* switch back to old mode */
325	RET
326