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