xref: /inferno-os/os/pc/mp.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
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