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