1 #include "u.h" 2 #include "../port/lib.h" 3 #include "mem.h" 4 #include "dat.h" 5 #include "fns.h" 6 #include "io.h" 7 #include "ureg.h" 8 9 #include "mp.h" 10 #include "apbootstrap.h" 11 12 static Bus* mpbus; 13 static Bus* mpbuslast; 14 static int mpisabus = -1; 15 static int mpeisabus = -1; 16 extern int i8259elcr; /* mask of level-triggered interrupts */ 17 static Apic mpapic[MaxAPICNO+1]; 18 static int machno2apicno[MaxAPICNO+1]; /* inverse map: machno -> APIC ID */ 19 static Lock mprdthilock; 20 static int mprdthi; 21 static Ref mpvnoref; /* unique vector assignment */ 22 static int mpmachno = 1; 23 24 static char* buses[] = { 25 "CBUSI ", 26 "CBUSII", 27 "EISA ", 28 "FUTURE", 29 "INTERN", 30 "ISA ", 31 "MBI ", 32 "MBII ", 33 "MCA ", 34 "MPI ", 35 "MPSA ", 36 "NUBUS ", 37 "PCI ", 38 "PCMCIA", 39 "TC ", 40 "VL ", 41 "VME ", 42 "XPRESS", 43 0, 44 }; 45 46 static Apic* 47 mkprocessor(PCMPprocessor* p) 48 { 49 Apic *apic; 50 51 if(!(p->flags & PcmpEN) || p->apicno > MaxAPICNO) 52 return 0; 53 54 apic = &mpapic[p->apicno]; 55 apic->type = PcmpPROCESSOR; 56 apic->apicno = p->apicno; 57 apic->flags = p->flags; 58 apic->lintr[0] = ApicIMASK; 59 apic->lintr[1] = ApicIMASK; 60 61 if(p->flags & PcmpBP){ 62 machno2apicno[0] = p->apicno; 63 apic->machno = 0; 64 } 65 else{ 66 machno2apicno[mpmachno] = p->apicno; 67 apic->machno = mpmachno; 68 mpmachno++; 69 } 70 71 return apic; 72 } 73 74 static Bus* 75 mkbus(PCMPbus* p) 76 { 77 Bus *bus; 78 int i; 79 80 for(i = 0; buses[i]; i++){ 81 if(strncmp(buses[i], p->string, sizeof(p->string)) == 0) 82 break; 83 } 84 if(buses[i] == 0) 85 return 0; 86 87 bus = xalloc(sizeof(Bus)); 88 if(mpbus) 89 mpbuslast->next = bus; 90 else 91 mpbus = bus; 92 mpbuslast = bus; 93 94 bus->type = i; 95 bus->busno = p->busno; 96 if(bus->type == BusEISA){ 97 bus->po = PcmpLOW; 98 bus->el = PcmpLEVEL; 99 if(mpeisabus != -1) 100 print("mkbus: more than one EISA bus\n"); 101 mpeisabus = bus->busno; 102 } 103 else if(bus->type == BusPCI){ 104 bus->po = PcmpLOW; 105 bus->el = PcmpLEVEL; 106 } 107 else if(bus->type == BusISA){ 108 bus->po = PcmpHIGH; 109 bus->el = PcmpEDGE; 110 if(mpisabus != -1) 111 print("mkbus: more than one ISA bus\n"); 112 mpisabus = bus->busno; 113 } 114 else{ 115 bus->po = PcmpHIGH; 116 bus->el = PcmpEDGE; 117 } 118 119 return bus; 120 } 121 122 static Bus* 123 mpgetbus(int busno) 124 { 125 Bus *bus; 126 127 for(bus = mpbus; bus; bus = bus->next){ 128 if(bus->busno == busno) 129 return bus; 130 } 131 print("mpgetbus: can't find bus %d\n", busno); 132 133 return 0; 134 } 135 136 static Apic* 137 mkioapic(PCMPioapic* p) 138 { 139 Apic *apic; 140 141 if(!(p->flags & PcmpEN) || p->apicno > MaxAPICNO) 142 return 0; 143 144 /* 145 * Map the I/O APIC. 146 */ 147 if(mmukmap(p->addr, 0, 1024) == 0) 148 return 0; 149 150 apic = &mpapic[p->apicno]; 151 apic->type = PcmpIOAPIC; 152 apic->apicno = p->apicno; 153 apic->addr = KADDR(p->addr); 154 apic->flags = p->flags; 155 156 return apic; 157 } 158 159 static Aintr* 160 mkiointr(PCMPintr* p) 161 { 162 Bus *bus; 163 Aintr *aintr; 164 165 /* 166 * According to the MultiProcessor Specification, a destination 167 * I/O APIC of 0xFF means the signal is routed to all I/O APICs. 168 * It's unclear how that can possibly be correct so treat it as 169 * an error for now. 170 */ 171 if(p->apicno == 0xFF) 172 return 0; 173 if((bus = mpgetbus(p->busno)) == 0) 174 return 0; 175 176 aintr = xalloc(sizeof(Aintr)); 177 aintr->intr = p; 178 aintr->apic = &mpapic[p->apicno]; 179 aintr->next = bus->aintr; 180 bus->aintr = aintr; 181 182 return aintr; 183 } 184 185 static int 186 mpintrinit(Bus* bus, PCMPintr* intr, int vno, int /*irq*/) 187 { 188 int el, po, v; 189 190 /* 191 * Parse an I/O or Local APIC interrupt table entry and 192 * return the encoded vector. 193 */ 194 v = vno; 195 196 po = intr->flags & PcmpPOMASK; 197 el = intr->flags & PcmpELMASK; 198 199 switch(intr->intr){ 200 201 default: /* PcmpINT */ 202 v |= ApicLOWEST; 203 break; 204 205 case PcmpNMI: 206 v |= ApicNMI; 207 po = PcmpHIGH; 208 el = PcmpEDGE; 209 break; 210 211 case PcmpSMI: 212 v |= ApicSMI; 213 break; 214 215 case PcmpExtINT: 216 v |= ApicExtINT; 217 /* 218 * The AMI Goliath doesn't boot successfully with it's LINTR0 219 * entry which decodes to low+level. The PPro manual says ExtINT 220 * should be level, whereas the Pentium is edge. Setting the 221 * Goliath to edge+high seems to cure the problem. Other PPro 222 * MP tables (e.g. ASUS P/I-P65UP5 have a entry which decodes 223 * to edge+high, so who knows. 224 * Perhaps it would be best just to not set an ExtINT entry at 225 * all, it shouldn't be needed for SMP mode. 226 */ 227 po = PcmpHIGH; 228 el = PcmpEDGE; 229 break; 230 } 231 232 /* 233 */ 234 if(bus->type == BusEISA && !po && !el /*&& !(i8259elcr & (1<<irq))*/){ 235 po = PcmpHIGH; 236 el = PcmpEDGE; 237 } 238 if(!po) 239 po = bus->po; 240 if(po == PcmpLOW) 241 v |= ApicLOW; 242 else if(po != PcmpHIGH){ 243 print("mpintrinit: bad polarity 0x%uX\n", po); 244 return ApicIMASK; 245 } 246 247 if(!el) 248 el = bus->el; 249 if(el == PcmpLEVEL) 250 v |= ApicLEVEL; 251 else if(el != PcmpEDGE){ 252 print("mpintrinit: bad trigger 0x%uX\n", el); 253 return ApicIMASK; 254 } 255 256 return v; 257 } 258 259 static int 260 mklintr(PCMPintr* p) 261 { 262 Apic *apic; 263 Bus *bus; 264 int intin, v; 265 266 /* 267 * The offsets of vectors for LINT[01] are known to be 268 * 0 and 1 from the local APIC vector space at VectorLAPIC. 269 */ 270 if((bus = mpgetbus(p->busno)) == 0) 271 return 0; 272 intin = p->intin; 273 274 /* 275 * Pentium Pros have problems if LINT[01] are set to ExtINT 276 * so just bag it, SMP mode shouldn't need ExtINT anyway. 277 */ 278 if(p->intr == PcmpExtINT || p->intr == PcmpNMI) 279 v = ApicIMASK; 280 else 281 v = mpintrinit(bus, p, VectorLAPIC+intin, p->irq); 282 283 if(p->apicno == 0xFF){ 284 for(apic = mpapic; apic <= &mpapic[MaxAPICNO]; apic++){ 285 if((apic->flags & PcmpEN) 286 && apic->type == PcmpPROCESSOR) 287 apic->lintr[intin] = v; 288 } 289 } 290 else{ 291 apic = &mpapic[p->apicno]; 292 if((apic->flags & PcmpEN) && apic->type == PcmpPROCESSOR) 293 apic->lintr[intin] = v; 294 } 295 296 return v; 297 } 298 299 static void 300 checkmtrr(void) 301 { 302 int i, vcnt; 303 Mach *mach0; 304 305 /* 306 * If there are MTRR registers, snarf them for validation. 307 */ 308 if(!(m->cpuiddx & 0x1000)) 309 return; 310 311 rdmsr(0x0FE, &m->mtrrcap); 312 rdmsr(0x2FF, &m->mtrrdef); 313 if(m->mtrrcap & 0x0100){ 314 rdmsr(0x250, &m->mtrrfix[0]); 315 rdmsr(0x258, &m->mtrrfix[1]); 316 rdmsr(0x259, &m->mtrrfix[2]); 317 for(i = 0; i < 8; i++) 318 rdmsr(0x268+i, &m->mtrrfix[(i+3)]); 319 } 320 vcnt = m->mtrrcap & 0x00FF; 321 if(vcnt > nelem(m->mtrrvar)) 322 vcnt = nelem(m->mtrrvar); 323 for(i = 0; i < vcnt; i++) 324 rdmsr(0x200+i, &m->mtrrvar[i]); 325 326 /* 327 * If not the bootstrap processor, compare. 328 */ 329 if(m->machno == 0) 330 return; 331 332 mach0 = MACHP(0); 333 if(mach0->mtrrcap != m->mtrrcap) 334 print("mtrrcap%d: %lluX %lluX\n", 335 m->machno, mach0->mtrrcap, m->mtrrcap); 336 if(mach0->mtrrdef != m->mtrrdef) 337 print("mtrrdef%d: %lluX %lluX\n", 338 m->machno, mach0->mtrrdef, m->mtrrdef); 339 for(i = 0; i < 11; i++){ 340 if(mach0->mtrrfix[i] != m->mtrrfix[i]) 341 print("mtrrfix%d: i%d: %lluX %lluX\n", 342 m->machno, i, mach0->mtrrfix[i], m->mtrrfix[i]); 343 } 344 for(i = 0; i < vcnt; i++){ 345 if(mach0->mtrrvar[i] != m->mtrrvar[i]) 346 print("mtrrvar%d: i%d: %lluX %lluX\n", 347 m->machno, i, mach0->mtrrvar[i], m->mtrrvar[i]); 348 } 349 } 350 351 static void 352 squidboy(Apic* apic) 353 { 354 // iprint("Hello Squidboy\n"); 355 356 machinit(); 357 mmuinit(); 358 359 cpuidentify(); 360 cpuidprint(); 361 checkmtrr(); 362 363 lock(&mprdthilock); 364 mprdthi |= (1<<apic->apicno)<<24; 365 unlock(&mprdthilock); 366 367 lapicinit(apic); 368 lapiconline(); 369 syncclock(); 370 timersinit(); 371 372 fpoff(); 373 374 lock(&active); 375 active.machs |= 1<<m->machno; 376 unlock(&active); 377 378 while(!active.thunderbirdsarego) 379 microdelay(100); 380 381 schedinit(); 382 } 383 384 static void 385 mpstartap(Apic* apic) 386 { 387 ulong *apbootp, *pdb, *pte; 388 Mach *mach, *mach0; 389 int i, machno; 390 uchar *p; 391 392 mach0 = MACHP(0); 393 394 /* 395 * Initialise the AP page-tables and Mach structure. The page-tables 396 * are the same as for the bootstrap processor with the exception of 397 * the PTE for the Mach structure. 398 * Xspanalloc will panic if an allocation can't be made. 399 */ 400 p = xspanalloc(4*BY2PG, BY2PG, 0); 401 pdb = (ulong*)p; 402 memmove(pdb, mach0->pdb, BY2PG); 403 p += BY2PG; 404 405 if((pte = mmuwalk(pdb, MACHADDR, 1, 0)) == nil) 406 return; 407 memmove(p, KADDR(PPN(*pte)), BY2PG); 408 *pte = PADDR(p)|PTEWRITE|PTEVALID; 409 if(mach0->havepge) 410 *pte |= PTEGLOBAL; 411 p += BY2PG; 412 413 mach = (Mach*)p; 414 if((pte = mmuwalk(pdb, MACHADDR, 2, 0)) == nil) 415 return; 416 *pte = PADDR(mach)|PTEWRITE|PTEVALID; 417 if(mach0->havepge) 418 *pte |= PTEGLOBAL; 419 p += BY2PG; 420 421 machno = apic->machno; 422 MACHP(machno) = mach; 423 mach->machno = machno; 424 mach->pdb = pdb; 425 mach->gdt = (Segdesc*)p; /* filled by mmuinit */ 426 427 /* 428 * Tell the AP where its kernel vector and pdb are. 429 * The offsets are known in the AP bootstrap code. 430 */ 431 apbootp = (ulong*)(APBOOTSTRAP+0x08); 432 *apbootp++ = (ulong)squidboy; 433 *apbootp++ = PADDR(pdb); 434 *apbootp = (ulong)apic; 435 436 /* 437 * Universal Startup Algorithm. 438 */ 439 p = KADDR(0x467); 440 *p++ = PADDR(APBOOTSTRAP); 441 *p++ = PADDR(APBOOTSTRAP)>>8; 442 i = (PADDR(APBOOTSTRAP) & ~0xFFFF)/16; 443 *p++ = i; 444 *p = i>>8; 445 446 nvramwrite(0x0F, 0x0A); 447 lapicstartap(apic, PADDR(APBOOTSTRAP)); 448 for(i = 0; i < 1000; i++){ 449 lock(&mprdthilock); 450 if(mprdthi & ((1<<apic->apicno)<<24)){ 451 unlock(&mprdthilock); 452 break; 453 } 454 unlock(&mprdthilock); 455 delay(10); 456 } 457 nvramwrite(0x0F, 0x00); 458 } 459 460 void 461 mpinit(void) 462 { 463 int ncpu; 464 char *cp; 465 PCMP *pcmp; 466 uchar *e, *p; 467 Apic *apic, *bpapic; 468 469 i8259init(); 470 syncclock(); 471 472 if(_mp_ == 0) 473 return; 474 pcmp = KADDR(_mp_->physaddr); 475 476 /* 477 * Map the local APIC. 478 */ 479 if(mmukmap(pcmp->lapicbase, 0, 1024) == 0) 480 return; 481 482 bpapic = nil; 483 484 /* 485 * Run through the table saving information needed for starting 486 * application processors and initialising any I/O APICs. The table 487 * is guaranteed to be in order such that only one pass is necessary. 488 */ 489 p = ((uchar*)pcmp)+sizeof(PCMP); 490 e = ((uchar*)pcmp)+pcmp->length; 491 while(p < e) switch(*p){ 492 493 default: 494 print("mpinit: unknown PCMP type 0x%uX (e-p 0x%luX)\n", 495 *p, e-p); 496 while(p < e){ 497 print("%uX ", *p); 498 p++; 499 } 500 break; 501 502 case PcmpPROCESSOR: 503 if(apic = mkprocessor((PCMPprocessor*)p)){ 504 /* 505 * Must take a note of bootstrap processor APIC 506 * now as it will be needed in order to start the 507 * application processors later and there's no 508 * guarantee that the bootstrap processor appears 509 * first in the table before the others. 510 */ 511 apic->addr = KADDR(pcmp->lapicbase); 512 if(apic->flags & PcmpBP) 513 bpapic = apic; 514 } 515 p += sizeof(PCMPprocessor); 516 continue; 517 518 case PcmpBUS: 519 mkbus((PCMPbus*)p); 520 p += sizeof(PCMPbus); 521 continue; 522 523 case PcmpIOAPIC: 524 if(apic = mkioapic((PCMPioapic*)p)) 525 ioapicinit(apic, ((PCMPioapic*)p)->apicno); 526 p += sizeof(PCMPioapic); 527 continue; 528 529 case PcmpIOINTR: 530 mkiointr((PCMPintr*)p); 531 p += sizeof(PCMPintr); 532 continue; 533 534 case PcmpLINTR: 535 mklintr((PCMPintr*)p); 536 p += sizeof(PCMPintr); 537 continue; 538 } 539 540 /* 541 * No bootstrap processor, no need to go further. 542 */ 543 if(bpapic == 0) 544 return; 545 546 lapicinit(bpapic); 547 lock(&mprdthilock); 548 mprdthi |= (1<<bpapic->apicno)<<24; 549 unlock(&mprdthilock); 550 551 /* 552 * These interrupts are local to the processor 553 * and do not appear in the I/O APIC so it is OK 554 * to set them now. 555 */ 556 intrenable(IrqTIMER, lapicclock, 0, BUSUNKNOWN, "clock"); 557 intrenable(IrqERROR, lapicerror, 0, BUSUNKNOWN, "lapicerror"); 558 intrenable(IrqSPURIOUS, lapicspurious, 0, BUSUNKNOWN, "lapicspurious"); 559 lapiconline(); 560 561 checkmtrr(); 562 563 /* 564 * Initialise the application processors. 565 */ 566 if(cp = getconf("*ncpu")){ 567 ncpu = strtol(cp, 0, 0); 568 if(ncpu < 1) 569 ncpu = 1; 570 } 571 else 572 ncpu = MaxAPICNO; 573 memmove((void*)APBOOTSTRAP, apbootstrap, sizeof(apbootstrap)); 574 for(apic = mpapic; apic <= &mpapic[MaxAPICNO]; apic++){ 575 if(ncpu <= 1) 576 break; 577 if((apic->flags & (PcmpBP|PcmpEN)) == PcmpEN 578 && apic->type == PcmpPROCESSOR){ 579 mpstartap(apic); 580 conf.nmach++; 581 ncpu--; 582 } 583 } 584 585 /* 586 * we don't really know the number of processors till 587 * here. 588 * 589 * set conf.copymode here if nmach > 1. 590 * Should look for an ExtINT line and enable it. 591 */ 592 if(X86FAMILY(m->cpuidax) == 3 || conf.nmach > 1) 593 conf.copymode = 1; 594 } 595 596 static int 597 mpintrenablex(Vctl* v, int tbdf) 598 { 599 Bus *bus; 600 Aintr *aintr; 601 Apic *apic; 602 Pcidev *pcidev; 603 int bno, dno, irq, lo, n, type, vno; 604 605 /* 606 * Find the bus. 607 */ 608 type = BUSTYPE(tbdf); 609 bno = BUSBNO(tbdf); 610 dno = BUSDNO(tbdf); 611 n = 0; 612 for(bus = mpbus; bus != nil; bus = bus->next){ 613 if(bus->type != type) 614 continue; 615 if(n == bno) 616 break; 617 n++; 618 } 619 if(bus == nil){ 620 print("ioapicirq: can't find bus type %d\n", type); 621 return -1; 622 } 623 624 /* 625 * For PCI devices the interrupt pin (INT[ABCD]) and device 626 * number are encoded into the entry irq field, so create something 627 * to match on. The interrupt pin used by the device has to be 628 * obtained from the PCI config space. 629 */ 630 if(bus->type == BusPCI){ 631 pcidev = pcimatchtbdf(tbdf); 632 if(pcidev != nil && (n = pcicfgr8(pcidev, PciINTP)) != 0) 633 irq = (dno<<2)|(n-1); 634 else 635 irq = -1; 636 //print("pcidev %uX: irq %uX v->irq %uX\n", tbdf, irq, v->irq); 637 } 638 else 639 irq = v->irq; 640 641 /* 642 * Find a matching interrupt entry from the list of interrupts 643 * attached to this bus. 644 */ 645 for(aintr = bus->aintr; aintr; aintr = aintr->next){ 646 if(aintr->intr->irq != irq) 647 continue; 648 649 /* 650 * Check if already enabled. Multifunction devices may share 651 * INT[A-D]# so, if already enabled, check the polarity matches 652 * and the trigger is level. 653 * 654 * Should check the devices differ only in the function number, 655 * but that can wait for the planned enable/disable rewrite. 656 * The RDT read here is safe for now as currently interrupts 657 * are never disabled once enabled. 658 */ 659 apic = aintr->apic; 660 ioapicrdtr(apic, aintr->intr->intin, 0, &lo); 661 if(!(lo & ApicIMASK)){ 662 vno = lo & 0xFF; 663 n = mpintrinit(bus, aintr->intr, vno, v->irq); 664 n |= ApicLOGICAL; 665 if(n != lo || !(n & ApicLEVEL)){ 666 print("mpintrenable: multiple botch irq%d, tbdf %uX, lo %8.8uX, n %8.8uX\n", 667 v->irq, tbdf, lo, n); 668 return -1; 669 } 670 671 v->isr = lapicisr; 672 v->eoi = lapiceoi; 673 674 return vno; 675 } 676 677 /* 678 * With the APIC a unique vector can be assigned to each 679 * request to enable an interrupt. There are two reasons this 680 * is a good idea: 681 * 1) to prevent lost interrupts, no more than 2 interrupts 682 * should be assigned per block of 16 vectors (there is an 683 * in-service entry and a holding entry for each priority 684 * level and there is one priority level per block of 16 685 * interrupts). 686 * 2) each input pin on the IOAPIC will receive a different 687 * vector regardless of whether the devices on that pin use 688 * the same IRQ as devices on another pin. 689 */ 690 vno = VectorAPIC + (incref(&mpvnoref)-1)*8; 691 if(vno > MaxVectorAPIC){ 692 print("mpintrenable: vno %d, irq %d, tbdf %uX\n", 693 vno, v->irq, tbdf); 694 return -1; 695 } 696 lo = mpintrinit(bus, aintr->intr, vno, v->irq); 697 //print("lo 0x%uX: busno %d intr %d vno %d irq %d elcr 0x%uX\n", 698 // lo, bus->busno, aintr->intr->irq, vno, 699 // v->irq, i8259elcr); 700 if(lo & ApicIMASK) 701 return -1; 702 lo |= ApicLOGICAL; 703 704 if((apic->flags & PcmpEN) && apic->type == PcmpIOAPIC){ 705 lock(&mprdthilock); 706 ioapicrdtw(apic, aintr->intr->intin, mprdthi, lo); 707 unlock(&mprdthilock); 708 } 709 //else 710 // print("lo not enabled 0x%uX %d\n", 711 // apic->flags, apic->type); 712 713 v->isr = lapicisr; 714 v->eoi = lapiceoi; 715 716 return vno; 717 } 718 719 return -1; 720 } 721 722 int 723 mpintrenable(Vctl* v) 724 { 725 int irq, tbdf, vno; 726 727 /* 728 * If the bus is known, try it. 729 * BUSUNKNOWN is given both by [E]ISA devices and by 730 * interrupts local to the processor (local APIC, coprocessor 731 * breakpoint and page-fault). 732 */ 733 tbdf = v->tbdf; 734 if(tbdf != BUSUNKNOWN && (vno = mpintrenablex(v, tbdf)) != -1) 735 return vno; 736 737 irq = v->irq; 738 if(irq >= IrqLINT0 && irq <= MaxIrqLAPIC){ 739 if(irq != IrqSPURIOUS) 740 v->isr = lapiceoi; 741 return VectorPIC+irq; 742 } 743 if(irq < 0 || irq > MaxIrqPIC){ 744 print("mpintrenable: irq %d out of range\n", irq); 745 return -1; 746 } 747 748 /* 749 * Either didn't find it or have to try the default buses 750 * (ISA and EISA). This hack is due to either over-zealousness 751 * or laziness on the part of some manufacturers. 752 * 753 * The MP configuration table on some older systems 754 * (e.g. ASUS PCI/E-P54NP4) has an entry for the EISA bus 755 * but none for ISA. It also has the interrupt type and 756 * polarity set to 'default for this bus' which wouldn't 757 * be compatible with ISA. 758 */ 759 if(mpeisabus != -1){ 760 vno = mpintrenablex(v, MKBUS(BusEISA, 0, 0, 0)); 761 if(vno != -1) 762 return vno; 763 } 764 if(mpisabus != -1){ 765 vno = mpintrenablex(v, MKBUS(BusISA, 0, 0, 0)); 766 if(vno != -1) 767 return vno; 768 } 769 770 return -1; 771 } 772 773 static Lock mpshutdownlock; 774 775 void 776 mpshutdown(void) 777 { 778 /* 779 * To be done... 780 */ 781 if(!canlock(&mpshutdownlock)){ 782 /* 783 * If this processor received the CTRL-ALT-DEL from 784 * the keyboard, acknowledge it. Send an INIT to self. 785 */ 786 #ifdef FIXTHIS 787 if(lapicisr(VectorKBD)) 788 lapiceoi(VectorKBD); 789 #endif /* FIX THIS */ 790 idle(); 791 } 792 793 print("apshutdown: active = 0x%2.2uX\n", active.machs); 794 delay(1000); 795 splhi(); 796 797 /* 798 * INIT all excluding self. 799 */ 800 lapicicrw(0, 0x000C0000|ApicINIT); 801 802 #ifdef notdef 803 /* 804 * Often the BIOS hangs during restart if a conventional 8042 805 * warm-boot sequence is tried. The following is Intel specific and 806 * seems to perform a cold-boot, but at least it comes back. 807 */ 808 *(ushort*)KADDR(0x472) = 0x1234; /* BIOS warm-boot flag */ 809 outb(0xCF9, 0x02); 810 outb(0xCF9, 0x06); 811 #else 812 pcireset(); 813 i8042reset(); 814 #endif /* notdef */ 815 } 816