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