958 lines · 21.1 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "ureg.h" #include "mp.h"#include "apbootstrap.h" #define dprint(...)	if(mpdebug) print(__VA_ARGS__); else USED(mpdebug) /* from mpacpi.c */Apic *bootapic; int mpdebug;void (*mpacpifunc)(void); static PCMP* mppcmp;static Bus* mpbus;static Bus* mpbuslast;static int mpisabus = -1;static int mpeisabus = -1;extern int i8259elcr;			/* mask of level-triggered interrupts *//* static */ Apic mpapic[MaxAPICNO+1];/* static */ int machno2apicno[MaxAPICNO+1];	/* inverse map: machno -> APIC ID *//* static */ Apic ioapic[MaxAPICNO+1];static Ref mpvnoref;			/* unique vector assignment */static int mpmachno = 1;static Lock mpphysidlock;static int mpphysid; static char* buses[] = {	"CBUSI ",	"CBUSII",	"EISA  ",	"FUTURE",	"INTERN",	"ISA   ",	"MBI   ",	"MBII  ",	"MCA   ",	"MPI   ",	"MPSA  ",	"NUBUS ",	"PCI   ",	"PCMCIA",	"TC    ",	"VL    ",	"VME   ",	"XPRESS",	0,}; static Apic*mkprocessor(PCMPprocessor* p){	int apicno;	Apic *apic; 	apicno = p->apicno;	if(!(p->flags & PcmpEN) || apicno > MaxAPICNO)		return 0; 	apic = &mpapic[apicno];	apic->type = PcmpPROCESSOR;	apic->apicno = apicno;	apic->flags = p->flags;	apic->lintr[0] = ApicIMASK;	apic->lintr[1] = ApicIMASK; 	if(p->flags & PcmpBP){		machno2apicno[0] = apicno;		apic->machno = 0;	}	else{		machno2apicno[mpmachno] = apicno;		apic->machno = mpmachno;		mpmachno++;	} 	return apic;} static Bus*mkbus(PCMPbus* p){	Bus *bus;	int i; 	for(i = 0; buses[i]; i++){		if(strncmp(buses[i], p->string, sizeof(p->string)) == 0)			break;	}	if(buses[i] == 0)		return 0; 	bus = xalloc(sizeof(Bus));	if(mpbus)		mpbuslast->next = bus;	else		mpbus = bus;	mpbuslast = bus; 	bus->type = i;	bus->busno = p->busno;	if(bus->type == BusEISA){		bus->po = PcmpLOW;		bus->el = PcmpLEVEL;		if(mpeisabus != -1)			print("mkbus: more than one EISA bus\n");		mpeisabus = bus->busno;	}	else if(bus->type == BusPCI){		bus->po = PcmpLOW;		bus->el = PcmpLEVEL;	}	else if(bus->type == BusISA){		bus->po = PcmpHIGH;		bus->el = PcmpEDGE;		if(mpisabus != -1)			print("mkbus: more than one ISA bus\n");		mpisabus = bus->busno;	}	else{		bus->po = PcmpHIGH;		bus->el = PcmpEDGE;	} 	return bus;} static Bus*mpgetbus(int busno){	Bus *bus; 	for(bus = mpbus; bus; bus = bus->next){		if(bus->busno == busno)			return bus;	}	print("mpgetbus: can't find bus %d\n", busno); 	return 0;} static Apic*mkioapic(PCMPioapic* p){	void *va;	int apicno;	Apic *apic; 	apicno = p->apicno;	if(!(p->flags & PcmpEN) || apicno > MaxAPICNO)		return 0; 	/*	 * Map the I/O APIC.	 */	if((va = vmap(p->addr, 1024)) == nil)		return 0; 	apic = &ioapic[apicno];	apic->type = PcmpIOAPIC;	apic->apicno = apicno;	apic->addr = va;	apic->paddr = p->addr;	apic->flags = p->flags; 	return apic;} static Aintr*mkiointr(PCMPintr* p){	Bus *bus;	Aintr *aintr;	PCMPintr* pcmpintr; 	/*	 * According to the MultiProcessor Specification, a destination	 * I/O APIC of 0xFF means the signal is routed to all I/O APICs.	 * It's unclear how that can possibly be correct so treat it as	 * an error for now.	 */	if(p->apicno == 0xFF)		return 0;	if((bus = mpgetbus(p->busno)) == 0)		return 0; 	aintr = xalloc(sizeof(Aintr));	aintr->intr = p; 	if(0)		dprint("mkiointr: type %d intr type %d flags %#o "			"bus %d irq %d apicno %d intin %d\n",			p->type, p->intr, p->flags,			p->busno, p->irq, p->apicno, p->intin);	/*	 * Hack for Intel SR1520ML motherboard, which BIOS describes	 * the i82575 dual ethernet controllers incorrectly.	 */	if(mppcmp && memcmp(mppcmp->product, "INTEL   X38MLST     ", 20) == 0){		if(p->busno == 1 && p->intin == 16 && p->irq == 1){			pcmpintr = malloc(sizeof(PCMPintr));			if(pcmpintr == nil)				panic("mkiointr: no memory");			memmove(pcmpintr, p, sizeof(PCMPintr));			print("mkiointr: %20.20s bus %d intin %d irq %d\n",				(char*)mppcmp->product,				pcmpintr->busno, pcmpintr->intin,				pcmpintr->irq);			pcmpintr->intin = 17;			aintr->intr = pcmpintr;		}	}	if ((unsigned)p->apicno >= nelem(mpapic))		panic("mkiointr: apic %d out of range", p->apicno);	aintr->apic = &ioapic[p->apicno];	aintr->next = bus->aintr;	bus->aintr = aintr; 	return aintr;} static intmpintrinit(Bus* bus, PCMPintr* intr, int vno, int /*irq*/){	int el, po, v; 	/*	 * Parse an I/O or Local APIC interrupt table entry and	 * return the encoded vector.	 */	v = vno; 	po = intr->flags & PcmpPOMASK;	el = intr->flags & PcmpELMASK; 	switch(intr->intr){ 	default:				/* PcmpINT */		v |= ApicFIXED;			/* no-op */		break; 	case PcmpNMI:		v |= ApicNMI;		po = PcmpHIGH;		el = PcmpEDGE;		break; 	case PcmpSMI:		v |= ApicSMI;		break; 	case PcmpExtINT:		v |= ApicExtINT;		/*		 * The AMI Goliath doesn't boot successfully with it's LINTR0		 * entry which decodes to low+level. The PPro manual says ExtINT		 * should be level, whereas the Pentium is edge. Setting the		 * Goliath to edge+high seems to cure the problem. Other PPro		 * MP tables (e.g. ASUS P/I-P65UP5 have a entry which decodes		 * to edge+high, so who knows.		 * Perhaps it would be best just to not set an ExtINT entry at		 * all, it shouldn't be needed for SMP mode.		 */		po = PcmpHIGH;		el = PcmpEDGE;		break;	} 	/*	 */	if(bus->type == BusEISA && !po && !el /*&& !(i8259elcr & (1<<irq))*/){		po = PcmpHIGH;		el = PcmpEDGE;	}	if(!po)		po = bus->po;	if(po == PcmpLOW)		v |= ApicLOW;	else if(po != PcmpHIGH){		print("mpintrinit: bad polarity 0x%uX\n", po);		return ApicIMASK;	} 	if(!el)		el = bus->el;	if(el == PcmpLEVEL)		v |= ApicLEVEL;	else if(el != PcmpEDGE){		print("mpintrinit: bad trigger 0x%uX\n", el);		return ApicIMASK;	} 	return v;} static intmklintr(PCMPintr* p){	Apic *apic;	Bus *bus;	int intin, v; 	/*	 * The offsets of vectors for LINT[01] are known to be	 * 0 and 1 from the local APIC vector space at VectorLAPIC.	 */	if((bus = mpgetbus(p->busno)) == 0)		return 0;	intin = p->intin; 	/*	 * Pentium Pros have problems if LINT[01] are set to ExtINT	 * so just bag it, SMP mode shouldn't need ExtINT anyway.	 */	if(p->intr == PcmpExtINT || p->intr == PcmpNMI)		v = ApicIMASK;	else		v = mpintrinit(bus, p, VectorLAPIC+intin, p->irq); 	if(p->apicno == 0xFF){		for(apic = mpapic; apic <= &mpapic[MaxAPICNO]; apic++){			if((apic->flags & PcmpEN)			&& apic->type == PcmpPROCESSOR)				apic->lintr[intin] = v;		}	}	else{		if ((unsigned)p->apicno >= nelem(mpapic))			panic("mklintr: ioapic %d out of range", p->apicno);		apic = &mpapic[p->apicno];		if((apic->flags & PcmpEN) && apic->type == PcmpPROCESSOR)			apic->lintr[intin] = v;	} 	return v;} static voidcheckmtrr(void){	int i, vcnt;	Mach *mach0; 	/*	 * If there are MTRR registers, snarf them for validation.	 */	if(!(m->cpuiddx & Mtrr))		return; 	rdmsr(0x0FE, &m->mtrrcap);	rdmsr(0x2FF, &m->mtrrdef);	if(m->mtrrcap & 0x0100){		rdmsr(0x250, &m->mtrrfix[0]);		rdmsr(0x258, &m->mtrrfix[1]);		rdmsr(0x259, &m->mtrrfix[2]);		for(i = 0; i < 8; i++)			rdmsr(0x268+i, &m->mtrrfix[(i+3)]);	}	vcnt = m->mtrrcap & 0x00FF;	if(vcnt > nelem(m->mtrrvar))		vcnt = nelem(m->mtrrvar);	for(i = 0; i < vcnt; i++)		rdmsr(0x200+i, &m->mtrrvar[i]); 	/*	 * If not the bootstrap processor, compare.	 */	if(m->machno == 0)		return; 	mach0 = MACHP(0);	if(mach0->mtrrcap != m->mtrrcap)		print("mtrrcap%d: %lluX %lluX\n",			m->machno, mach0->mtrrcap, m->mtrrcap);	if(mach0->mtrrdef != m->mtrrdef)		print("mtrrdef%d: %lluX %lluX\n",			m->machno, mach0->mtrrdef, m->mtrrdef);	for(i = 0; i < 11; i++){		if(mach0->mtrrfix[i] != m->mtrrfix[i])			print("mtrrfix%d: i%d: %lluX %lluX\n",				m->machno, i, mach0->mtrrfix[i], m->mtrrfix[i]);	}	for(i = 0; i < vcnt; i++){		if(mach0->mtrrvar[i] != m->mtrrvar[i])			print("mtrrvar%d: i%d: %lluX %lluX\n",				m->machno, i, mach0->mtrrvar[i], m->mtrrvar[i]);	}} static voidsquidboy(Apic* apic){//	iprint("Hello Squidboy\n"); 	machinit();	fpsavealloc();	mmuinit(); 	cpuidentify();	cpuidprint();	checkmtrr(); 	apic->online = 1;	coherence(); 	lapicinit(apic);	lapiconline();	syncclock();	timersinit(); 	fpoff(); 	lock(&active);	active.machs |= 1<<m->machno;	unlock(&active); 	while(!active.thunderbirdsarego)		microdelay(100); 	schedinit();} static voidmpstartap(Apic* apic){	ulong *apbootp, *pdb, *pte;	Mach *mach, *mach0;	int i, machno;	uchar *p; 	mach0 = MACHP(0); 	/*	 * Initialise the AP page-tables and Mach structure. The page-tables	 * are the same as for the bootstrap processor with the exception of	 * the PTE for the Mach structure.	 * Xspanalloc will panic if an allocation can't be made.	 */	p = xspanalloc(4*BY2PG, BY2PG, 0);	pdb = (ulong*)p;	memmove(pdb, mach0->pdb, BY2PG);	p += BY2PG; 	if((pte = mmuwalk(pdb, MACHADDR, 1, 0)) == nil)		return;	memmove(p, KADDR(PPN(*pte)), BY2PG);	*pte = PADDR(p)|PTEWRITE|PTEVALID;	if(mach0->havepge)		*pte |= PTEGLOBAL;	p += BY2PG; 	mach = (Mach*)p;	if((pte = mmuwalk(pdb, MACHADDR, 2, 0)) == nil)		return;	*pte = PADDR(mach)|PTEWRITE|PTEVALID;	if(mach0->havepge)		*pte |= PTEGLOBAL;	p += BY2PG; 	machno = apic->machno;	MACHP(machno) = mach;	mach->machno = machno;	mach->pdb = pdb;	mach->gdt = (Segdesc*)p;	/* filled by mmuinit */ 	/*	 * Tell the AP where its kernel vector and pdb are.	 * The offsets are known in the AP bootstrap code.	 */	apbootp = (ulong*)(APBOOTSTRAP+0x08);	*apbootp++ = (ulong)squidboy;	/* assembler jumps here eventually */	*apbootp++ = PADDR(pdb);	*apbootp = (ulong)apic; 	/*	 * Universal Startup Algorithm.	 */	p = KADDR(0x467);		/* warm-reset vector */	*p++ = PADDR(APBOOTSTRAP);	*p++ = PADDR(APBOOTSTRAP)>>8;	i = (PADDR(APBOOTSTRAP) & ~0xFFFF)/16;	/* code assumes i==0 */	if(i != 0)		print("mp: bad APBOOTSTRAP\n");	*p++ = i;

Lines 1–500 of 958.