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