]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/mp.c
improved interrupt vector allocation code
[plan9front.git] / sys / src / 9 / pc / mp.c
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 PCMP* mppcmp;
13 static Bus* mpbus;
14 static Bus* mpbuslast;
15 static int mpisabus = -1;
16 static int mpeisabus = -1;
17 extern int i8259elcr;                   /* mask of level-triggered interrupts */
18 static Apic mpapic[MaxAPICNO+1];
19 static int machno2apicno[MaxAPICNO+1];  /* inverse map: machno -> APIC ID */
20 static Ref mpvnoref;                    /* unique vector assignment */
21 static int mpmachno = 1;
22 static Lock mpphysidlock;
23 static int mpphysid;
24
25 static char* buses[] = {
26         "CBUSI ",
27         "CBUSII",
28         "EISA  ",
29         "FUTURE",
30         "INTERN",
31         "ISA   ",
32         "MBI   ",
33         "MBII  ",
34         "MCA   ",
35         "MPI   ",
36         "MPSA  ",
37         "NUBUS ",
38         "PCI   ",
39         "PCMCIA",
40         "TC    ",
41         "VL    ",
42         "VME   ",
43         "XPRESS",
44         0,
45 };
46
47 static Apic*
48 mkprocessor(PCMPprocessor* p)
49 {
50         int apicno;
51         Apic *apic;
52
53         apicno = p->apicno;
54         if(!(p->flags & PcmpEN) || apicno > MaxAPICNO)
55                 return 0;
56
57         apic = &mpapic[apicno];
58         apic->type = PcmpPROCESSOR;
59         apic->apicno = apicno;
60         apic->flags = p->flags;
61         apic->lintr[0] = ApicIMASK;
62         apic->lintr[1] = ApicIMASK;
63
64         if(p->flags & PcmpBP){
65                 machno2apicno[0] = apicno;
66                 apic->machno = 0;
67         }
68         else{
69                 machno2apicno[mpmachno] = apicno;
70                 apic->machno = mpmachno;
71                 mpmachno++;
72         }
73
74         return apic;
75 }
76
77 static Bus*
78 mkbus(PCMPbus* p)
79 {
80         Bus *bus;
81         int i;
82
83         for(i = 0; buses[i]; i++){
84                 if(strncmp(buses[i], p->string, sizeof(p->string)) == 0)
85                         break;
86         }
87         if(buses[i] == 0)
88                 return 0;
89
90         bus = xalloc(sizeof(Bus));
91         if(mpbus)
92                 mpbuslast->next = bus;
93         else
94                 mpbus = bus;
95         mpbuslast = bus;
96
97         bus->type = i;
98         bus->busno = p->busno;
99         if(bus->type == BusEISA){
100                 bus->po = PcmpLOW;
101                 bus->el = PcmpLEVEL;
102                 if(mpeisabus != -1)
103                         print("mkbus: more than one EISA bus\n");
104                 mpeisabus = bus->busno;
105         }
106         else if(bus->type == BusPCI){
107                 bus->po = PcmpLOW;
108                 bus->el = PcmpLEVEL;
109         }
110         else if(bus->type == BusISA){
111                 bus->po = PcmpHIGH;
112                 bus->el = PcmpEDGE;
113                 if(mpisabus != -1)
114                         print("mkbus: more than one ISA bus\n");
115                 mpisabus = bus->busno;
116         }
117         else{
118                 bus->po = PcmpHIGH;
119                 bus->el = PcmpEDGE;
120         }
121
122         return bus;
123 }
124
125 static Bus*
126 mpgetbus(int busno)
127 {
128         Bus *bus;
129
130         for(bus = mpbus; bus; bus = bus->next){
131                 if(bus->busno == busno)
132                         return bus;
133         }
134         print("mpgetbus: can't find bus %d\n", busno);
135
136         return 0;
137 }
138
139 static Apic*
140 mkioapic(PCMPioapic* p)
141 {
142         void *va;
143         int apicno;
144         Apic *apic;
145
146         apicno = p->apicno;
147         if(!(p->flags & PcmpEN) || apicno > MaxAPICNO)
148                 return 0;
149
150         /*
151          * Map the I/O APIC.
152          */
153         if((va = vmap(p->addr, 1024)) == nil)
154                 return 0;
155
156         apic = &mpapic[apicno];
157         if(apic->flags != 0)
158                 print("mkioapic: APIC ID conflict at %d\n", p->apicno);
159         apic->type = PcmpIOAPIC;
160         apic->apicno = apicno;
161         apic->addr = va;
162         apic->paddr = p->addr;
163         apic->flags = p->flags;
164
165         return apic;
166 }
167
168 static Aintr*
169 mkiointr(PCMPintr* p)
170 {
171         Bus *bus;
172         Aintr *aintr;
173         PCMPintr* pcmpintr;
174
175         /*
176          * According to the MultiProcessor Specification, a destination
177          * I/O APIC of 0xFF means the signal is routed to all I/O APICs.
178          * It's unclear how that can possibly be correct so treat it as
179          * an error for now.
180          */
181         if(p->apicno == 0xFF)
182                 return 0;
183         if((bus = mpgetbus(p->busno)) == 0)
184                 return 0;
185
186         aintr = xalloc(sizeof(Aintr));
187         aintr->intr = p;
188
189         if(0)
190                 print("iointr: type %d intr type %d flags %#o "
191                         "bus %d irq %d apicno %d intin %d\n",
192                         p->type, p->intr, p->flags,
193                         p->busno, p->irq, p->apicno, p->intin);
194         /*
195          * Hack for Intel SR1520ML motherboard, which BIOS describes
196          * the i82575 dual ethernet controllers incorrectly.
197          */
198         if(memcmp(mppcmp->product, "INTEL   X38MLST     ", 20) == 0){
199                 if(p->busno == 1 && p->intin == 16 && p->irq == 1){
200                         pcmpintr = malloc(sizeof(PCMPintr));
201                         memmove(pcmpintr, p, sizeof(PCMPintr));
202                         print("mkiointr: %20.20s bus %d intin %d irq %d\n",
203                                 (char*)mppcmp->product,
204                                 pcmpintr->busno, pcmpintr->intin,
205                                 pcmpintr->irq);
206                         pcmpintr->intin = 17;
207                         aintr->intr = pcmpintr;
208                 }
209         }
210         aintr->apic = &mpapic[p->apicno];
211         aintr->next = bus->aintr;
212         bus->aintr = aintr;
213
214         return aintr;
215 }
216
217 static int
218 mpintrinit(Bus* bus, PCMPintr* intr, int vno, int /*irq*/)
219 {
220         int el, po, v;
221
222         /*
223          * Parse an I/O or Local APIC interrupt table entry and
224          * return the encoded vector.
225          */
226         v = vno;
227
228         po = intr->flags & PcmpPOMASK;
229         el = intr->flags & PcmpELMASK;
230
231         switch(intr->intr){
232
233         default:                                /* PcmpINT */
234                 v |= ApicFIXED;                 /* no-op */
235                 break;
236
237         case PcmpNMI:
238                 v |= ApicNMI;
239                 po = PcmpHIGH;
240                 el = PcmpEDGE;
241                 break;
242
243         case PcmpSMI:
244                 v |= ApicSMI;
245                 break;
246
247         case PcmpExtINT:
248                 v |= ApicExtINT;
249                 /*
250                  * The AMI Goliath doesn't boot successfully with it's LINTR0
251                  * entry which decodes to low+level. The PPro manual says ExtINT
252                  * should be level, whereas the Pentium is edge. Setting the
253                  * Goliath to edge+high seems to cure the problem. Other PPro
254                  * MP tables (e.g. ASUS P/I-P65UP5 have a entry which decodes
255                  * to edge+high, so who knows.
256                  * Perhaps it would be best just to not set an ExtINT entry at
257                  * all, it shouldn't be needed for SMP mode.
258                  */
259                 po = PcmpHIGH;
260                 el = PcmpEDGE;
261                 break;
262         }
263
264         /*
265          */
266         if(bus->type == BusEISA && !po && !el /*&& !(i8259elcr & (1<<irq))*/){
267                 po = PcmpHIGH;
268                 el = PcmpEDGE;
269         }
270         if(!po)
271                 po = bus->po;
272         if(po == PcmpLOW)
273                 v |= ApicLOW;
274         else if(po != PcmpHIGH){
275                 print("mpintrinit: bad polarity 0x%uX\n", po);
276                 return ApicIMASK;
277         }
278
279         if(!el)
280                 el = bus->el;
281         if(el == PcmpLEVEL)
282                 v |= ApicLEVEL;
283         else if(el != PcmpEDGE){
284                 print("mpintrinit: bad trigger 0x%uX\n", el);
285                 return ApicIMASK;
286         }
287
288         return v;
289 }
290
291 static int
292 mklintr(PCMPintr* p)
293 {
294         Apic *apic;
295         Bus *bus;
296         int intin, v;
297
298         /*
299          * The offsets of vectors for LINT[01] are known to be
300          * 0 and 1 from the local APIC vector space at VectorLAPIC.
301          */
302         if((bus = mpgetbus(p->busno)) == 0)
303                 return 0;
304         intin = p->intin;
305
306         /*
307          * Pentium Pros have problems if LINT[01] are set to ExtINT
308          * so just bag it, SMP mode shouldn't need ExtINT anyway.
309          */
310         if(p->intr == PcmpExtINT || p->intr == PcmpNMI)
311                 v = ApicIMASK;
312         else
313                 v = mpintrinit(bus, p, VectorLAPIC+intin, p->irq);
314
315         if(p->apicno == 0xFF){
316                 for(apic = mpapic; apic <= &mpapic[MaxAPICNO]; apic++){
317                         if((apic->flags & PcmpEN)
318                         && apic->type == PcmpPROCESSOR)
319                                 apic->lintr[intin] = v;
320                 }
321         }
322         else{
323                 apic = &mpapic[p->apicno];
324                 if((apic->flags & PcmpEN) && apic->type == PcmpPROCESSOR)
325                         apic->lintr[intin] = v;
326         }
327
328         return v;
329 }
330
331 static void
332 checkmtrr(void)
333 {
334         int i, vcnt;
335         Mach *mach0;
336
337         /*
338          * If there are MTRR registers, snarf them for validation.
339          */
340         if(!(m->cpuiddx & 0x1000))
341                 return;
342
343         rdmsr(0x0FE, &m->mtrrcap);
344         rdmsr(0x2FF, &m->mtrrdef);
345         if(m->mtrrcap & 0x0100){
346                 rdmsr(0x250, &m->mtrrfix[0]);
347                 rdmsr(0x258, &m->mtrrfix[1]);
348                 rdmsr(0x259, &m->mtrrfix[2]);
349                 for(i = 0; i < 8; i++)
350                         rdmsr(0x268+i, &m->mtrrfix[(i+3)]);
351         }
352         vcnt = m->mtrrcap & 0x00FF;
353         if(vcnt > nelem(m->mtrrvar))
354                 vcnt = nelem(m->mtrrvar);
355         for(i = 0; i < vcnt; i++)
356                 rdmsr(0x200+i, &m->mtrrvar[i]);
357
358         /*
359          * If not the bootstrap processor, compare.
360          */
361         if(m->machno == 0)
362                 return;
363
364         mach0 = MACHP(0);
365         if(mach0->mtrrcap != m->mtrrcap)
366                 print("mtrrcap%d: %lluX %lluX\n",
367                         m->machno, mach0->mtrrcap, m->mtrrcap);
368         if(mach0->mtrrdef != m->mtrrdef)
369                 print("mtrrdef%d: %lluX %lluX\n",
370                         m->machno, mach0->mtrrdef, m->mtrrdef);
371         for(i = 0; i < 11; i++){
372                 if(mach0->mtrrfix[i] != m->mtrrfix[i])
373                         print("mtrrfix%d: i%d: %lluX %lluX\n",
374                                 m->machno, i, mach0->mtrrfix[i], m->mtrrfix[i]);
375         }
376         for(i = 0; i < vcnt; i++){
377                 if(mach0->mtrrvar[i] != m->mtrrvar[i])
378                         print("mtrrvar%d: i%d: %lluX %lluX\n",
379                                 m->machno, i, mach0->mtrrvar[i], m->mtrrvar[i]);
380         }
381 }
382
383 static void
384 squidboy(Apic* apic)
385 {
386 //      iprint("Hello Squidboy\n");
387
388         machinit();
389         mmuinit();
390
391         cpuidentify();
392         cpuidprint();
393         checkmtrr();
394
395         apic->online = 1;
396
397         lapicinit(apic);
398         lapiconline();
399         syncclock();
400         timersinit();
401
402         fpoff();
403
404         lock(&active);
405         active.machs |= 1<<m->machno;
406         unlock(&active);
407
408         while(!active.thunderbirdsarego)
409                 microdelay(100);
410
411         schedinit();
412 }
413
414 static void
415 mpstartap(Apic* apic)
416 {
417         ulong *apbootp, *pdb, *pte;
418         Mach *mach, *mach0;
419         int i, machno;
420         uchar *p;
421
422         mach0 = MACHP(0);
423
424         /*
425          * Initialise the AP page-tables and Mach structure. The page-tables
426          * are the same as for the bootstrap processor with the exception of
427          * the PTE for the Mach structure.
428          * Xspanalloc will panic if an allocation can't be made.
429          */
430         p = xspanalloc(4*BY2PG, BY2PG, 0);
431         pdb = (ulong*)p;
432         memmove(pdb, mach0->pdb, BY2PG);
433         p += BY2PG;
434
435         if((pte = mmuwalk(pdb, MACHADDR, 1, 0)) == nil)
436                 return;
437         memmove(p, KADDR(PPN(*pte)), BY2PG);
438         *pte = PADDR(p)|PTEWRITE|PTEVALID;
439         if(mach0->havepge)
440                 *pte |= PTEGLOBAL;
441         p += BY2PG;
442
443         mach = (Mach*)p;
444         if((pte = mmuwalk(pdb, MACHADDR, 2, 0)) == nil)
445                 return;
446         *pte = PADDR(mach)|PTEWRITE|PTEVALID;
447         if(mach0->havepge)
448                 *pte |= PTEGLOBAL;
449         p += BY2PG;
450
451         machno = apic->machno;
452         MACHP(machno) = mach;
453         mach->machno = machno;
454         mach->pdb = pdb;
455         mach->gdt = (Segdesc*)p;        /* filled by mmuinit */
456
457         /*
458          * Tell the AP where its kernel vector and pdb are.
459          * The offsets are known in the AP bootstrap code.
460          */
461         apbootp = (ulong*)(APBOOTSTRAP+0x08);
462         *apbootp++ = (ulong)squidboy;
463         *apbootp++ = PADDR(pdb);
464         *apbootp = (ulong)apic;
465
466         /*
467          * Universal Startup Algorithm.
468          */
469         p = KADDR(0x467);
470         *p++ = PADDR(APBOOTSTRAP);
471         *p++ = PADDR(APBOOTSTRAP)>>8;
472         i = (PADDR(APBOOTSTRAP) & ~0xFFFF)/16;
473         /* code assumes i==0 */
474         if(i != 0)
475                 print("mp: bad APBOOTSTRAP\n");
476         *p++ = i;
477         *p = i>>8;
478
479         nvramwrite(0x0F, 0x0A);
480         lapicstartap(apic, PADDR(APBOOTSTRAP));
481         for(i = 0; i < 1000; i++){
482                 if(apic->online)
483                         break;
484                 delay(10);
485         }
486         nvramwrite(0x0F, 0x00);
487 }
488
489 static void
490 dumpmp(uchar *p, uchar *e)
491 {
492         int i;
493
494         for(i = 0; p < e; p++) {
495                 if((i % 16) == 0) print("*mp%d=", i/16);
496                 print("%.2x ", *p);
497                 if((++i % 16) == 0) print("\n");
498         }
499         if((i % 16) != 0) print("\n");
500 }
501
502 static void
503 mpoverride(uchar** newp, uchar** e)
504 {
505         int size, i, j;
506         char buf[20];
507         uchar* p;
508         char* s;
509         
510         size = atoi(getconf("*mp"));
511         if(size == 0) panic("mpoverride: invalid size in *mp");
512         *newp = p = malloc(size);
513         if(p == nil) panic("mpoverride: can't allocate memory");
514         *e = p + size;
515         for(i = 0; ; i++){
516                 snprint(buf, sizeof buf, "*mp%d", i);
517                 s = getconf(buf);
518                 if(s == nil) break;
519                 while(*s){
520                         j = strtol(s, &s, 16);
521                         if(*s && *s != ' ' || j < 0 || j > 0xff) panic("mpoverride: invalid entry in %s", buf);
522                         if(p >= *e) panic("mpoverride: overflow in %s", buf);
523                         *p++ = j;
524                 }
525         }
526         if(p != *e) panic("mpoverride: size doesn't match");
527 }
528
529 void
530 mpinit(void)
531 {
532         int ncpu;
533         char *cp;
534         PCMP *pcmp;
535         uchar *e, *p;
536         Apic *apic, *bpapic;
537         void *va;
538
539         i8259init();
540         syncclock();
541
542         if(_mp_ == 0)
543                 return;
544         pcmp = KADDR(_mp_->physaddr);
545
546         /*
547          * Map the local APIC.
548          */
549         if((va = vmap(pcmp->lapicbase, 1024)) == nil)
550                 return;
551         mppcmp = pcmp;
552         print("LAPIC: %.8lux %.8lux\n", pcmp->lapicbase, (ulong)va);
553
554         bpapic = nil;
555
556         /*
557          * Run through the table saving information needed for starting
558          * application processors and initialising any I/O APICs. The table
559          * is guaranteed to be in order such that only one pass is necessary.
560          */
561         p = ((uchar*)pcmp)+sizeof(PCMP);
562         e = ((uchar*)pcmp)+pcmp->length;
563         if(getconf("*dumpmp") != nil)
564                 dumpmp(p, e);
565         if(getconf("*mp") != nil)
566                 mpoverride(&p, &e);
567         while(p < e) switch(*p){
568
569         default:
570                 print("mpinit: unknown PCMP type 0x%uX (e-p 0x%luX)\n",
571                         *p, e-p);
572                 while(p < e){
573                         print("%uX ", *p);
574                         p++;
575                 }
576                 break;
577
578         case PcmpPROCESSOR:
579                 if(apic = mkprocessor((PCMPprocessor*)p)){
580                         /*
581                          * Must take a note of bootstrap processor APIC
582                          * now as it will be needed in order to start the
583                          * application processors later and there's no
584                          * guarantee that the bootstrap processor appears
585                          * first in the table before the others.
586                          */
587                         apic->addr = va;
588                         apic->paddr = pcmp->lapicbase;
589                         if(apic->flags & PcmpBP)
590                                 bpapic = apic;
591                 }
592                 p += sizeof(PCMPprocessor);
593                 continue;
594
595         case PcmpBUS:
596                 mkbus((PCMPbus*)p);
597                 p += sizeof(PCMPbus);
598                 continue;
599
600         case PcmpIOAPIC:
601                 if(apic = mkioapic((PCMPioapic*)p))
602                         ioapicinit(apic, ((PCMPioapic*)p)->apicno);
603                 p += sizeof(PCMPioapic);
604                 continue;
605
606         case PcmpIOINTR:
607                 mkiointr((PCMPintr*)p);
608                 p += sizeof(PCMPintr);
609                 continue;
610
611         case PcmpLINTR:
612                 mklintr((PCMPintr*)p);
613                 p += sizeof(PCMPintr);
614                 continue;
615         }
616
617         /*
618          * No bootstrap processor, no need to go further.
619          */
620         if(bpapic == 0)
621                 return;
622         bpapic->online = 1;
623
624         lapicinit(bpapic);
625
626         /*
627          * These interrupts are local to the processor
628          * and do not appear in the I/O APIC so it is OK
629          * to set them now.
630          */
631         intrenable(IrqTIMER, lapicclock, 0, BUSUNKNOWN, "clock");
632         intrenable(IrqERROR, lapicerror, 0, BUSUNKNOWN, "lapicerror");
633         intrenable(IrqSPURIOUS, lapicspurious, 0, BUSUNKNOWN, "lapicspurious");
634         lapiconline();
635
636         checkmtrr();
637
638         /*
639          * Initialise the application processors.
640          */
641         if(cp = getconf("*ncpu")){
642                 ncpu = strtol(cp, 0, 0);
643                 if(ncpu < 1)
644                         ncpu = 1;
645                 else if(ncpu > MAXMACH)
646                         ncpu = MAXMACH;
647         }
648         else
649                 ncpu = MAXMACH;
650         memmove((void*)APBOOTSTRAP, apbootstrap, sizeof(apbootstrap));
651         for(apic = mpapic; apic <= &mpapic[MaxAPICNO]; apic++){
652                 if(ncpu <= 1)
653                         break;
654                 if((apic->flags & (PcmpBP|PcmpEN)) == PcmpEN
655                 && apic->type == PcmpPROCESSOR){
656                         mpstartap(apic);
657                         conf.nmach++;
658                         ncpu--;
659                 }
660         }
661
662         /*
663          *  we don't really know the number of processors till
664          *  here.
665          *
666          *  set conf.copymode here if nmach > 1.
667          *  Should look for an ExtINT line and enable it.
668          */
669         if(X86FAMILY(m->cpuidax) == 3 || conf.nmach > 1)
670                 conf.copymode = 1;
671 }
672
673 static int
674 mpintrcpu(void)
675 {
676         int i;
677
678         /*
679          * The bulk of this code was written ~1995, when there was
680          * one architecture and one generation of hardware, the number
681          * of CPUs was up to 4(8) and the choices for interrupt routing
682          * were physical, or flat logical (optionally with lowest
683          * priority interrupt). Logical mode hasn't scaled well with
684          * the increasing number of packages/cores/threads, so the
685          * fall-back is to physical mode, which works across all processor
686          * generations, both AMD and Intel, using the APIC and xAPIC.
687          *
688          * Interrupt routing policy can be set here.
689          * Currently, just assign each interrupt to a different CPU on
690          * a round-robin basis. Some idea of the packages/cores/thread
691          * topology would be useful here, e.g. to not assign interrupts
692          * to more than one thread in a core, or to use a "noise" core.
693          * But, as usual, Intel make that an onerous task. 
694          */
695         lock(&mpphysidlock);
696         for(;;){
697                 i = mpphysid++;
698                 if(mpphysid >= MaxAPICNO+1)
699                         mpphysid = 0;
700                 if(mpapic[i].online)
701                         break;
702         }
703         unlock(&mpphysidlock);
704
705         return mpapic[i].apicno;
706 }
707
708 /* hardcoded VectorAPIC and stuff. bad. */
709 static int
710 allocvector(void)
711 {
712         static int round = 0, num = 1;
713         static Lock l;
714         int vno;
715         
716         lock(&l);
717         if(num >= 24) {
718                 if(++round >= 8) round = 0;
719                 num = 1;
720         }
721         vno = 64 + num++ * 8 + round;
722         unlock(&l);
723         return vno;
724 }
725
726 static int
727 mpintrenablex(Vctl* v, int tbdf)
728 {
729         Bus *bus;
730         Aintr *aintr;
731         Apic *apic;
732         Pcidev *pcidev;
733         int bno, dno, hi, irq, lo, n, type, vno;
734
735         /*
736          * Find the bus.
737          */
738         type = BUSTYPE(tbdf);
739         bno = BUSBNO(tbdf);
740         dno = BUSDNO(tbdf);
741         if(type == BusISA)
742                 bno = mpisabus;
743         for(bus = mpbus; bus != nil; bus = bus->next){
744                 if(bus->type != type)
745                         continue;
746                 if(bus->busno == bno)
747                         break;
748         }
749         if(bus == nil){
750                 print("ioapicirq: can't find bus type %d, number %d\n", type, bno);
751                 return -1;
752         }
753
754         /*
755          * For PCI devices the interrupt pin (INT[ABCD]) and device
756          * number are encoded into the entry irq field, so create something
757          * to match on. The interrupt pin used by the device has to be
758          * obtained from the PCI config space.
759          */
760         if(bus->type == BusPCI){
761                 pcidev = pcimatchtbdf(tbdf);
762                 if(pcidev != nil && (n = pcicfgr8(pcidev, PciINTP)) != 0)
763                         irq = (dno<<2)|(n-1);
764                 else
765                         irq = -1;
766                 //print("pcidev %uX: irq %uX v->irq %uX\n", tbdf, irq, v->irq);
767         }
768         else
769                 irq = v->irq;
770
771         /*
772          * Find a matching interrupt entry from the list of interrupts
773          * attached to this bus.
774          */
775         for(aintr = bus->aintr; aintr; aintr = aintr->next){
776                 if(aintr->intr->irq != irq)
777                         continue;
778                 if (0) {
779                         PCMPintr* p = aintr->intr;
780
781                         print("mpintrenablex: bus %d intin %d irq %d\n",
782                                 p->busno, p->intin, p->irq);
783                 }
784                 /*
785                  * Check if already enabled. Multifunction devices may share
786                  * INT[A-D]# so, if already enabled, check the polarity matches
787                  * and the trigger is level.
788                  *
789                  * Should check the devices differ only in the function number,
790                  * but that can wait for the planned enable/disable rewrite.
791                  * The RDT read here is safe for now as currently interrupts
792                  * are never disabled once enabled.
793                  */
794                 apic = aintr->apic;
795                 ioapicrdtr(apic, aintr->intr->intin, 0, &lo);
796                 if(!(lo & ApicIMASK)){
797                         vno = lo & 0xFF;
798 //print("%s vector %d (!imask)\n", v->name, vno);
799                         n = mpintrinit(bus, aintr->intr, vno, v->irq);
800                         n |= ApicPHYSICAL;              /* no-op */
801                         lo &= ~(ApicRemoteIRR|ApicDELIVS);
802                         if(n != lo || !(n & ApicLEVEL)){
803                                 print("mpintrenable: multiple botch irq%d, tbdf %uX, lo %8.8uX, n %8.8uX\n",
804                                         v->irq, tbdf, lo, n);
805                                 return -1;
806                         }
807
808                         v->isr = lapicisr;
809                         v->eoi = lapiceoi;
810
811                         return vno;
812                 }
813
814                 /*
815                  * With the APIC a unique vector can be assigned to each
816                  * request to enable an interrupt. There are two reasons this
817                  * is a good idea:
818                  * 1) to prevent lost interrupts, no more than 2 interrupts
819                  *    should be assigned per block of 16 vectors (there is an
820                  *    in-service entry and a holding entry for each priority
821                  *    level and there is one priority level per block of 16
822                  *    interrupts).
823                  * 2) each input pin on the IOAPIC will receive a different
824                  *    vector regardless of whether the devices on that pin use
825                  *    the same IRQ as devices on another pin.
826                  */
827                 vno = allocvector();
828                 hi = mpintrcpu()<<24;
829                 lo = mpintrinit(bus, aintr->intr, vno, v->irq);
830                 //print("lo 0x%uX: busno %d intr %d vno %d irq %d elcr 0x%uX\n",
831                 //      lo, bus->busno, aintr->intr->irq, vno,
832                 //      v->irq, i8259elcr);
833                 if(lo & ApicIMASK)
834                         return -1;
835
836                 lo |= ApicPHYSICAL;                     /* no-op */
837
838                 if((apic->flags & PcmpEN) && apic->type == PcmpIOAPIC)
839                         ioapicrdtw(apic, aintr->intr->intin, hi, lo);
840                 //else
841                 //      print("lo not enabled 0x%uX %d\n",
842                 //              apic->flags, apic->type);
843
844                 v->isr = lapicisr;
845                 v->eoi = lapiceoi;
846
847                 return vno;
848         }
849
850         return -1;
851 }
852
853 enum {
854         MSICtrl = 0x02, /* message control register (16 bit) */
855         MSIAddr = 0x04, /* message address register (64 bit) */
856         MSIData = 0x0C, /* message data register (16 bit) */
857 };
858
859 static int
860 msiintrenable(Vctl *v)
861 {
862         int tbdf, vno, cap, cpu;
863         Pcidev *pci;
864
865         if(getconf("*msi") == nil)
866                 return -1;
867         tbdf = v->tbdf;
868         if(tbdf == BUSUNKNOWN || BUSTYPE(tbdf) != BusPCI)
869                 return -1;
870         pci = pcimatchtbdf(tbdf);
871         if(pci == nil) {
872                 print("msiintrenable: could not find Pcidev for tbdf %.8x\n", tbdf);
873                 return -1;
874         }
875         cap = 0;
876         for(;;) {
877                 cap = pcinextcap(pci, cap);
878                 if(cap == 0)
879                         return -1;
880                 if(pcicfgr8(pci, cap) == 0x05) /* MSI block */
881                         break;
882         }
883         
884         vno = allocvector();
885         cpu = mpintrcpu();
886         pcicfgw32(pci, cap + MSIAddr, (0xFEE << 20) | (cpu << 12));
887         pcicfgw32(pci, cap + MSIAddr + 4, 0);
888         pcicfgw16(pci, cap + MSIData, vno | (1<<14));
889         pcicfgw16(pci, cap + MSICtrl, 1);
890         print("msiintrenable: success with tbdf %.8x, vector %d, cpu %d\n", tbdf, vno, cpu);
891         v->isr = lapicisr;
892         v->eoi = lapiceoi;
893         return vno;
894 }
895
896 int
897 mpintrenable(Vctl* v)
898 {
899         int irq, tbdf, vno;
900
901         vno = msiintrenable(v);
902         if(vno != -1)
903                 return vno;
904
905         /*
906          * If the bus is known, try it.
907          * BUSUNKNOWN is given both by [E]ISA devices and by
908          * interrupts local to the processor (local APIC, coprocessor
909          * breakpoint and page-fault).
910          */
911         tbdf = v->tbdf;
912         if(tbdf != BUSUNKNOWN && (vno = mpintrenablex(v, tbdf)) != -1)
913                 return vno;
914
915         irq = v->irq;
916         if(irq >= IrqLINT0 && irq <= MaxIrqLAPIC){
917                 if(irq != IrqSPURIOUS)
918                         v->isr = lapiceoi;
919                 return VectorPIC+irq;
920         }
921         if(irq < 0 || irq > MaxIrqPIC){
922                 print("mpintrenable: irq %d out of range\n", irq);
923                 return -1;
924         }
925
926         /*
927          * Either didn't find it or have to try the default buses
928          * (ISA and EISA). This hack is due to either over-zealousness 
929          * or laziness on the part of some manufacturers.
930          *
931          * The MP configuration table on some older systems
932          * (e.g. ASUS PCI/E-P54NP4) has an entry for the EISA bus
933          * but none for ISA. It also has the interrupt type and
934          * polarity set to 'default for this bus' which wouldn't
935          * be compatible with ISA.
936          */
937         if(mpeisabus != -1){
938                 vno = mpintrenablex(v, MKBUS(BusEISA, 0, 0, 0));
939                 if(vno != -1)
940                         return vno;
941         }
942         if(mpisabus != -1){
943                 vno = mpintrenablex(v, MKBUS(BusISA, 0, 0, 0));
944                 if(vno != -1)
945                         return vno;
946         }
947         print("mpintrenable: out of choices eisa %d isa %d tbdf %#ux irq %d\n",
948                 mpeisabus, mpisabus, v->tbdf, v->irq);
949         return -1;
950 }
951
952 static Lock mpshutdownlock;
953
954 void
955 mpshutdown(void)
956 {
957         /*
958          * To be done...
959          */
960         if(!canlock(&mpshutdownlock)){
961                 /*
962                  * If this processor received the CTRL-ALT-DEL from
963                  * the keyboard, acknowledge it. Send an INIT to self.
964                  */
965 #ifdef FIXTHIS
966                 if(lapicisr(VectorKBD))
967                         lapiceoi(VectorKBD);
968 #endif /* FIX THIS */
969                 arch->introff();
970                 idle();
971         }
972
973         print("apshutdown: active = %#8.8ux\n", active.machs);
974         delay(1000);
975         splhi();
976
977         /*
978          * INIT all excluding self.
979          */
980         lapicicrw(0, 0x000C0000|ApicINIT);
981
982         pcireset();
983         i8042reset();
984
985         /*
986          * Often the BIOS hangs during restart if a conventional 8042
987          * warm-boot sequence is tried. The following is Intel specific and
988          * seems to perform a cold-boot, but at least it comes back.
989          * And sometimes there is no keyboard...
990          *
991          * The reset register (0xcf9) is usually in one of the bridge
992          * chips. The actual location and sequence could be extracted from
993          * ACPI but why bother, this is the end of the line anyway.
994          */
995         print("no kbd; trying bios warm boot...");
996         *(ushort*)KADDR(0x472) = 0x1234;        /* BIOS warm-boot flag */
997         outb(0xCF9, 0x02);
998         outb(0xCF9, 0x06);
999
1000         print("can't reset\n");
1001         for(;;)
1002                 idle();
1003 }