xref: /plan9-contrib/sys/src/9/kw/trap.c (revision 178702b161d3fe3e021aa6cb2f305be898e56ca0)
1 /*
2  * sheevaplug 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 enum {
16 	Ntimevec = 20,			/* # of time buckets for each intr */
17 	Nvecs = 256,
18 };
19 
20 extern int notify(Ureg*);
21 
22 extern int ldrexvalid;
23 
24 typedef struct Vctl Vctl;
25 typedef struct Vctl {
26 	Vctl*	next;		/* handlers on this vector */
27 	char	*name;		/* of driver, xallocated */
28 	void	(*f)(Ureg*, void*);	/* handler to call */
29 	void*	a;		/* argument to call it with */
30 } Vctl;
31 
32 static Lock vctllock;
33 static Vctl* vctl[32];
34 
35 uvlong ninterrupt;
36 uvlong ninterruptticks;
37 ulong intrtimes[Nvecs][Ntimevec];
38 
39 typedef struct Handler Handler;
40 struct Handler {
41 	void	(*r)(Ureg*, void*);
42 	void	*a;
43 	char	name[KNAMELEN];
44 };
45 
46 static Handler irqlo[32];
47 static Handler irqhi[32];
48 static Handler irqbridge[32];
49 static Lock irqlock;
50 static int probing, trapped;
51 
52 typedef struct Irq Irq;
53 struct Irq {
54 	ulong	*irq;
55 	ulong	*irqmask;
56 	Handler	*irqvec;
57 	int	nirqvec;
58 	char	*name;
59 };
60 /* irq and irqmask are filled in by trapinit */
61 static Irq irqs[] = {
62 [Irqlo]		{nil, nil, irqlo,	nelem(irqlo),	"lo"},
63 [Irqhi]		{nil, nil, irqhi,	nelem(irqhi),	"hi"},
64 [Irqbridge]	{nil, nil, irqbridge,	nelem(irqbridge), "bridge"},
65 };
66 
67 /*
68  *  keep histogram of interrupt service times
69  */
70 void
71 intrtime(Mach*, int vno)
72 {
73 	ulong diff, x;
74 
75 	if (m == nil)
76 		return;
77 	x = perfticks();
78 	diff = x - m->perf.intrts;
79 	m->perf.intrts = x;
80 
81 	m->perf.inintr += diff;
82 	if(up == nil && m->perf.inidle > diff)
83 		m->perf.inidle -= diff;
84 
85 	if (m->cpuhz == 0)			/* not set yet? */
86 		return;
87 	diff /= (m->cpuhz/1000000)*100;		/* quantum = 100µsec */
88 	if(diff >= Ntimevec)
89 		diff = Ntimevec-1;
90 	assert(vno >= 0 && vno < Nvecs);
91 	intrtimes[vno][diff]++;
92 }
93 
94 void
95 intrfmtcounts(char *s, char *se)
96 {
97 	USED(s, se);
98 }
99 
100 static void
101 dumpcounts(void)
102 {
103 }
104 
105 void
106 intrclear(int sort, int v)
107 {
108 	*irqs[sort].irq = ~(1 << v);
109 }
110 
111 void
112 intrmask(int sort, int v)
113 {
114 	*irqs[sort].irqmask &= ~(1 << v);
115 }
116 
117 void
118 intrunmask(int sort, int v)
119 {
120 	*irqs[sort].irqmask |= 1 << v;
121 }
122 
123 static void
124 maskallints(void)
125 {
126 	CpucsReg *cpu = (CpucsReg *)soc.cpu;
127 	IntrReg *intr;
128 
129 	/* no fiq or ep in use */
130 	intr = (IntrReg *)soc.intr;
131 	intr->lo.irqmask = 0;
132 	intr->hi.irqmask = 0;
133 	cpu->irqmask = 0;
134 	coherence();
135 }
136 
137 void
138 intrset(Handler *h, void (*f)(Ureg*, void*), void *a, char *name)
139 {
140 	if(h->r != nil) {
141 //		iprint("duplicate irq: %s (%#p)\n", h->name, h->r);
142 		return;
143 	}
144 	h->r = f;
145 	h->a = a;
146 	strncpy(h->name, name, KNAMELEN-1);
147 	h->name[KNAMELEN-1] = 0;
148 }
149 
150 void
151 intrunset(Handler *h)
152 {
153 	h->r = nil;
154 	h->a = nil;
155 	h->name[0] = 0;
156 }
157 
158 void
159 intrdel(Handler *h, void (*f)(Ureg*, void*), void *a, char *name)
160 {
161 	if(h->r != f || h->a != a || strcmp(h->name, name) != 0)
162 		return;
163 	intrunset(h);
164 }
165 
166 void
167 intrenable(int sort, int v, void (*f)(Ureg*, void*), void *a, char *name)
168 {
169 //iprint("enabling intr %d vec %d for %s\n", sort, v, name);
170 	ilock(&irqlock);
171 	intrset(&irqs[sort].irqvec[v], f, a, name);
172 	intrunmask(sort, v);
173 	iunlock(&irqlock);
174 }
175 
176 void
177 intrdisable(int sort, int v, void (*f)(Ureg*, void*), void* a, char *name)
178 {
179 	ilock(&irqlock);
180 	intrdel(&irqs[sort].irqvec[v], f, a, name);
181 	intrmask(sort, v);
182 	iunlock(&irqlock);
183 }
184 
185 /*
186  *  called by trap to handle interrupts
187  */
188 static void
189 intrs(Ureg *ur, int sort)
190 {
191 	int i, s;
192 	ulong ibits;
193 	Handler *h;
194 	Irq irq;
195 
196 	assert(sort >= 0 && sort < nelem(irqs));
197 	irq = irqs[sort];
198 	ibits = *irq.irq;
199 	ibits &= *irq.irqmask;
200 
201 	for(i = 0; i < irq.nirqvec && ibits; i++)
202 		if(ibits & (1<<i)){
203 			h = &irq.irqvec[i];
204 			if(h->r != nil){
205 				h->r(ur, h->a);
206 				splhi();
207 				intrtime(m, sort*32 + i);
208 				if (sort == Irqbridge && i == IRQcputimer0)
209 					m->inclockintr = 1;
210 				ibits &= ~(1<<i);
211 			}
212 		}
213 	if(ibits != 0) {
214 		iprint("spurious irq%s interrupt: %8.8lux\n", irq.name, ibits);
215 		s = splfhi();
216 		*irq.irq &= ibits;
217 		*irq.irqmask &= ~ibits;
218 		splx(s);
219 	}
220 }
221 
222 void
223 intrhi(Ureg *ureg, void*)
224 {
225 	intrs(ureg, Irqhi);
226 }
227 
228 void
229 intrbridge(Ureg *ureg, void*)
230 {
231 	intrs(ureg, Irqbridge);
232 	intrclear(Irqlo, IRQ0bridge);
233 }
234 
235 void
236 trapinit(void)
237 {
238 	int i;
239 	CpucsReg *cpu;
240 	IntrReg *intr;
241 	Vectorpage *page0 = (Vectorpage*)HVECTORS;
242 
243 	intr = (IntrReg *)soc.intr;
244 	cpu = (CpucsReg *)soc.cpu;
245 	irqs[Irqlo].irq = &intr->lo.irq;
246 	irqs[Irqlo].irqmask = &intr->lo.irqmask;
247 	irqs[Irqhi].irq = &intr->hi.irq;
248 	irqs[Irqhi].irqmask = &intr->hi.irqmask;
249 	irqs[Irqbridge].irq = &cpu->irq;
250 	irqs[Irqbridge].irqmask = &cpu->irqmask;
251 	coherence();
252 
253 	setr13(PsrMfiq, m->fiqstack + nelem(m->fiqstack));
254 	setr13(PsrMirq, m->irqstack + nelem(m->irqstack));
255 	setr13(PsrMabt, m->abtstack + nelem(m->abtstack));
256 	setr13(PsrMund, m->undstack + nelem(m->undstack));
257 
258 	memmove(page0->vectors, vectors, sizeof page0->vectors);
259 	memmove(page0->vtable,  vtable,  sizeof page0->vtable);
260 	cacheuwbinv();
261 	l2cacheuwbinv();
262 
263 	cpu->cpucfg &= ~Cfgvecinithi;
264 
265 	for(i = 0; i < nelem(irqlo); i++)
266 		intrunset(&irqlo[i]);
267 	for(i = 0; i < nelem(irqhi); i++)
268 		intrunset(&irqhi[i]);
269 	for(i = 0; i < nelem(irqbridge); i++)
270 		intrunset(&irqbridge[i]);
271 
272 	/* disable all interrupts */
273 	intr->lo.fiqmask = intr->hi.fiqmask = 0;
274 	intr->lo.irqmask = intr->hi.irqmask = 0;
275 	intr->lo.epmask =  intr->hi.epmask = 0;
276 	cpu->irqmask = 0;
277 	coherence();
278 
279 	/* clear interrupts */
280 	intr->lo.irq = intr->hi.irq = ~0;
281 	cpu->irq = ~0;
282 	coherence();
283 
284 	intrenable(Irqlo, IRQ0hisum, intrhi, nil, "hi");
285 	intrenable(Irqlo, IRQ0bridge, intrbridge, nil, "bridge");
286 
287 	/* enable watchdog & access-error interrupts */
288 	cpu->irqmask |= 1 << IRQcputimerwd | 1 << IRQaccesserr;
289 	coherence();
290 }
291 
292 static char *trapnames[PsrMask+1] = {
293 	[ PsrMusr ] "user mode",
294 	[ PsrMfiq ] "fiq interrupt",
295 	[ PsrMirq ] "irq interrupt",
296 	[ PsrMsvc ] "svc/swi exception",
297 	[ PsrMabt ] "prefetch abort/data abort",
298 	[ PsrMabt+1 ] "data abort",
299 	[ PsrMund ] "undefined instruction",
300 	[ PsrMsys ] "sys trap",
301 };
302 
303 static char *
304 trapname(int psr)
305 {
306 	char *s;
307 
308 	s = trapnames[psr & PsrMask];
309 	if(s == nil)
310 		s = "unknown trap number in psr";
311 	return s;
312 }
313 
314 /*
315  *  called by trap to handle access faults
316  */
317 static void
318 faultarm(Ureg *ureg, uintptr va, int user, int read)
319 {
320 	int n, insyscall;
321 	char buf[ERRMAX];
322 	static int cnt, lastpid;
323 	static ulong lastva;
324 
325 	if(up == nil) {
326 		dumpregs(ureg);
327 		panic("fault: nil up in faultarm, accessing %#p", va);
328 	}
329 	insyscall = up->insyscall;
330 	up->insyscall = 1;
331 
332 	/* this is quite helpful during mmu and cache debugging */
333 	if(va == lastva && up->pid == lastpid) {
334 		++cnt;
335 		if (cnt >= 2)
336 			/* fault() isn't fixing the underlying cause */
337 			panic("fault: %d consecutive faults for va %#lux",
338 				cnt+1, va);
339 	} else {
340 		cnt = 0;
341 		lastva = va;
342 		lastpid = up->pid;
343 	}
344 
345 	n = fault(va, read);
346 	if(n < 0){
347 		if(!user){
348 			dumpregs(ureg);
349 			panic("fault: kernel accessing %#p", va);
350 		}
351 		/* don't dump registers; programs suicide all the time */
352 		snprint(buf, sizeof buf, "sys: trap: fault %s va=%#p",
353 			read? "read": "write", va);
354 		postnote(up, 1, buf, NDebug);
355 	}
356 	up->insyscall = insyscall;
357 }
358 
359 /*
360  *  returns 1 if the instruction writes memory, 0 otherwise
361  */
362 int
363 writetomem(ulong inst)
364 {
365 	/* swap always write memory */
366 	if((inst & 0x0FC00000) == 0x01000000)
367 		return 1;
368 
369 	/* loads and stores are distinguished by bit 20 */
370 	if(inst & (1<<20))
371 		return 0;
372 
373 	return 1;
374 }
375 
376 void
377 trap(Ureg *ureg)
378 {
379 	int user, x, rv, rem;
380 	ulong inst;
381 	u32int fsr;
382 	uintptr va;
383 	char buf[ERRMAX];
384 
385 	if(up != nil)
386 		rem = (char*)ureg - up->kstack;
387 	else
388 		rem = (char*)ureg - ((char*)m + sizeof(Mach));
389 	if(rem < 256) {
390 		dumpstack();
391 		panic("trap %d bytes remaining, up %#p ureg %#p at pc %#lux",
392 			rem, up, ureg, ureg->pc);
393 	}
394 
395 	user = (ureg->psr & PsrMask) == PsrMusr;
396 	if(user){
397 		up->dbgreg = ureg;
398 		cycles(&up->kentry);
399 	}
400 
401 	if(ureg->type == PsrMabt+1)
402 		ureg->pc -= 8;
403 	else
404 		ureg->pc -= 4;
405 
406 	m->inclockintr = 0;
407 	switch(ureg->type) {
408 	default:
409 		panic("unknown trap %ld", ureg->type);
410 		break;
411 	case PsrMirq:
412 		ldrexvalid = 0;
413 		// splflo();		/* allow fast interrupts */
414 		intrs(ureg, Irqlo);
415 		m->intr++;
416 		break;
417 	case PsrMabt:			/* prefetch fault */
418 		ldrexvalid = 0;
419 		faultarm(ureg, ureg->pc, user, 1);
420 		break;
421 	case PsrMabt+1:			/* data fault */
422 		ldrexvalid = 0;
423 		va = farget();
424 		inst = *(ulong*)(ureg->pc);
425 		fsr = fsrget() & 0xf;
426 		if (probing && !user) {
427 			if (trapped++ > 0)
428 				panic("trap: recursive probe %#lux", va);
429 			ureg->pc += 4;	/* continue at next instruction */
430 			break;
431 		}
432 		switch(fsr){
433 		case 0x0:
434 			panic("vector exception at %#lux", ureg->pc);
435 			break;
436 		case 0x1:
437 		case 0x3:
438 			if(user){
439 				snprint(buf, sizeof buf,
440 					"sys: alignment: pc %#lux va %#p\n",
441 					ureg->pc, va);
442 				postnote(up, 1, buf, NDebug);
443 			} else
444 				panic("kernel alignment: pc %#lux va %#p", ureg->pc, va);
445 			break;
446 		case 0x2:
447 			panic("terminal exception at %#lux", ureg->pc);
448 			break;
449 		case 0x4:
450 		case 0x6:
451 		case 0x8:
452 		case 0xa:
453 		case 0xc:
454 		case 0xe:
455 			panic("external abort %#ux pc %#lux addr %#px",
456 				fsr, ureg->pc, va);
457 			break;
458 		case 0x5:		/* translation fault, no section entry */
459 		case 0x7:		/* translation fault, no page entry */
460 			faultarm(ureg, va, user, !writetomem(inst));
461 			break;
462 		case 0x9:
463 		case 0xb:
464 			/* domain fault, accessing something we shouldn't */
465 			if(user){
466 				snprint(buf, sizeof buf,
467 					"sys: access violation: pc %#lux va %#p\n",
468 					ureg->pc, va);
469 				postnote(up, 1, buf, NDebug);
470 			} else
471 				panic("kernel access violation: pc %#lux va %#p",
472 					ureg->pc, va);
473 			break;
474 		case 0xd:
475 		case 0xf:
476 			/* permission error, copy on write or real permission error */
477 			faultarm(ureg, va, user, !writetomem(inst));
478 			break;
479 		}
480 		break;
481 	case PsrMund:	/* undefined instruction */
482 		if(user){
483 			if(seg(up, ureg->pc, 0) != nil &&
484 			   *(u32int*)ureg->pc == 0xD1200070){
485 				snprint(buf, sizeof buf, "sys: breakpoint");
486 				postnote(up, 1, buf, NDebug);
487 			}else{
488 				/* look for floating point instructions to interpret */
489 				x = spllo();
490 				rv = fpiarm(ureg);
491 				splx(x);
492 				if(rv == 0){
493 					ldrexvalid = 0;
494 					snprint(buf, sizeof buf,
495 						"undefined instruction: pc %#lux",
496 						ureg->pc);
497 					postnote(up, 1, buf, NDebug);
498 				}
499 			}
500 		}else{
501 			iprint("undefined instruction: pc %#lux inst %#ux\n",
502 				ureg->pc, ((u32int*)ureg->pc)[-2]);
503 			panic("undefined instruction");
504 		}
505 		break;
506 	}
507 	splhi();
508 
509 	/* delaysched set because we held a lock or because our quantum ended */
510 	if(up && up->delaysched && m->inclockintr){
511 		ldrexvalid = 0;
512 		sched();
513 		splhi();
514 	}
515 
516 	if(user){
517 		if(up->procctl || up->nnote)
518 			notify(ureg);
519 		kexit(ureg);
520 	}
521 }
522 
523 int
524 isvalidaddr(void *v)
525 {
526 	return (uintptr)v >= KZERO;
527 }
528 
529 void
530 dumplongs(char *msg, ulong *v, int n)
531 {
532 	int i, l;
533 
534 	l = 0;
535 	iprint("%s at %.8p: ", msg, v);
536 	for(i=0; i<n; i++){
537 		if(l >= 4){
538 			iprint("\n    %.8p: ", v);
539 			l = 0;
540 		}
541 		if(isvalidaddr(v)){
542 			iprint(" %.8lux", *v++);
543 			l++;
544 		}else{
545 			iprint(" invalid");
546 			break;
547 		}
548 	}
549 	iprint("\n");
550 }
551 
552 static void
553 dumpstackwithureg(Ureg *ureg)
554 {
555 	uintptr l, i, v, estack;
556 	u32int *p;
557 
558 	iprint("ktrace /kernel/path %#.8lux %#.8lux %#.8lux # pc, sp, link\n",
559 		ureg->pc, ureg->sp, ureg->r14);
560 	delay(2000);
561 	i = 0;
562 	if(up != nil && (uintptr)&l <= (uintptr)up->kstack+KSTACK)
563 		estack = (uintptr)up->kstack+KSTACK;
564 	else if((uintptr)&l >= (uintptr)m->stack
565 	     && (uintptr)&l <= (uintptr)m+MACHSIZE)
566 		estack = (uintptr)m+MACHSIZE;
567 	else{
568 		if(up != nil)
569 			iprint("&up->kstack %#p &l %#p\n", up->kstack, &l);
570 		else
571 			iprint("&m %#p &l %#p\n", m, &l);
572 		return;
573 	}
574 	for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){
575 		v = *(uintptr*)l;
576 		if(KTZERO < v && v < (uintptr)etext && !(v & 3)){
577 			v -= sizeof(u32int);		/* back up an instr */
578 			p = (u32int*)v;
579 			if((*p & 0x0f000000) == 0x0b000000){	/* BL instr? */
580 				iprint("%#8.8lux=%#8.8lux ", l, v);
581 				i++;
582 			}
583 		}
584 		if(i == 4){
585 			i = 0;
586 			iprint("\n");
587 		}
588 	}
589 	if(i)
590 		iprint("\n");
591 }
592 
593 /*
594  * Fill in enough of Ureg to get a stack trace, and call a function.
595  * Used by debugging interface rdb.
596  */
597 void
598 callwithureg(void (*fn)(Ureg*))
599 {
600 	Ureg ureg;
601 
602 	ureg.pc = getcallerpc(&fn);
603 	ureg.sp = PTR2UINT(&fn);
604 	fn(&ureg);
605 }
606 
607 void
608 dumpstack(void)
609 {
610 	callwithureg(dumpstackwithureg);
611 }
612 
613 void
614 dumpregs(Ureg* ureg)
615 {
616 	int s;
617 
618 	if (ureg == nil) {
619 		iprint("trap: no user process\n");
620 		return;
621 	}
622 	s = splhi();
623 	iprint("trap: %s", trapname(ureg->type));
624 	if(ureg != nil && (ureg->psr & PsrMask) != PsrMsvc)
625 		iprint(" in %s", trapname(ureg->psr));
626 	iprint("\n");
627 	iprint("psr %8.8lux type %2.2lux pc %8.8lux link %8.8lux\n",
628 		ureg->psr, ureg->type, ureg->pc, ureg->link);
629 	iprint("R14 %8.8lux R13 %8.8lux R12 %8.8lux R11 %8.8lux R10 %8.8lux\n",
630 		ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
631 	iprint("R9  %8.8lux R8  %8.8lux R7  %8.8lux R6  %8.8lux R5  %8.8lux\n",
632 		ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
633 	iprint("R4  %8.8lux R3  %8.8lux R2  %8.8lux R1  %8.8lux R0  %8.8lux\n",
634 		ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
635 	iprint("stack is at %#p\n", ureg);
636 	iprint("pc %#lux link %#lux\n", ureg->pc, ureg->link);
637 
638 	if(up)
639 		iprint("user stack: %#p-%#p\n", up->kstack, up->kstack+KSTACK-4);
640 	else
641 		iprint("kernel stack: %8.8lux-%8.8lux\n",
642 			(ulong)(m+1), (ulong)m+BY2PG-4);
643 	dumplongs("stack", (ulong *)(ureg + 1), 16);
644 	delay(2000);
645 	dumpstack();
646 	splx(s);
647 }
648 
649 void
650 idlehands(void)
651 {
652 	extern void _idlehands(void);
653 
654 	_idlehands();
655 }
656 
657 /* assumes that addr is already mapped suitable (e.g., by mmuidmap) */
658 vlong
659 probeaddr(uintptr addr)
660 {
661 	vlong v;
662 	static Lock fltlck;
663 
664 	ilock(&fltlck);
665 	trapped = 0;
666 	probing = 1;
667 	coherence();
668 
669 	v = *(ulong *)addr;	/* this may cause a fault (okay under ilock) */
670 	USED(probing);
671 	coherence();
672 
673 	probing = 0;
674 	coherence();
675 	if (trapped)
676 		v = -1;
677 	iunlock(&fltlck);
678 	return v;
679 }
680