xref: /plan9/sys/src/9/bcm/trap.c (revision 8153b942127462338e00e239914941524d579a3b)
1 /*
2  * traps, exceptions, interrupts, system calls.
3  */
4 #include "u.h"
5 #include "../port/lib.h"
6 #include "mem.h"
7 #include "dat.h"
8 #include "fns.h"
9 #include "io.h"
10 #include "ureg.h"
11 #include "../port/error.h"
12 
13 #include "arm.h"
14 
15 #define INTREGS		(VIRTIO+0xB200)
16 
17 typedef struct Intregs Intregs;
18 typedef struct Vctl Vctl;
19 
20 enum {
21 	Debug = 0,
22 
23 	Nvec = 8,		/* # of vectors at start of lexception.s */
24 	Fiqenable = 1<<7,
25 };
26 
27 /*
28  *   Layout at virtual address KZERO (double mapped at HVECTORS).
29  */
30 typedef struct Vpage0 {
31 	void	(*vectors[Nvec])(void);
32 	u32int	vtable[Nvec];
33 } Vpage0;
34 
35 /*
36  * interrupt control registers
37  */
38 struct Intregs {
39 	u32int	ARMpending;
40 	u32int	GPUpending[2];
41 	u32int	FIQctl;
42 	u32int	GPUenable[2];
43 	u32int	ARMenable;
44 	u32int	GPUdisable[2];
45 	u32int	ARMdisable;
46 };
47 
48 struct Vctl {
49 	Vctl	*next;
50 	int	irq;
51 	u32int	*reg;
52 	u32int	mask;
53 	void	(*f)(Ureg*, void*);
54 	void	*a;
55 };
56 
57 static Vctl *vctl, *vfiq;
58 
59 static char *trapnames[PsrMask+1] = {
60 	[ PsrMusr ] "user mode",
61 	[ PsrMfiq ] "fiq interrupt",
62 	[ PsrMirq ] "irq interrupt",
63 	[ PsrMsvc ] "svc/swi exception",
64 	[ PsrMabt ] "prefetch abort/data abort",
65 	[ PsrMabt+1 ] "data abort",
66 	[ PsrMund ] "undefined instruction",
67 	[ PsrMsys ] "sys trap",
68 };
69 
70 extern int notify(Ureg*);
71 
72 /*
73  *  set up for exceptions
74  */
75 void
trapinit(void)76 trapinit(void)
77 {
78 	Vpage0 *vpage0;
79 
80 	/* disable everything */
81 	intrsoff();
82 
83 	/* set up the exception vectors */
84 	vpage0 = (Vpage0*)HVECTORS;
85 	memmove(vpage0->vectors, vectors, sizeof(vpage0->vectors));
86 	memmove(vpage0->vtable, vtable, sizeof(vpage0->vtable));
87 	cacheuwbinv();
88 
89 	/* set up the stacks for the interrupt modes */
90 	setr13(PsrMfiq, (u32int*)(FIQSTKTOP));
91 	setr13(PsrMirq, m->sirq);
92 	setr13(PsrMabt, m->sabt);
93 	setr13(PsrMund, m->sund);
94 	setr13(PsrMsys, m->ssys);
95 
96 	coherence();
97 }
98 
99 void
intrsoff(void)100 intrsoff(void)
101 {
102 	Intregs *ip;
103 	int disable;
104 
105 	ip = (Intregs*)INTREGS;
106 	disable = ~0;
107 	ip->GPUdisable[0] = disable;
108 	ip->GPUdisable[1] = disable;
109 	ip->ARMdisable = disable;
110 	ip->FIQctl = 0;
111 }
112 
113 /*
114  *  called by trap to handle irq interrupts.
115  *  returns true iff a clock interrupt, thus maybe reschedule.
116  */
117 static int
irq(Ureg * ureg)118 irq(Ureg* ureg)
119 {
120 	Vctl *v;
121 	int clockintr;
122 
123 	clockintr = 0;
124 	for(v = vctl; v; v = v->next)
125 		if(*v->reg & v->mask){
126 			coherence();
127 			v->f(ureg, v->a);
128 			coherence();
129 			if(v->irq == IRQclock)
130 				clockintr = 1;
131 		}
132 	return clockintr;
133 }
134 
135 /*
136  * called direct from lexception.s to handle fiq interrupt.
137  */
138 void
fiq(Ureg * ureg)139 fiq(Ureg *ureg)
140 {
141 	Vctl *v;
142 
143 	v = vfiq;
144 	if(v == nil)
145 		panic("unexpected item in bagging area");
146 	m->intr++;
147 	ureg->pc -= 4;
148 	coherence();
149 	v->f(ureg, v->a);
150 	coherence();
151 }
152 
153 void
irqenable(int irq,void (* f)(Ureg *,void *),void * a)154 irqenable(int irq, void (*f)(Ureg*, void*), void* a)
155 {
156 	Vctl *v;
157 	Intregs *ip;
158 	u32int *enable;
159 
160 	ip = (Intregs*)INTREGS;
161 	v = (Vctl*)malloc(sizeof(Vctl));
162 	if(v == nil)
163 		panic("irqenable: no mem");
164 	v->irq = irq;
165 	if(irq >= IRQbasic){
166 		enable = &ip->ARMenable;
167 		v->reg = &ip->ARMpending;
168 		v->mask = 1 << (irq - IRQbasic);
169 	}else{
170 		enable = &ip->GPUenable[irq/32];
171 		v->reg = &ip->GPUpending[irq/32];
172 		v->mask = 1 << (irq % 32);
173 	}
174 	v->f = f;
175 	v->a = a;
176 	if(irq == IRQfiq){
177 		assert((ip->FIQctl & Fiqenable) == 0);
178 		assert((*enable & v->mask) == 0);
179 		vfiq = v;
180 		ip->FIQctl = Fiqenable | irq;
181 	}else{
182 		v->next = vctl;
183 		vctl = v;
184 		*enable = v->mask;
185 	}
186 }
187 
188 static char *
trapname(int psr)189 trapname(int psr)
190 {
191 	char *s;
192 
193 	s = trapnames[psr & PsrMask];
194 	if(s == nil)
195 		s = "unknown trap number in psr";
196 	return s;
197 }
198 
199 /* this is quite helpful during mmu and cache debugging */
200 static void
ckfaultstuck(uintptr va)201 ckfaultstuck(uintptr va)
202 {
203 	static int cnt, lastpid;
204 	static uintptr lastva;
205 
206 	if (va == lastva && up->pid == lastpid) {
207 		++cnt;
208 		if (cnt >= 2)
209 			/* fault() isn't fixing the underlying cause */
210 			panic("fault: %d consecutive faults for va %#p",
211 				cnt+1, va);
212 	} else {
213 		cnt = 0;
214 		lastva = va;
215 		lastpid = up->pid;
216 	}
217 }
218 
219 /*
220  *  called by trap to handle access faults
221  */
222 static void
faultarm(Ureg * ureg,uintptr va,int user,int read)223 faultarm(Ureg *ureg, uintptr va, int user, int read)
224 {
225 	int n, insyscall;
226 	char buf[ERRMAX];
227 
228 	if(up == nil) {
229 		dumpregs(ureg);
230 		panic("fault: nil up in faultarm, accessing %#p", va);
231 	}
232 	insyscall = up->insyscall;
233 	up->insyscall = 1;
234 	if (Debug)
235 		ckfaultstuck(va);
236 
237 	n = fault(va, read);
238 	if(n < 0){
239 		if(!user){
240 			dumpregs(ureg);
241 			panic("fault: kernel accessing %#p", va);
242 		}
243 		/* don't dump registers; programs suicide all the time */
244 		snprint(buf, sizeof buf, "sys: trap: fault %s va=%#p",
245 			read? "read": "write", va);
246 		postnote(up, 1, buf, NDebug);
247 	}
248 	up->insyscall = insyscall;
249 }
250 
251 /*
252  *  returns 1 if the instruction writes memory, 0 otherwise
253  */
254 int
writetomem(ulong inst)255 writetomem(ulong inst)
256 {
257 	/* swap always write memory */
258 	if((inst & 0x0FC00000) == 0x01000000)
259 		return 1;
260 
261 	/* loads and stores are distinguished by bit 20 */
262 	if(inst & (1<<20))
263 		return 0;
264 
265 	return 1;
266 }
267 
268 /*
269  *  here on all exceptions other than syscall (SWI) and fiq
270  */
271 void
trap(Ureg * ureg)272 trap(Ureg *ureg)
273 {
274 	int clockintr, user, x, rv, rem;
275 	ulong inst, fsr;
276 	uintptr va;
277 	char buf[ERRMAX];
278 
279 	assert(!islo());
280 	if(up != nil)
281 		rem = ((char*)ureg)-up->kstack;
282 	else
283 		rem = ((char*)ureg)-((char*)m+sizeof(Mach));
284 	if(rem < 256) {
285 		iprint("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux\n",
286 			rem, up, ureg, ureg->pc);
287 		delay(1000);
288 		dumpstack();
289 		panic("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux",
290 			rem, up, ureg, ureg->pc);
291 	}
292 
293 	user = (ureg->psr & PsrMask) == PsrMusr;
294 	if(user){
295 		up->dbgreg = ureg;
296 		cycles(&up->kentry);
297 	}
298 
299 	/*
300 	 * All interrupts/exceptions should be resumed at ureg->pc-4,
301 	 * except for Data Abort which resumes at ureg->pc-8.
302 	 */
303 	if(ureg->type == (PsrMabt+1))
304 		ureg->pc -= 8;
305 	else
306 		ureg->pc -= 4;
307 
308 	clockintr = 0;		/* if set, may call sched() before return */
309 	switch(ureg->type){
310 	default:
311 		panic("unknown trap; type %#lux, psr mode %#lux", ureg->type,
312 			ureg->psr & PsrMask);
313 		break;
314 	case PsrMirq:
315 		clockintr = irq(ureg);
316 		m->intr++;
317 		break;
318 	case PsrMabt:			/* prefetch fault */
319 		x = ifsrget();
320 		fsr = (x>>7) & 0x8 | x & 0x7;
321 		switch(fsr){
322 		case 0x02:		/* instruction debug event (BKPT) */
323 			if(user){
324 				snprint(buf, sizeof buf, "sys: breakpoint");
325 				postnote(up, 1, buf, NDebug);
326 			}else{
327 				iprint("kernel bkpt: pc %#lux inst %#ux\n",
328 					ureg->pc, *(u32int*)ureg->pc);
329 				panic("kernel bkpt");
330 			}
331 			break;
332 		default:
333 			faultarm(ureg, ureg->pc, user, 1);
334 			break;
335 		}
336 		break;
337 	case PsrMabt+1:			/* data fault */
338 		va = farget();
339 		inst = *(ulong*)(ureg->pc);
340 		/* bits 12 and 10 have to be concatenated with status */
341 		x = fsrget();
342 		fsr = (x>>7) & 0x20 | (x>>6) & 0x10 | x & 0xf;
343 		switch(fsr){
344 		default:
345 		case 0xa:		/* ? was under external abort */
346 			panic("unknown data fault, 6b fsr %#lux", fsr);
347 			break;
348 		case 0x0:
349 			panic("vector exception at %#lux", ureg->pc);
350 			break;
351 		case 0x1:		/* alignment fault */
352 		case 0x3:		/* access flag fault (section) */
353 			if(user){
354 				snprint(buf, sizeof buf,
355 					"sys: alignment: pc %#lux va %#p\n",
356 					ureg->pc, va);
357 				postnote(up, 1, buf, NDebug);
358 			} else
359 				panic("kernel alignment: pc %#lux va %#p", ureg->pc, va);
360 			break;
361 		case 0x2:
362 			panic("terminal exception at %#lux", ureg->pc);
363 			break;
364 		case 0x4:		/* icache maint fault */
365 		case 0x6:		/* access flag fault (page) */
366 		case 0x8:		/* precise external abort, non-xlat'n */
367 		case 0x28:
368 		case 0xc:		/* l1 translation, precise ext. abort */
369 		case 0x2c:
370 		case 0xe:		/* l2 translation, precise ext. abort */
371 		case 0x2e:
372 		case 0x16:		/* imprecise ext. abort, non-xlt'n */
373 		case 0x36:
374 			panic("external abort %#lux pc %#lux addr %#p",
375 				fsr, ureg->pc, va);
376 			break;
377 		case 0x1c:		/* l1 translation, precise parity err */
378 		case 0x1e:		/* l2 translation, precise parity err */
379 		case 0x18:		/* imprecise parity or ecc err */
380 			panic("translation parity error %#lux pc %#lux addr %#p",
381 				fsr, ureg->pc, va);
382 			break;
383 		case 0x5:		/* translation fault, no section entry */
384 		case 0x7:		/* translation fault, no page entry */
385 			faultarm(ureg, va, user, !writetomem(inst));
386 			break;
387 		case 0x9:
388 		case 0xb:
389 			/* domain fault, accessing something we shouldn't */
390 			if(user){
391 				snprint(buf, sizeof buf,
392 					"sys: access violation: pc %#lux va %#p\n",
393 					ureg->pc, va);
394 				postnote(up, 1, buf, NDebug);
395 			} else
396 				panic("kernel access violation: pc %#lux va %#p",
397 					ureg->pc, va);
398 			break;
399 		case 0xd:
400 		case 0xf:
401 			/* permission error, copy on write or real permission error */
402 			faultarm(ureg, va, user, !writetomem(inst));
403 			break;
404 		}
405 		break;
406 	case PsrMund:			/* undefined instruction */
407 		if(user){
408 			if(seg(up, ureg->pc, 0) != nil &&
409 			   *(u32int*)ureg->pc == 0xD1200070)
410 				postnote(up, 1, "sys: breakpoint", NDebug);
411 			else{
412 				/* look for floating point instructions to interpret */
413 				rv = fpuemu(ureg);
414 				if(rv == 0){
415 					snprint(buf, sizeof buf,
416 						"undefined instruction: pc %#lux\n",
417 						ureg->pc);
418 					postnote(up, 1, buf, NDebug);
419 				}
420 			}
421 		}else{
422 			if (ureg->pc & 3) {
423 				iprint("rounding fault pc %#lux down to word\n",
424 					ureg->pc);
425 				ureg->pc &= ~3;
426 			}
427 			iprint("undefined instruction: pc %#lux inst %#ux\n",
428 				ureg->pc, *(u32int*)ureg->pc);
429 			panic("undefined instruction");
430 		}
431 		break;
432 	}
433 	splhi();
434 
435 	/* delaysched set because we held a lock or because our quantum ended */
436 	if(up && up->delaysched && clockintr){
437 		sched();		/* can cause more traps */
438 		splhi();
439 	}
440 
441 	if(user){
442 		if(up->procctl || up->nnote)
443 			notify(ureg);
444 		kexit(ureg);
445 	}
446 }
447 
448 int
isvalidaddr(void * v)449 isvalidaddr(void *v)
450 {
451 	return (uintptr)v >= KZERO;
452 }
453 
454 static void
dumplongs(char * msg,ulong * v,int n)455 dumplongs(char *msg, ulong *v, int n)
456 {
457 	int i, l;
458 
459 	l = 0;
460 	iprint("%s at %.8p: ", msg, v);
461 	for(i=0; i<n; i++){
462 		if(l >= 4){
463 			iprint("\n    %.8p: ", v);
464 			l = 0;
465 		}
466 		if(isvalidaddr(v)){
467 			iprint(" %.8lux", *v++);
468 			l++;
469 		}else{
470 			iprint(" invalid");
471 			break;
472 		}
473 	}
474 	iprint("\n");
475 }
476 
477 static void
dumpstackwithureg(Ureg * ureg)478 dumpstackwithureg(Ureg *ureg)
479 {
480 	uintptr l, i, v, estack;
481 	u32int *p;
482 	char *s;
483 
484 	if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){
485 		iprint("dumpstack disabled\n");
486 		return;
487 	}
488 	iprint("ktrace /kernel/path %#.8lux %#.8lux %#.8lux # pc, sp, link\n",
489 		ureg->pc, ureg->sp, ureg->r14);
490 	delay(2000);
491 	i = 0;
492 	if(up != nil && (uintptr)&l <= (uintptr)up->kstack+KSTACK)
493 		estack = (uintptr)up->kstack+KSTACK;
494 	else if((uintptr)&l >= (uintptr)m->stack
495 	     && (uintptr)&l <= (uintptr)m+MACHSIZE)
496 		estack = (uintptr)m+MACHSIZE;
497 	else{
498 		if(up != nil)
499 			iprint("&up->kstack %#p &l %#p\n", up->kstack, &l);
500 		else
501 			iprint("&m %#p &l %#p\n", m, &l);
502 		return;
503 	}
504 	for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){
505 		v = *(uintptr*)l;
506 		if(KTZERO < v && v < (uintptr)etext && !(v & 3)){
507 			v -= sizeof(u32int);		/* back up an instr */
508 			p = (u32int*)v;
509 			if((*p & 0x0f000000) == 0x0b000000){	/* BL instr? */
510 				iprint("%#8.8lux=%#8.8lux ", l, v);
511 				i++;
512 			}
513 		}
514 		if(i == 4){
515 			i = 0;
516 			iprint("\n");
517 		}
518 	}
519 	if(i)
520 		iprint("\n");
521 }
522 
523 /*
524  * Fill in enough of Ureg to get a stack trace, and call a function.
525  * Used by debugging interface rdb.
526  */
527 void
callwithureg(void (* fn)(Ureg *))528 callwithureg(void (*fn)(Ureg*))
529 {
530 	Ureg ureg;
531 
532 	ureg.pc = getcallerpc(&fn);
533 	ureg.sp = PTR2UINT(&fn);
534 	fn(&ureg);
535 }
536 
537 void
dumpstack(void)538 dumpstack(void)
539 {
540 	callwithureg(dumpstackwithureg);
541 }
542 
543 void
dumpregs(Ureg * ureg)544 dumpregs(Ureg* ureg)
545 {
546 	int s;
547 
548 	if (ureg == nil) {
549 		iprint("trap: no user process\n");
550 		return;
551 	}
552 	s = splhi();
553 	iprint("trap: %s", trapname(ureg->type));
554 	if(ureg != nil && (ureg->psr & PsrMask) != PsrMsvc)
555 		iprint(" in %s", trapname(ureg->psr));
556 	iprint("\n");
557 	iprint("psr %8.8lux type %2.2lux pc %8.8lux link %8.8lux\n",
558 		ureg->psr, ureg->type, ureg->pc, ureg->link);
559 	iprint("R14 %8.8lux R13 %8.8lux R12 %8.8lux R11 %8.8lux R10 %8.8lux\n",
560 		ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
561 	iprint("R9  %8.8lux R8  %8.8lux R7  %8.8lux R6  %8.8lux R5  %8.8lux\n",
562 		ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
563 	iprint("R4  %8.8lux R3  %8.8lux R2  %8.8lux R1  %8.8lux R0  %8.8lux\n",
564 		ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
565 	iprint("stack is at %#p\n", ureg);
566 	iprint("pc %#lux link %#lux\n", ureg->pc, ureg->link);
567 
568 	if(up)
569 		iprint("user stack: %#p-%#p\n", up->kstack, up->kstack+KSTACK-4);
570 	else
571 		iprint("kernel stack: %8.8lux-%8.8lux\n",
572 			(ulong)(m+1), (ulong)m+BY2PG-4);
573 	dumplongs("stack", (ulong *)(ureg + 1), 16);
574 	delay(2000);
575 	dumpstack();
576 	splx(s);
577 }
578