2 #include "../port/lib.h"
10 #include "apbootstrap.h"
12 /* filled in by pcmpinit or acpiinit */
17 Apic *mpioapic[MaxAPICNO+1];
18 Apic *mpapic[MaxAPICNO+1];
21 mpintrinit(Bus* bus, PCMPintr* intr, int vno, int /*irq*/)
26 * Parse an I/O or Local APIC interrupt table entry and
27 * return the encoded vector.
31 po = intr->flags & PcmpPOMASK;
32 el = intr->flags & PcmpELMASK;
35 default: /* PcmpINT */
36 v |= ApicFIXED; /* no-op */
52 * The AMI Goliath doesn't boot successfully with it's LINTR0
53 * entry which decodes to low+level. The PPro manual says ExtINT
54 * should be level, whereas the Pentium is edge. Setting the
55 * Goliath to edge+high seems to cure the problem. Other PPro
56 * MP tables (e.g. ASUS P/I-P65UP5 have a entry which decodes
57 * to edge+high, so who knows.
58 * Perhaps it would be best just to not set an ExtINT entry at
59 * all, it shouldn't be needed for SMP mode.
68 if(bus->type == BusEISA && !po && !el /*&& !(i8259elcr & (1<<irq))*/){
76 else if(po != PcmpHIGH){
77 print("mpintrinit: bad polarity 0x%uX\n", po);
85 else if(el != PcmpEDGE){
86 print("mpintrinit: bad trigger 0x%uX\n", el);
100 * If there are MTRR registers, snarf them for validation.
102 if(!(m->cpuiddx & 0x1000))
105 rdmsr(0x0FE, &m->mtrrcap);
106 rdmsr(0x2FF, &m->mtrrdef);
107 if(m->mtrrcap & 0x0100){
108 rdmsr(0x250, &m->mtrrfix[0]);
109 rdmsr(0x258, &m->mtrrfix[1]);
110 rdmsr(0x259, &m->mtrrfix[2]);
111 for(i = 0; i < 8; i++)
112 rdmsr(0x268+i, &m->mtrrfix[(i+3)]);
114 vcnt = m->mtrrcap & 0x00FF;
115 if(vcnt > nelem(m->mtrrvar))
116 vcnt = nelem(m->mtrrvar);
117 for(i = 0; i < vcnt; i++)
118 rdmsr(0x200+i, &m->mtrrvar[i]);
121 * If not the bootstrap processor, compare.
127 if(mach0->mtrrcap != m->mtrrcap)
128 print("mtrrcap%d: %lluX %lluX\n",
129 m->machno, mach0->mtrrcap, m->mtrrcap);
130 if(mach0->mtrrdef != m->mtrrdef)
131 print("mtrrdef%d: %lluX %lluX\n",
132 m->machno, mach0->mtrrdef, m->mtrrdef);
133 for(i = 0; i < 11; i++){
134 if(mach0->mtrrfix[i] != m->mtrrfix[i])
135 print("mtrrfix%d: i%d: %lluX %lluX\n",
136 m->machno, i, mach0->mtrrfix[i], m->mtrrfix[i]);
138 for(i = 0; i < vcnt; i++){
139 if(mach0->mtrrvar[i] != m->mtrrvar[i])
140 print("mtrrvar%d: i%d: %lluX %lluX\n",
141 m->machno, i, mach0->mtrrvar[i], m->mtrrvar[i]);
151 cycles(&m->tscticks); /* Uses the rdtsc instruction */
160 if(arch->fastclock != tscticks)
167 x = MACHP(0)->tscticks;
168 while(x == MACHP(0)->tscticks)
170 wrmsr(0x10, MACHP(0)->tscticks);
171 cycles(&m->tscticks);
178 // iprint("Hello Squidboy\n");
197 active.machs |= 1<<m->machno;
200 while(!active.thunderbirdsarego)
207 mpstartap(Apic* apic)
209 ulong *apbootp, *pdb, *pte;
217 * Initialise the AP page-tables and Mach structure. The page-tables
218 * are the same as for the bootstrap processor with the exception of
219 * the PTE for the Mach structure.
220 * Xspanalloc will panic if an allocation can't be made.
222 p = xspanalloc(4*BY2PG, BY2PG, 0);
224 memmove(pdb, mach0->pdb, BY2PG);
227 if((pte = mmuwalk(pdb, MACHADDR, 1, 0)) == nil)
229 memmove(p, KADDR(PPN(*pte)), BY2PG);
230 *pte = PADDR(p)|PTEWRITE|PTEVALID;
236 if((pte = mmuwalk(pdb, MACHADDR, 2, 0)) == nil)
238 *pte = PADDR(mach)|PTEWRITE|PTEVALID;
243 machno = apic->machno;
244 MACHP(machno) = mach;
245 mach->machno = machno;
247 mach->gdt = (Segdesc*)p; /* filled by mmuinit */
250 * Tell the AP where its kernel vector and pdb are.
251 * The offsets are known in the AP bootstrap code.
253 apbootp = (ulong*)(APBOOTSTRAP+0x08);
254 *apbootp++ = (ulong)squidboy;
255 *apbootp++ = PADDR(pdb);
256 *apbootp = (ulong)apic;
259 * Universal Startup Algorithm.
262 *p++ = PADDR(APBOOTSTRAP);
263 *p++ = PADDR(APBOOTSTRAP)>>8;
264 i = (PADDR(APBOOTSTRAP) & ~0xFFFF)/16;
265 /* code assumes i==0 */
267 print("mp: bad APBOOTSTRAP\n");
271 nvramwrite(0x0F, 0x0A);
272 lapicstartap(apic, PADDR(APBOOTSTRAP));
273 for(i = 0; i < 1000; i++){
278 nvramwrite(0x0F, 0x00);
291 if(getconf("*apicdebug")){
296 for(i=0; i<=MaxAPICNO; i++){
298 print("LAPIC%d: pa=%lux va=%lux flags=%x\n",
299 i, apic->paddr, (ulong)apic->addr, apic->flags);
300 if(apic = mpioapic[i])
301 print("IOAPIC%d: pa=%lux va=%lux flags=%x gsibase=%d mre=%d\n",
302 i, apic->paddr, (ulong)apic->addr, apic->flags, apic->gsibase, apic->mre);
304 for(b = mpbus; b; b = b->next){
305 print("BUS%d type=%d flags=%x\n", b->busno, b->type, b->po|b->el);
306 for(ai = b->aintr; ai; ai = ai->next){
308 print("\ttype=%d irq=%d (%d [%c]) apic=%d intin=%d flags=%x\n",
309 pi->type, pi->irq, pi->irq>>2, "ABCD"[pi->irq&3],
310 pi->apicno, pi->intin, pi->flags);
316 for(i=0; i<=MaxAPICNO; i++){
319 if(mpapic[i]->flags & PcmpBP){
326 panic("mpinit: no bootstrap processor");
333 * These interrupts are local to the processor
334 * and do not appear in the I/O APIC so it is OK
337 intrenable(IrqTIMER, lapicclock, 0, BUSUNKNOWN, "clock");
338 intrenable(IrqERROR, lapicerror, 0, BUSUNKNOWN, "lapicerror");
339 intrenable(IrqSPURIOUS, lapicspurious, 0, BUSUNKNOWN, "lapicspurious");
345 * Initialise the application processors.
347 if(cp = getconf("*ncpu")){
348 ncpu = strtol(cp, 0, 0);
351 else if(ncpu > MAXMACH)
356 memmove((void*)APBOOTSTRAP, apbootstrap, sizeof(apbootstrap));
357 for(i=0; i<nelem(mpapic); i++){
358 if((apic = mpapic[i]) == nil)
362 if((apic->flags & (PcmpBP|PcmpEN)) == PcmpEN){
370 * we don't really know the number of processors till
373 * set conf.copymode here if nmach > 1.
374 * Should look for an ExtINT line and enable it.
376 if(X86FAMILY(m->cpuidax) == 3 || conf.nmach > 1)
383 static Lock physidlock;
388 * The bulk of this code was written ~1995, when there was
389 * one architecture and one generation of hardware, the number
390 * of CPUs was up to 4(8) and the choices for interrupt routing
391 * were physical, or flat logical (optionally with lowest
392 * priority interrupt). Logical mode hasn't scaled well with
393 * the increasing number of packages/cores/threads, so the
394 * fall-back is to physical mode, which works across all processor
395 * generations, both AMD and Intel, using the APIC and xAPIC.
397 * Interrupt routing policy can be set here.
398 * Currently, just assign each interrupt to a different CPU on
399 * a round-robin basis. Some idea of the packages/cores/thread
400 * topology would be useful here, e.g. to not assign interrupts
401 * to more than one thread in a core, or to use a "noise" core.
402 * But, as usual, Intel make that an onerous task.
407 if(physid >= nelem(mpapic))
411 if(mpapic[i]->online)
416 return mpapic[i]->apicno;
420 * With the APIC a unique vector can be assigned to each
421 * request to enable an interrupt. There are two reasons this
423 * 1) to prevent lost interrupts, no more than 2 interrupts
424 * should be assigned per block of 16 vectors (there is an
425 * in-service entry and a holding entry for each priority
426 * level and there is one priority level per block of 16
428 * 2) each input pin on the IOAPIC will receive a different
429 * vector regardless of whether the devices on that pin use
430 * the same IRQ as devices on another pin.
435 static int round = 0, num = 0;
440 vno = VectorAPIC + num;
441 if(vno < MaxVectorAPIC-7)
450 mpintrenablex(Vctl* v, int tbdf)
456 int bno, dno, pin, hi, irq, lo, n, type, vno;
458 type = BUSTYPE(tbdf);
465 if(pcidev = pcimatchtbdf(tbdf))
466 pin = pcicfgr8(pcidev, PciINTP);
467 } else if(type == BusISA)
471 for(bus = mpbus; bus != nil; bus = bus->next){
472 if(bus->type != type)
474 if(bus->busno == bno)
480 * if the PCI device is behind a PCI-PCI bridge thats not described
481 * by the MP or ACPI tables then walk up the bus translating interrupt
484 if(pcidev && pcidev->parent && pin > 0){
485 pin = ((dno+(pin-1))%4)+1;
486 pcidev = pcidev->parent;
487 bno = BUSBNO(pcidev->tbdf);
488 dno = BUSDNO(pcidev->tbdf);
491 print("mpintrenable: can't find bus type %d, number %d\n", type, bno);
496 * For PCI devices the interrupt pin (INT[ABCD]) and device
497 * number are encoded into the entry irq field, so create something
500 if(bus->type == BusPCI){
502 irq = (dno<<2)|(pin-1);
510 * Find a matching interrupt entry from the list of interrupts
511 * attached to this bus.
513 for(aintr = bus->aintr; aintr; aintr = aintr->next){
514 if(aintr->intr->irq != irq)
517 PCMPintr* p = aintr->intr;
518 print("mpintrenablex: bus %d intin %d irq %d\n",
519 p->busno, p->intin, p->irq);
522 * Check if already enabled. Multifunction devices may share
523 * INT[A-D]# so, if already enabled, check the polarity matches
524 * and the trigger is level.
526 * Should check the devices differ only in the function number,
527 * but that can wait for the planned enable/disable rewrite.
528 * The RDT read here is safe for now as currently interrupts
529 * are never disabled once enabled.
532 ioapicrdtr(apic, aintr->intr->intin, 0, &lo);
533 if(!(lo & ApicIMASK)){
535 if(0) print("%s vector %d (!imask)\n", v->name, vno);
536 n = mpintrinit(bus, aintr->intr, vno, v->irq);
537 n |= ApicPHYSICAL; /* no-op */
538 lo &= ~(ApicRemoteIRR|ApicDELIVS);
540 print("mpintrenable: multiple botch irq %d, tbdf %uX, lo %8.8uX, n %8.8uX\n",
541 v->irq, tbdf, lo, n);
550 hi = mpintrcpu()<<24;
551 lo = mpintrinit(bus, aintr->intr, vno, v->irq);
552 lo |= ApicPHYSICAL; /* no-op */
554 print("mpintrenable: disabled irq %d, tbdf %uX, lo %8.8uX, hi %8.8uX\n",
555 v->irq, tbdf, lo, hi);
558 if((apic->flags & PcmpEN) && apic->type == PcmpIOAPIC)
559 ioapicrdtw(apic, aintr->intr->intin, hi, lo);
570 MSICtrl = 0x02, /* message control register (16 bit) */
571 MSIAddr = 0x04, /* message address register (64 bit) */
572 MSIData32 = 0x08, /* message data register for 32 bit MSI (16 bit) */
573 MSIData64 = 0x0C, /* message data register for 64 bit MSI (16 bit) */
577 msiintrenable(Vctl *v)
579 int tbdf, vno, cap, cpu, ok64;
582 if(getconf("*msi") == nil)
585 if(tbdf == BUSUNKNOWN || BUSTYPE(tbdf) != BusPCI)
587 pci = pcimatchtbdf(tbdf);
589 print("msiintrenable: could not find Pcidev for tbdf %uX\n", tbdf);
592 cap = pcicap(pci, PciCapMSI);
597 ok64 = (pcicfgr16(pci, cap + MSICtrl) & (1<<7)) != 0;
598 pcicfgw32(pci, cap + MSIAddr, (0xFEE << 20) | (cpu << 12));
599 if(ok64) pcicfgw32(pci, cap + MSIAddr + 4, 0);
600 pcicfgw16(pci, cap + (ok64 ? MSIData64 : MSIData32), vno | (1<<14));
601 pcicfgw16(pci, cap + MSICtrl, 1);
608 mpintrenable(Vctl* v)
612 vno = msiintrenable(v);
617 * If the bus is known, try it.
618 * BUSUNKNOWN is given both by [E]ISA devices and by
619 * interrupts local to the processor (local APIC, coprocessor
620 * breakpoint and page-fault).
623 if(tbdf != BUSUNKNOWN && (vno = mpintrenablex(v, tbdf)) != -1)
627 if(irq >= IrqLINT0 && irq <= MaxIrqLAPIC){
628 if(irq != IrqSPURIOUS)
630 return VectorPIC+irq;
632 if(irq < 0 || irq > MaxIrqPIC){
633 print("mpintrenable: irq %d out of range\n", irq);
638 * Either didn't find it or have to try the default buses
639 * (ISA and EISA). This hack is due to either over-zealousness
640 * or laziness on the part of some manufacturers.
642 * The MP configuration table on some older systems
643 * (e.g. ASUS PCI/E-P54NP4) has an entry for the EISA bus
644 * but none for ISA. It also has the interrupt type and
645 * polarity set to 'default for this bus' which wouldn't
646 * be compatible with ISA.
649 vno = mpintrenablex(v, MKBUS(BusEISA, 0, 0, 0));
654 vno = mpintrenablex(v, MKBUS(BusISA, 0, 0, 0));
658 print("mpintrenable: out of choices eisa %d isa %d tbdf %uX irq %d\n",
659 mpeisabus, mpisabus, v->tbdf, v->irq);
667 static Lock shutdownlock;
672 if(!canlock(&shutdownlock)){
674 * If this processor received the CTRL-ALT-DEL from
675 * the keyboard, acknowledge it. Send an INIT to self.
678 if(lapicisr(VectorKBD))
680 #endif /* FIX THIS */
685 print("apshutdown: active = %#8.8ux\n", active.machs);
690 * INIT all excluding self.
692 lapicicrw(0, 0x000C0000|ApicINIT);
698 * Often the BIOS hangs during restart if a conventional 8042
699 * warm-boot sequence is tried. The following is Intel specific and
700 * seems to perform a cold-boot, but at least it comes back.
701 * And sometimes there is no keyboard...
703 * The reset register (0xcf9) is usually in one of the bridge
704 * chips. The actual location and sequence could be extracted from
705 * ACPI but why bother, this is the end of the line anyway.
707 print("no kbd; trying bios warm boot...");
708 *(ushort*)KADDR(0x472) = 0x1234; /* BIOS warm-boot flag */
712 print("can't reset\n");