]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/mp.c
pc, pc64: implement acpi reset (for efi)
[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 /* filled in by pcmpinit or acpiinit */
13 Bus* mpbus;
14 Bus* mpbuslast;
15 int mpisabus = -1;
16 int mpeisabus = -1;
17 Apic *mpioapic[MaxAPICNO+1];
18 Apic *mpapic[MaxAPICNO+1];
19
20 int
21 mpintrinit(Bus* bus, PCMPintr* intr, int vno, int /*irq*/)
22 {
23         int el, po, v;
24
25         /*
26          * Parse an I/O or Local APIC interrupt table entry and
27          * return the encoded vector.
28          */
29         v = vno;
30
31         po = intr->flags & PcmpPOMASK;
32         el = intr->flags & PcmpELMASK;
33
34         switch(intr->intr){
35         default:                                /* PcmpINT */
36                 v |= ApicFIXED;                 /* no-op */
37                 break;
38
39         case PcmpNMI:
40                 v |= ApicNMI;
41                 po = PcmpHIGH;
42                 el = PcmpEDGE;
43                 break;
44
45         case PcmpSMI:
46                 v |= ApicSMI;
47                 break;
48
49         case PcmpExtINT:
50                 v |= ApicExtINT;
51                 /*
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.
60                  */
61                 po = PcmpHIGH;
62                 el = PcmpEDGE;
63                 break;
64         }
65
66         /*
67          */
68         if(bus->type == BusEISA && !po && !el /*&& !(i8259elcr & (1<<irq))*/){
69                 po = PcmpHIGH;
70                 el = PcmpEDGE;
71         }
72         if(!po)
73                 po = bus->po;
74         if(po == PcmpLOW)
75                 v |= ApicLOW;
76         else if(po != PcmpHIGH){
77                 print("mpintrinit: bad polarity 0x%uX\n", po);
78                 return ApicIMASK;
79         }
80
81         if(!el)
82                 el = bus->el;
83         if(el == PcmpLEVEL)
84                 v |= ApicLEVEL;
85         else if(el != PcmpEDGE){
86                 print("mpintrinit: bad trigger 0x%uX\n", el);
87                 return ApicIMASK;
88         }
89
90         return v;
91 }
92
93 uvlong
94 tscticks(uvlong *hz)
95 {
96         if(hz != nil)
97                 *hz = m->cpuhz;
98
99         cycles(&m->tscticks);   /* Uses the rdtsc instruction */
100         return m->tscticks;
101 }
102
103 void
104 syncclock(void)
105 {
106         uvlong x;
107
108         if(arch->fastclock != tscticks)
109                 return;
110
111         if(m->machno == 0){
112                 wrmsr(0x10, 0);
113                 m->tscticks = 0;
114         } else {
115                 x = MACHP(0)->tscticks;
116                 while(x == MACHP(0)->tscticks)
117                         ;
118                 wrmsr(0x10, MACHP(0)->tscticks);
119                 cycles(&m->tscticks);
120         }
121 }
122
123 void
124 mpinit(void)
125 {
126         int ncpu, i;
127         Apic *apic;
128         char *cp;
129
130         i8259init();
131         syncclock();
132
133         if(getconf("*apicdebug")){
134                 Bus *b;
135                 Aintr *ai;
136                 PCMPintr *pi;
137
138                 for(i=0; i<=MaxAPICNO; i++){
139                         if(apic = mpapic[i])
140                                 print("LAPIC%d: pa=%lux va=%#p flags=%x\n",
141                                         i, apic->paddr, apic->addr, apic->flags);
142                         if(apic = mpioapic[i])
143                                 print("IOAPIC%d: pa=%lux va=%#p flags=%x gsibase=%d mre=%d\n",
144                                         i, apic->paddr, apic->addr, apic->flags, apic->gsibase, apic->mre);
145                 }
146                 for(b = mpbus; b; b = b->next){
147                         print("BUS%d type=%d flags=%x\n", b->busno, b->type, b->po|b->el);
148                         for(ai = b->aintr; ai; ai = ai->next){
149                                 if(pi = ai->intr)
150                                         print("\ttype=%d irq=%d (%d [%c]) apic=%d intin=%d flags=%x\n",
151                                                 pi->type, pi->irq, pi->irq>>2, "ABCD"[pi->irq&3],
152                                                 pi->apicno, pi->intin, pi->flags);
153                         }
154                 }
155         }
156
157         apic = nil;
158         for(i=0; i<=MaxAPICNO; i++){
159                 if(mpapic[i] == nil)
160                         continue;
161                 if(mpapic[i]->flags & PcmpBP){
162                         apic = mpapic[i];
163                         break;
164                 }
165         }
166
167         if(apic == nil){
168                 panic("mpinit: no bootstrap processor");
169                 return;
170         }
171         apic->online = 1;
172
173         lapicinit(apic);
174
175         /*
176          * These interrupts are local to the processor
177          * and do not appear in the I/O APIC so it is OK
178          * to set them now.
179          */
180         intrenable(IrqTIMER, lapicclock, 0, BUSUNKNOWN, "clock");
181         intrenable(IrqERROR, lapicerror, 0, BUSUNKNOWN, "lapicerror");
182         intrenable(IrqSPURIOUS, lapicspurious, 0, BUSUNKNOWN, "lapicspurious");
183         lapiconline();
184
185         /*
186          * Initialise the application processors.
187          */
188         if(cp = getconf("*ncpu")){
189                 ncpu = strtol(cp, 0, 0);
190                 if(ncpu < 1)
191                         ncpu = 1;
192                 else if(ncpu > MAXMACH)
193                         ncpu = MAXMACH;
194         }
195         else
196                 ncpu = MAXMACH;
197         memmove((void*)APBOOTSTRAP, apbootstrap, sizeof(apbootstrap));
198         for(i=0; i<nelem(mpapic); i++){
199                 if((apic = mpapic[i]) == nil)
200                         continue;
201                 if(ncpu <= 1)
202                         break;
203                 if((apic->flags & (PcmpBP|PcmpEN)) == PcmpEN){
204                         mpstartap(apic);
205                         conf.nmach++;
206                         ncpu--;
207                 }
208         }
209
210         /*
211          *  we don't really know the number of processors till
212          *  here.
213          *
214          *  set conf.copymode here if nmach > 1.
215          *  Should look for an ExtINT line and enable it.
216          */
217         if(X86FAMILY(m->cpuidax) == 3 || conf.nmach > 1)
218                 conf.copymode = 1;
219 }
220
221 static int
222 mpintrcpu(void)
223 {
224         static Lock physidlock;
225         static int physid;
226         int i;
227
228         /*
229          * The bulk of this code was written ~1995, when there was
230          * one architecture and one generation of hardware, the number
231          * of CPUs was up to 4(8) and the choices for interrupt routing
232          * were physical, or flat logical (optionally with lowest
233          * priority interrupt). Logical mode hasn't scaled well with
234          * the increasing number of packages/cores/threads, so the
235          * fall-back is to physical mode, which works across all processor
236          * generations, both AMD and Intel, using the APIC and xAPIC.
237          *
238          * Interrupt routing policy can be set here.
239          * Currently, just assign each interrupt to a different CPU on
240          * a round-robin basis. Some idea of the packages/cores/thread
241          * topology would be useful here, e.g. to not assign interrupts
242          * to more than one thread in a core, or to use a "noise" core.
243          * But, as usual, Intel make that an onerous task. 
244          */
245         lock(&physidlock);
246         for(;;){
247                 i = physid++;
248                 if(physid >= nelem(mpapic))
249                         physid = 0;
250                 if(mpapic[i] == nil)
251                         continue;
252                 if(mpapic[i]->online)
253                         break;
254         }
255         unlock(&physidlock);
256
257         return mpapic[i]->apicno;
258 }
259
260 /*
261  * With the APIC a unique vector can be assigned to each
262  * request to enable an interrupt. There are two reasons this
263  * is a good idea:
264  * 1) to prevent lost interrupts, no more than 2 interrupts
265  *    should be assigned per block of 16 vectors (there is an
266  *    in-service entry and a holding entry for each priority
267  *    level and there is one priority level per block of 16
268  *    interrupts).
269  * 2) each input pin on the IOAPIC will receive a different
270  *    vector regardless of whether the devices on that pin use
271  *    the same IRQ as devices on another pin.
272  */
273 static int
274 allocvector(void)
275 {
276         static int round = 0, num = 0;
277         static Lock l;
278         int vno;
279         
280         lock(&l);
281         vno = VectorAPIC + num;
282         if(vno < MaxVectorAPIC-7)
283                 num += 8;
284         else
285                 num = ++round % 8;
286         unlock(&l);
287         return vno;
288 }
289
290 static int
291 mpintrenablex(Vctl* v, int tbdf)
292 {
293         Bus *bus;
294         Aintr *aintr;
295         Apic *apic;
296         Pcidev *pcidev;
297         int bno, dno, pin, hi, irq, lo, n, type, vno;
298
299         type = BUSTYPE(tbdf);
300         bno = BUSBNO(tbdf);
301         dno = BUSDNO(tbdf);
302
303         pin = 0;
304         pcidev = nil;
305         if(type == BusPCI){
306                 if(pcidev = pcimatchtbdf(tbdf))
307                         pin = pcicfgr8(pcidev, PciINTP);
308         } else if(type == BusISA)
309                 bno = mpisabus;
310
311 Findbus:
312         for(bus = mpbus; bus != nil; bus = bus->next){
313                 if(bus->type != type)
314                         continue;
315                 if(bus->busno == bno)
316                         break;
317         }
318
319         if(bus == nil){
320                 /*
321                  * if the PCI device is behind a PCI-PCI bridge thats not described
322                  * by the MP or ACPI tables then walk up the bus translating interrupt
323                  * pin to parent bus.
324                  */
325                 if(pcidev && pcidev->parent && pin > 0){
326                         pin = ((dno+(pin-1))%4)+1;
327                         pcidev = pcidev->parent;
328                         bno = BUSBNO(pcidev->tbdf);
329                         dno = BUSDNO(pcidev->tbdf);
330                         goto Findbus;
331                 }
332                 print("mpintrenable: can't find bus type %d, number %d\n", type, bno);
333                 return -1;
334         }
335
336         /*
337          * For PCI devices the interrupt pin (INT[ABCD]) and device
338          * number are encoded into the entry irq field, so create something
339          * to match on.
340          */
341         if(bus->type == BusPCI){
342                 if(pin > 0)
343                         irq = (dno<<2)|(pin-1);
344                 else
345                         irq = -1;
346         }
347         else
348                 irq = v->irq;
349
350         /*
351          * Find a matching interrupt entry from the list of interrupts
352          * attached to this bus.
353          */
354         for(aintr = bus->aintr; aintr; aintr = aintr->next){
355                 if(aintr->intr->irq != irq)
356                         continue;
357                 if(0){
358                         PCMPintr* p = aintr->intr;
359                         print("mpintrenablex: bus %d intin %d irq %d\n",
360                                 p->busno, p->intin, p->irq);
361                 }
362                 /*
363                  * Check if already enabled. Multifunction devices may share
364                  * INT[A-D]# so, if already enabled, check the polarity matches
365                  * and the trigger is level.
366                  *
367                  * Should check the devices differ only in the function number,
368                  * but that can wait for the planned enable/disable rewrite.
369                  * The RDT read here is safe for now as currently interrupts
370                  * are never disabled once enabled.
371                  */
372                 apic = aintr->apic;
373                 ioapicrdtr(apic, aintr->intr->intin, 0, &lo);
374                 if(!(lo & ApicIMASK)){
375                         vno = lo & 0xFF;
376                         if(0) print("%s vector %d (!imask)\n", v->name, vno);
377                         n = mpintrinit(bus, aintr->intr, vno, v->irq);
378                         n |= ApicPHYSICAL;              /* no-op */
379                         lo &= ~(ApicRemoteIRR|ApicDELIVS);
380                         if(n != lo){
381                                 print("mpintrenable: multiple botch irq %d, tbdf %uX, lo %8.8uX, n %8.8uX\n",
382                                         v->irq, tbdf, lo, n);
383                                 return -1;
384                         }
385                         v->isr = lapicisr;
386                         v->eoi = lapiceoi;
387                         return vno;
388                 }
389
390                 vno = allocvector();
391                 hi = mpintrcpu()<<24;
392                 lo = mpintrinit(bus, aintr->intr, vno, v->irq);
393                 lo |= ApicPHYSICAL;                     /* no-op */
394                 if(lo & ApicIMASK){
395                         print("mpintrenable: disabled irq %d, tbdf %uX, lo %8.8uX, hi %8.8uX\n",
396                                 v->irq, tbdf, lo, hi);
397                         return -1;
398                 }
399                 if((apic->flags & PcmpEN) && apic->type == PcmpIOAPIC)
400                         ioapicrdtw(apic, aintr->intr->intin, hi, lo);
401
402                 v->isr = lapicisr;
403                 v->eoi = lapiceoi;
404                 return vno;
405         }
406
407         return -1;
408 }
409
410 enum {
411         MSICtrl = 0x02, /* message control register (16 bit) */
412         MSIAddr = 0x04, /* message address register (64 bit) */
413         MSIData32 = 0x08, /* message data register for 32 bit MSI (16 bit) */
414         MSIData64 = 0x0C, /* message data register for 64 bit MSI (16 bit) */
415 };
416
417 enum {
418         HTMSIMapping    = 0xA8,
419         HTMSIFlags      = 0x02,
420         HTMSIFlagsEn    = 0x01,
421 };
422
423 static int
424 htmsicapenable(Pcidev *p)
425 {
426         int cap, flags;
427
428         if((cap = pcihtcap(p, HTMSIMapping)) <= 0)
429                 return -1;
430         flags = pcicfgr8(p, cap + HTMSIFlags);
431         if((flags & HTMSIFlagsEn) == 0)
432                 pcicfgw8(p, cap + HTMSIFlags, flags | HTMSIFlagsEn);
433         return 0;
434 }
435
436 static int
437 htmsienable(Pcidev *pdev)
438 {
439         Pcidev *p;
440
441         p = nil;
442         while((p = pcimatch(p, 0x1022, 0)) != nil)
443                 if(p->did == 0x1103 || p->did == 0x1203)
444                         break;
445
446         if(p == nil)
447                 return 0;       /* not hypertransport platform */
448
449         p = nil;
450         while((p = pcimatch(p, 0x10de, 0)) != nil){
451                 switch(p->did){
452                 case 0x02f0:    /* NVIDIA NFORCE C51 MEMC0 */
453                 case 0x02f1:    /* NVIDIA NFORCE C51 MEMC1 */
454                 case 0x02f2:    /* NVIDIA NFORCE C51 MEMC2 */
455                 case 0x02f3:    /* NVIDIA NFORCE C51 MEMC3 */
456                 case 0x02f4:    /* NVIDIA NFORCE C51 MEMC4 */
457                 case 0x02f5:    /* NVIDIA NFORCE C51 MEMC5 */
458                 case 0x02f6:    /* NVIDIA NFORCE C51 MEMC6 */
459                 case 0x02f7:    /* NVIDIA NFORCE C51 MEMC7 */
460                 case 0x0369:    /* NVIDIA NFORCE MCP55 MEMC */
461                         htmsicapenable(p);
462                         break;
463                 }
464         }
465
466         if(htmsicapenable(pdev) == 0)
467                 return 0;
468
469         for(p = pdev->parent; p != nil; p = p->parent)
470                 if(htmsicapenable(p) == 0)
471                         return 0;
472
473         return -1;
474 }
475
476 static int
477 msiintrenable(Vctl *v)
478 {
479         int tbdf, vno, cap, cpu, ok64;
480         Pcidev *pci;
481
482         if(getconf("*nomsi") != nil)
483                 return -1;
484         tbdf = v->tbdf;
485         if(tbdf == BUSUNKNOWN || BUSTYPE(tbdf) != BusPCI)
486                 return -1;
487         pci = pcimatchtbdf(tbdf);
488         if(pci == nil) {
489                 print("msiintrenable: could not find Pcidev for tbdf %uX\n", tbdf);
490                 return -1;
491         }
492         if(htmsienable(pci) < 0)
493                 return -1;
494         cap = pcicap(pci, PciCapMSI);
495         if(cap < 0)
496                 return -1;
497         vno = allocvector();
498         cpu = mpintrcpu();
499         ok64 = (pcicfgr16(pci, cap + MSICtrl) & (1<<7)) != 0;
500         pcicfgw32(pci, cap + MSIAddr, (0xFEE << 20) | (cpu << 12));
501         if(ok64) pcicfgw32(pci, cap + MSIAddr + 4, 0);
502         pcicfgw16(pci, cap + (ok64 ? MSIData64 : MSIData32), vno | (1<<14));
503         pcicfgw16(pci, cap + MSICtrl, 1);
504         v->isr = lapicisr;
505         v->eoi = lapiceoi;
506         return vno;
507 }
508
509 int
510 mpintrenable(Vctl* v)
511 {
512         int irq, tbdf, vno;
513
514         vno = msiintrenable(v);
515         if(vno != -1)
516                 return vno;
517
518         /*
519          * If the bus is known, try it.
520          * BUSUNKNOWN is given both by [E]ISA devices and by
521          * interrupts local to the processor (local APIC, coprocessor
522          * breakpoint and page-fault).
523          */
524         tbdf = v->tbdf;
525         if(tbdf != BUSUNKNOWN && (vno = mpintrenablex(v, tbdf)) != -1)
526                 return vno;
527
528         irq = v->irq;
529         if(irq >= IrqLINT0 && irq <= MaxIrqLAPIC){
530                 if(irq != IrqSPURIOUS)
531                         v->isr = lapiceoi;
532                 return VectorPIC+irq;
533         }
534         if(irq < 0 || irq > MaxIrqPIC){
535                 print("mpintrenable: irq %d out of range\n", irq);
536                 return -1;
537         }
538
539         /*
540          * Either didn't find it or have to try the default buses
541          * (ISA and EISA). This hack is due to either over-zealousness 
542          * or laziness on the part of some manufacturers.
543          *
544          * The MP configuration table on some older systems
545          * (e.g. ASUS PCI/E-P54NP4) has an entry for the EISA bus
546          * but none for ISA. It also has the interrupt type and
547          * polarity set to 'default for this bus' which wouldn't
548          * be compatible with ISA.
549          */
550         if(mpeisabus != -1){
551                 vno = mpintrenablex(v, MKBUS(BusEISA, 0, 0, 0));
552                 if(vno != -1)
553                         return vno;
554         }
555         if(mpisabus != -1){
556                 vno = mpintrenablex(v, MKBUS(BusISA, 0, 0, 0));
557                 if(vno != -1)
558                         return vno;
559         }
560         print("mpintrenable: out of choices eisa %d isa %d tbdf %uX irq %d\n",
561                 mpeisabus, mpisabus, v->tbdf, v->irq);
562         return -1;
563 }
564
565
566 void
567 mpshutdown(void)
568 {
569         /*
570          * Park application processors.
571          */
572         if(m->machno != 0){
573                 splhi();
574                 arch->introff();
575                 idle();
576         }
577
578         print("mpshutdown: active = %#8.8ux\n", active.machs);
579         delay(1000);
580         splhi();
581
582         /*
583          * INIT all excluding self.
584          */
585         lapicicrw(0, 0x000C0000|ApicINIT);
586
587         pcireset();
588         acpireset();
589         i8042reset();
590
591         /*
592          * Often the BIOS hangs during restart if a conventional 8042
593          * warm-boot sequence is tried. The following is Intel specific and
594          * seems to perform a cold-boot, but at least it comes back.
595          * And sometimes there is no keyboard...
596          *
597          * The reset register (0xcf9) is usually in one of the bridge
598          * chips. The actual location and sequence could be extracted from
599          * ACPI but why bother, this is the end of the line anyway.
600          */
601         print("no kbd; trying bios warm boot...");
602         *(ushort*)KADDR(0x472) = 0x1234;        /* BIOS warm-boot flag */
603         outb(0xCF9, 0x02);
604         outb(0xCF9, 0x06);
605
606         print("can't reset\n");
607         idle();
608 }