xref: /plan9-contrib/sys/src/9/pcboot/trap.c (revision b94bb474148e9d24a82a427863d9c9eb4c20f4ae)
1 #include	"u.h"
2 #include	"tos.h"
3 #include	"../port/lib.h"
4 #include	"mem.h"
5 #include	"dat.h"
6 #include	"fns.h"
7 #include	"io.h"
8 #include	"ureg.h"
9 #include	"../port/error.h"
10 
11 enum {
12 	Dumpstack = 1,		/* flag: allow stack dump on panic */
13 };
14 
15 static int trapinited;
16 
17 void	noted(Ureg*, ulong);
18 
19 static void debugbpt(Ureg*, void*);
20 static void fault386(Ureg*, void*);
21 static void doublefault(Ureg*, void*);
22 static void unexpected(Ureg*, void*);
23 static void _dumpstack(Ureg*);
24 
25 static Lock vctllock;
26 static Vctl *vctl[256];
27 
28 enum
29 {
30 	Ntimevec = 20		/* number of time buckets for each intr */
31 };
32 
33 void
34 intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
35 {
36 	int vno;
37 	Vctl *v;
38 
39 	if(f == nil){
40 		print("intrenable: nil handler for %d, tbdf %#uX for %s\n",
41 			irq, tbdf, name);
42 		return;
43 	}
44 
45 	v = xalloc(sizeof(Vctl));
46 	v->isintr = 1;
47 	v->irq = irq;
48 	v->tbdf = tbdf;
49 	v->f = f;
50 	v->a = a;
51 	strncpy(v->name, name, KNAMELEN-1);
52 	v->name[KNAMELEN-1] = 0;
53 
54 	ilock(&vctllock);
55 	vno = arch->intrenable(v);
56 	if(vno == -1){
57 		iunlock(&vctllock);
58 		print("intrenable: couldn't enable irq %d, tbdf %#uX for %s\n",
59 			irq, tbdf, v->name);
60 		xfree(v);
61 		return;
62 	}
63 	if(vctl[vno]){
64 		if(vctl[vno]->isr != v->isr || vctl[vno]->eoi != v->eoi)
65 			panic("intrenable: handler: %s %s %#p %#p %#p %#p",
66 				vctl[vno]->name, v->name,
67 				vctl[vno]->isr, v->isr, vctl[vno]->eoi, v->eoi);
68 		v->next = vctl[vno];
69 	}
70 	vctl[vno] = v;
71 	iunlock(&vctllock);
72 }
73 
74 int
75 intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name)
76 {
77 	Vctl **pv, *v;
78 	int vno;
79 
80 	/*
81 	 * For now, none of this will work with the APIC code,
82 	 * there is no mapping between irq and vector as the IRQ
83 	 * is pretty meaningless.
84 	 */
85 	if(arch->intrvecno == nil)
86 		return -1;
87 	vno = arch->intrvecno(irq);
88 	ilock(&vctllock);
89 	pv = &vctl[vno];
90 	while (*pv &&
91 		  ((*pv)->irq != irq || (*pv)->tbdf != tbdf || (*pv)->f != f || (*pv)->a != a ||
92 		   strcmp((*pv)->name, name)))
93 		pv = &((*pv)->next);
94 	assert(*pv);
95 
96 	v = *pv;
97 	*pv = (*pv)->next;	/* Link out the entry */
98 
99 	if(vctl[vno] == nil && arch->intrdisable != nil)
100 		arch->intrdisable(irq);
101 	iunlock(&vctllock);
102 	xfree(v);
103 	return 0;
104 }
105 
106 static long
107 irqallocread(Chan*, void *vbuf, long n, vlong offset)
108 {
109 	char *buf, *p, str[2*(11+1)+KNAMELEN+1+1];
110 	int m, vno;
111 	long oldn;
112 	Vctl *v;
113 
114 	if(n < 0 || offset < 0)
115 		error(Ebadarg);
116 
117 	oldn = n;
118 	buf = vbuf;
119 	for(vno=0; vno<nelem(vctl); vno++){
120 		for(v=vctl[vno]; v; v=v->next){
121 			m = snprint(str, sizeof str, "%11d %11d %.*s\n", vno, v->irq, KNAMELEN, v->name);
122 			if(m <= offset)	/* if do not want this, skip entry */
123 				offset -= m;
124 			else{
125 				/* skip offset bytes */
126 				m -= offset;
127 				p = str+offset;
128 				offset = 0;
129 
130 				/* write at most max(n,m) bytes */
131 				if(m > n)
132 					m = n;
133 				memmove(buf, p, m);
134 				n -= m;
135 				buf += m;
136 
137 				if(n == 0)
138 					return oldn;
139 			}
140 		}
141 	}
142 	return oldn - n;
143 }
144 
145 void
146 trapenable(int vno, void (*f)(Ureg*, void*), void* a, char *name)
147 {
148 	Vctl *v;
149 
150 	if(vno < 0 || vno >= VectorPIC)
151 		panic("trapenable: vno %d", vno);
152 	v = xalloc(sizeof(Vctl));
153 	v->tbdf = BUSUNKNOWN;
154 	v->f = f;
155 	v->a = a;
156 	strncpy(v->name, name, KNAMELEN);
157 	v->name[KNAMELEN-1] = 0;
158 
159 	ilock(&vctllock);
160 	if(vctl[vno])
161 		v->next = vctl[vno]->next;
162 	vctl[vno] = v;
163 	iunlock(&vctllock);
164 }
165 
166 static void
167 nmienable(void)
168 {
169 	int x;
170 
171 	/*
172 	 * Hack: should be locked with NVRAM access.
173 	 */
174 	outb(0x70, 0x80);		/* NMI latch clear */
175 	outb(0x70, 0);
176 
177 	x = inb(0x61) & 0x07;		/* Enable NMI */
178 	outb(0x61, 0x08|x);
179 	outb(0x61, x);
180 }
181 
182 /*
183  * Minimal trap setup.  Just enough so that we can panic
184  * on traps (bugs) during kernel initialization.
185  * Called very early - malloc is not yet available.
186  */
187 void
188 trapinit0(void)
189 {
190 	int d1, v;
191 	ulong vaddr;
192 	Segdesc *idt;
193 
194 	idt = (Segdesc*)IDTADDR;
195 	vaddr = (ulong)vectortable;
196 	for(v = 0; v < 256; v++){
197 		d1 = (vaddr & 0xFFFF0000)|SEGP;
198 		switch(v){
199 
200 		case VectorBPT:
201 			d1 |= SEGPL(3)|SEGIG;
202 			break;
203 
204 		case VectorSYSCALL:
205 			d1 |= SEGPL(3)|SEGIG;
206 			break;
207 
208 		default:
209 			d1 |= SEGPL(0)|SEGIG;
210 			break;
211 		}
212 		idt[v].d0 = (vaddr & 0xFFFF)|(KESEL<<16);
213 		idt[v].d1 = d1;
214 		vaddr += 6;
215 	}
216 }
217 
218 void
219 trapinit(void)
220 {
221 	/*
222 	 * Special traps.
223 	 * Syscall() is called directly without going through trap().
224 	 */
225 	trapenable(VectorBPT, debugbpt, 0, "debugpt");
226 	trapenable(VectorPF, fault386, 0, "fault386");
227 	trapenable(Vector2F, doublefault, 0, "doublefault");
228 	trapenable(Vector15, unexpected, 0, "unexpected");
229 	nmienable();
230 
231 	addarchfile("irqalloc", 0444, irqallocread, nil);
232 	trapinited = 1;
233 }
234 
235 static char* excname[32] = {
236 	"divide error",
237 	"debug exception",
238 	"nonmaskable interrupt",
239 	"breakpoint",
240 	"overflow",
241 	"bounds check",
242 	"invalid opcode",
243 	"coprocessor not available",
244 	"double fault",
245 	"coprocessor segment overrun",
246 	"invalid TSS",
247 	"segment not present",
248 	"stack exception",
249 	"general protection violation",
250 	"page fault",
251 	"15 (reserved)",
252 	"coprocessor error",
253 	"alignment check",
254 	"machine check",
255 	"19 (reserved)",
256 	"20 (reserved)",
257 	"21 (reserved)",
258 	"22 (reserved)",
259 	"23 (reserved)",
260 	"24 (reserved)",
261 	"25 (reserved)",
262 	"26 (reserved)",
263 	"27 (reserved)",
264 	"28 (reserved)",
265 	"29 (reserved)",
266 	"30 (reserved)",
267 	"31 (reserved)",
268 };
269 
270 /*
271  *  keep histogram of interrupt service times
272  */
273 void
274 intrtime(Mach*, int vno)
275 {
276 	ulong diff;
277 	ulong x;
278 
279 	x = perfticks();
280 	diff = x - m->perf.intrts;
281 	m->perf.intrts = x;
282 
283 	m->perf.inintr += diff;
284 	if(up == nil && m->perf.inidle > diff)
285 		m->perf.inidle -= diff;
286 	USED(vno);
287 }
288 
289 /* go to user space */
290 void
291 kexit(Ureg*)
292 {
293 	uvlong t;
294 	Tos *tos;
295 
296 	/* precise time accounting, kernel exit */
297 	tos = (Tos*)(USTKTOP-sizeof(Tos));
298 	cycles(&t);
299 	tos->kcycles += t - up->kentry;
300 	tos->pcycles = up->pcycles;
301 	tos->pid = up->pid;
302 }
303 
304 /*
305  *  All traps come here.  It is slower to have all traps call trap()
306  *  rather than directly vectoring the handler.  However, this avoids a
307  *  lot of code duplication and possible bugs.  The only exception is
308  *  VectorSYSCALL.
309  *  Trap is called with interrupts disabled via interrupt-gates.
310  */
311 void
312 trap(Ureg* ureg)
313 {
314 	int clockintr, i, vno, user;
315 	Vctl *ctl, *v;
316 	Mach *mach;
317 
318 	if(!trapinited){
319 		/* fault386 can give a better error message */
320 		if(ureg->trap == VectorPF)
321 			fault386(ureg, nil);
322 		panic("trap %lud: not ready", ureg->trap);
323 	}
324 
325 	if (m == 0)
326 		panic("trap: nil m");
327 	m->perf.intrts = perfticks();
328 	user = (ureg->cs & 0xFFFF) == UESEL;
329 
330 	clockintr = 0;
331 
332 	vno = ureg->trap;
333 	if(ctl = vctl[vno]){
334 		if(ctl->isintr){
335 			m->intr++;
336 			if(vno >= VectorPIC && vno != VectorSYSCALL)
337 				m->lastintr = ctl->irq;
338 		}
339 
340 		if(ctl->isr)
341 			ctl->isr(vno);
342 		for(v = ctl; v != nil; v = v->next){
343 			if(v->f)
344 				v->f(ureg, v->a);
345 		}
346 		if(ctl->eoi)
347 			ctl->eoi(vno);
348 
349 		if(ctl->isintr){
350 			intrtime(m, vno);
351 
352 			if(ctl->irq == IrqCLOCK || ctl->irq == IrqTIMER)
353 				clockintr = 1;
354 
355 			if(up && !clockintr)
356 				preempted();
357 		}
358 	}
359 	else if(vno < nelem(excname) && user){
360 		char buf[ERRMAX];
361 
362 		spllo();
363 		snprint(buf, sizeof buf, "sys: trap: %s", excname[vno]);
364 		postnote(up, 1, buf, NDebug);
365 	}
366 	else if(vno >= VectorPIC && vno != VectorSYSCALL){
367 		/*
368 		 * An unknown interrupt.
369 		 * Check for a default IRQ7. This can happen when
370 		 * the IRQ input goes away before the acknowledge.
371 		 * In this case, a 'default IRQ7' is generated, but
372 		 * the corresponding bit in the ISR isn't set.
373 		 * In fact, just ignore all such interrupts.
374 		 */
375 
376 		/* call all interrupt routines, just in case */
377 		for(i = VectorPIC; i <= MaxIrqLAPIC; i++){
378 			ctl = vctl[i];
379 			if(ctl == nil)
380 				continue;
381 			if(!ctl->isintr)
382 				continue;
383 			for(v = ctl; v != nil; v = v->next){
384 				if(v->f)
385 					v->f(ureg, v->a);
386 			}
387 			/* should we do this? */
388 			if(ctl->eoi)
389 				ctl->eoi(i);
390 		}
391 
392 		/* clear the interrupt */
393 		i8259isr(vno);
394 
395 		if(0)print("cpu%d: spurious interrupt %d, last %d\n",
396 			m->machno, vno, m->lastintr);
397 		if(0)if(conf.nmach > 1){
398 			for(i = 0; i < 32; i++){
399 				if(!(active.machs & (1<<i)))
400 					continue;
401 				mach = MACHP(i);
402 				if(m->machno == mach->machno)
403 					continue;
404 				print(" cpu%d: last %d",
405 					mach->machno, mach->lastintr);
406 			}
407 			print("\n");
408 		}
409 		m->spuriousintr++;
410 		return;
411 	}
412 	else{
413 		if(vno == VectorNMI){
414 			/*
415 			 * Don't re-enable, it confuses the crash dumps.
416 			nmienable();
417 			 */
418 			iprint("cpu%d: PC %#8.8lux\n", m->machno, ureg->pc);
419 			while(m->machno != 0)
420 				;
421 		}
422 		dumpregs(ureg);
423 		if(vno < nelem(excname))
424 			panic("%s", excname[vno]);
425 		panic("unknown trap/intr: %d", vno);
426 	}
427 	splhi();
428 
429 	/* delaysched set because we held a lock or because our quantum ended */
430 	if(up && up->delaysched && clockintr){
431 		sched();
432 		splhi();
433 	}
434 }
435 
436 /*
437  *  dump registers
438  */
439 void
440 dumpregs2(Ureg* ureg)
441 {
442 	if(up)
443 		iprint("cpu%d: registers for %s %lud\n",
444 			m->machno, up->text, up->pid);
445 	else
446 		iprint("cpu%d: registers for kernel\n", m->machno);
447 	iprint("FLAGS=%luX TRAP=%luX ECODE=%luX PC=%luX",
448 		ureg->flags, ureg->trap, ureg->ecode, ureg->pc);
449 	iprint(" SS=%4.4luX USP=%luX\n", ureg->ss & 0xFFFF, ureg->usp);
450 	iprint("  AX %8.8luX  BX %8.8luX  CX %8.8luX  DX %8.8luX\n",
451 		ureg->ax, ureg->bx, ureg->cx, ureg->dx);
452 	iprint("  SI %8.8luX  DI %8.8luX  BP %8.8luX\n",
453 		ureg->si, ureg->di, ureg->bp);
454 	iprint("  CS %4.4luX  DS %4.4luX  ES %4.4luX  FS %4.4luX  GS %4.4luX\n",
455 		ureg->cs & 0xFFFF, ureg->ds & 0xFFFF, ureg->es & 0xFFFF,
456 		ureg->fs & 0xFFFF, ureg->gs & 0xFFFF);
457 }
458 
459 void
460 dumpregs(Ureg* ureg)
461 {
462 	vlong mca, mct;
463 
464 	dumpregs2(ureg);
465 
466 	/*
467 	 * Processor control registers.
468 	 * If machine check exception, time stamp counter, page size extensions
469 	 * or enhanced virtual 8086 mode extensions are supported, there is a
470 	 * CR4. If there is a CR4 and machine check extensions, read the machine
471 	 * check address and machine check type registers if RDMSR supported.
472 	 */
473 	iprint("  CR0 %8.8lux CR2 %8.8lux CR3 %8.8lux",
474 		getcr0(), getcr2(), getcr3());
475 	if(m->cpuiddx & 0x9A){
476 		iprint(" CR4 %8.8lux", getcr4());
477 		if((m->cpuiddx & 0xA0) == 0xA0){
478 			rdmsr(0x00, &mca);
479 			rdmsr(0x01, &mct);
480 			iprint("\n  MCA %8.8llux MCT %8.8llux", mca, mct);
481 		}
482 	}
483 	iprint("\n  ur %#p up %#p\n", ureg, up);
484 }
485 
486 
487 /*
488  * Fill in enough of Ureg to get a stack trace, and call a function.
489  * Used by debugging interface rdb.
490  */
491 void
492 callwithureg(void (*fn)(Ureg*))
493 {
494 	Ureg ureg;
495 	ureg.pc = getcallerpc(&fn);
496 	ureg.sp = (ulong)&fn;
497 	fn(&ureg);
498 }
499 
500 static void
501 _dumpstack(Ureg *ureg)
502 {
503 	uintptr l, v, i, estack;
504 	extern ulong etext;
505 	int x;
506 	char *s;
507 
508 	if (!Dumpstack) {
509 		print("no stack dump\n");
510 		return;
511 	}
512 	if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){
513 		iprint("dumpstack disabled\n");
514 		return;
515 	}
516 	iprint("dumpstack\n");
517 
518 	x = 0;
519 	x += iprint("ktrace /kernel/path %.8lux %.8lux <<EOF\n", ureg->pc, ureg->sp);
520 	i = 0;
521 	if(up
522 	&& (uintptr)&l >= (uintptr)up->kstack
523 	&& (uintptr)&l <= (uintptr)up->kstack+KSTACK)
524 		estack = (uintptr)up->kstack+KSTACK;
525 	else if((uintptr)&l >= (uintptr)m->stack
526 	&& (uintptr)&l <= (uintptr)m+MACHSIZE)
527 		estack = (uintptr)m+MACHSIZE;
528 	else
529 		return;
530 	x += iprint("estackx %p\n", estack);
531 
532 	for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){
533 		v = *(uintptr*)l;
534 		if((KTZERO < v && v < (uintptr)&etext) || estack-l < 32){
535 			/*
536 			 * Could Pick off general CALL (((uchar*)v)[-5] == 0xE8)
537 			 * and CALL indirect through AX
538 			 * (((uchar*)v)[-2] == 0xFF && ((uchar*)v)[-2] == 0xD0),
539 			 * but this is too clever and misses faulting address.
540 			 */
541 			x += iprint("%.8p=%.8p ", l, v);
542 			i++;
543 		}
544 		if(i == 4){
545 			i = 0;
546 			x += iprint("\n");
547 		}
548 	}
549 	if(i)
550 		iprint("\n");
551 	iprint("EOF\n");
552 
553 	if(ureg->trap != VectorNMI)
554 		return;
555 
556 	i = 0;
557 	for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){
558 		iprint("%.8p ", *(uintptr*)l);
559 		if(++i == 8){
560 			i = 0;
561 			iprint("\n");
562 		}
563 	}
564 	if(i)
565 		iprint("\n");
566 }
567 
568 void
569 dumpstack(void)
570 {
571 	callwithureg(_dumpstack);
572 }
573 
574 static void
575 debugbpt(Ureg* ureg, void*)
576 {
577 	char buf[ERRMAX];
578 
579 	if(up == 0)
580 		panic("kernel bpt");
581 	/* restore pc to instruction that caused the trap */
582 	ureg->pc--;
583 	snprint(buf, sizeof buf, "sys: breakpoint");
584 	postnote(up, 1, buf, NDebug);
585 }
586 
587 static void
588 doublefault(Ureg*, void*)
589 {
590 	panic("double fault");
591 }
592 
593 static void
594 unexpected(Ureg* ureg, void*)
595 {
596 	print("unexpected trap %lud; ignoring\n", ureg->trap);
597 }
598 
599 extern void checkfault(ulong, ulong);
600 static void
601 fault386(Ureg* ureg, void*)
602 {
603 	ulong addr;
604 	int read, user, n, insyscall;
605 
606 	addr = getcr2();
607 	read = !(ureg->ecode & 2);
608 
609 	user = (ureg->cs & 0xFFFF) == UESEL;
610 	if(!user){
611 		if(vmapsync(addr))
612 			return;
613 		if(addr >= USTKTOP)
614 			panic("kernel fault: bad address pc=%#.8lux addr=%#.8lux", ureg->pc, addr);
615 		if(up == nil)
616 			panic("kernel fault: no user process pc=%#.8lux addr=%#.8lux", ureg->pc, addr);
617 	} else
618 		panic("fault386: fault from user mode");
619 	if(up == nil)
620 		panic("user fault: up=0 pc=%#.8lux addr=%#.8lux", ureg->pc, addr);
621 
622 	insyscall = up->insyscall;
623 	up->insyscall = 1;
624 	n = fault(addr, read);
625 	if(n < 0){
626 		dumpregs(ureg);
627 		panic("fault: %#lux", addr);
628 	}
629 	up->insyscall = insyscall;
630 }
631 
632 /*
633  *  dregs of system calls
634  */
635 
636 /*
637  *  Syscall is called directly from assembler without going through trap().
638  */
639 void
640 syscall(Ureg*)
641 {
642 	/* the bootstrap doesn't implement system calls */
643 	panic("syscall");
644 }
645 
646 long
647 execregs(ulong entry, ulong ssize, ulong nargs)
648 {
649 	ulong *sp;
650 	Ureg *ureg;
651 
652 	up->fpstate = FPinit;
653 	fpoff();
654 
655 	sp = (ulong*)(USTKTOP - ssize);
656 	*--sp = nargs;
657 
658 	ureg = up->dbgreg;
659 	ureg->usp = (ulong)sp;
660 	ureg->pc = entry;
661 	return USTKTOP-sizeof(Tos);		/* address of kernel/user shared data */
662 }
663 
664 /*
665  *  return the userpc the last exception happened at
666  */
667 ulong
668 userpc(void)
669 {
670 	Ureg *ureg;
671 
672 	ureg = (Ureg*)up->dbgreg;
673 	return ureg->pc;
674 }
675 
676 /* This routine must save the values of registers the user is not permitted
677  * to write from devproc and then restore the saved values before returning.
678  */
679 void
680 setregisters(Ureg* ureg, char* pureg, char* uva, int n)
681 {
682 	ulong cs, ds, es, flags, fs, gs, ss;
683 
684 	ss = ureg->ss;
685 	flags = ureg->flags;
686 	cs = ureg->cs;
687 	ds = ureg->ds;
688 	es = ureg->es;
689 	fs = ureg->fs;
690 	gs = ureg->gs;
691 	memmove(pureg, uva, n);
692 	ureg->gs = gs;
693 	ureg->fs = fs;
694 	ureg->es = es;
695 	ureg->ds = ds;
696 	ureg->cs = cs;
697 	ureg->flags = (ureg->flags & 0x00FF) | (flags & 0xFF00);
698 	ureg->ss = ss;
699 }
700 
701 static void
702 linkproc(void)
703 {
704 	spllo();
705 	up->kpfun(up->kparg);
706 	pexit("kproc dying", 0);
707 }
708 
709 void
710 kprocchild(Proc* p, void (*func)(void*), void* arg)
711 {
712 	/*
713 	 * gotolabel() needs a word on the stack in
714 	 * which to place the return PC used to jump
715 	 * to linkproc().
716 	 */
717 	p->sched.pc = (ulong)linkproc;
718 	p->sched.sp = (ulong)p->kstack+KSTACK-BY2WD;
719 
720 	p->kpfun = func;
721 	p->kparg = arg;
722 }
723 
724 void
725 forkchild(Proc *p, Ureg *ureg)
726 {
727 	Ureg *cureg;
728 
729 	/*
730 	 * Add 2*BY2WD to the stack to account for
731 	 *  - the return PC
732 	 *  - trap's argument (ur)
733 	 */
734 	p->sched.sp = (ulong)p->kstack+KSTACK-(sizeof(Ureg)+2*BY2WD);
735 	p->sched.pc = (ulong)forkret;
736 
737 	cureg = (Ureg*)(p->sched.sp+2*BY2WD);
738 	memmove(cureg, ureg, sizeof(Ureg));
739 	/* return value of syscall in child */
740 	cureg->ax = 0;
741 
742 	/* Things from bottom of syscall which were never executed */
743 	p->psstate = 0;
744 	p->insyscall = 0;
745 }
746 
747 /* Give enough context in the ureg to produce a kernel stack for
748  * a sleeping process
749  */
750 void
751 setkernur(Ureg* ureg, Proc* p)
752 {
753 	ureg->pc = p->sched.pc;
754 	ureg->sp = p->sched.sp+4;
755 }
756 
757 ulong
758 dbgpc(Proc *p)
759 {
760 	Ureg *ureg;
761 
762 	ureg = p->dbgreg;
763 	if(ureg == 0)
764 		return 0;
765 
766 	return ureg->pc;
767 }
768