Plan 9 from Bell Labs’s /usr/web/sources/extra/9hist/pc/mp.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


## diffname pc/mp.c 1997/0327
## diff -e /dev/null /n/emeliedump/1997/0327/sys/src/brazil/pc/mp.c
0a
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"

#include "mp.h"
#include "apbootstrap.h"

extern int elcr;

static Bus* mpbus;
static Bus* mpbuslast;
static Apic mpapic[MaxAPICNO+1];
static int machno2apicno[MaxAPICNO+1];	/* inverse map: machno -> APIC ID */
static Lock mprdthilock;
static int mprdthi;

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)
{
	Apic *apic;

	if(!(p->flags & PcmpEN) || p->apicno > MaxAPICNO)
		return 0;

	apic = &mpapic[p->apicno];
	apic->type = PcmpPROCESSOR;
	apic->apicno = p->apicno;
	apic->flags = p->flags;
	apic->vecbase = VectorLAPIC;
	apic->lintr[0] = ApicIMASK;
	apic->lintr[1] = ApicIMASK;

	if(p->flags & PcmpBP){
		machno2apicno[0] = p->apicno;
		apic->machno = 0;
	}
	else{
		machno2apicno[conf.nmach] = p->apicno;
		apic->machno = conf.nmach;
		conf.nmach++;
	}

	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;
	}
	else if(bus->type == BusPCI){
		bus->po = PcmpLOW;
		bus->el = PcmpLEVEL;
	}
	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)
{
	Apic *apic;
	ulong addr, *pte;

	if(!(p->flags & PcmpEN) || p->apicno > MaxAPICNO)
		return 0;

	/*
	 * Map the I/O APIC. This should be in the same 4MB segment
	 * as MACHADDR so no new 2nd level table will be allocated.
	 */
	addr = p->addr;
	if((pte = mmuwalk(m->pdb, addr, 1)) == 0)
		return 0;
	if(!(*pte & PTEVALID)){
		*pte = addr|PTEWRITE|PTEUNCACHED|PTEVALID;
		mmuflushtlb(PADDR(m->pdb));
	}

	apic = &mpapic[p->apicno];
	apic->type = PcmpIOAPIC;
	apic->apicno = p->apicno;
	apic->addr = KADDR(addr);
	apic->flags = p->flags;

	return apic;
}

static Aintr*
mkiointr(PCMPintr* p)
{
	Bus *bus;
	Aintr *aintr;

	/*
	 * 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;
	aintr->apic = &mpapic[p->apicno];
	aintr->next = bus->aintr;
	bus->aintr = aintr;

	return aintr;
}

static int
mpintrinit(Bus* bus, PCMPintr* intr, int vector)
{
	int el, po, v;

	/*
	 * Parse an I/O or Local APIC interrupt table entry and
	 * return the encoded vector.
	 */
	v = vector;

	switch(intr->intr){

	default:				/* PcmpINT */
		v |= ApicLOWEST;
		break;

	case PcmpNMI:
		v |= ApicNMI;
		break;

	case PcmpSMI:
		v |= ApicSMI;
		break;

	case PcmpExtINT:
		v |= ApicExtINT;
		break;
	}

	po = intr->flags & PcmpPOMASK;
	el = intr->flags & PcmpELMASK;

	/*
	 */
	if(bus->type == BusEISA && !po && !el /*&& !(elcr & (1<<(v-VectorPIC)))*/){
		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 int
mklintr(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.
	 * Can't use apic->vecbase here as this vector may be applied
	 * to all local APICs (apicno == 0xFF).
	 */
	if((bus = mpgetbus(p->busno)) == 0)
		return 0;
	intin = p->intin;
	v = mpintrinit(bus, p, VectorLAPIC+intin);

	if(p->apicno == 0xFF){
		for(apic = mpapic; apic <= &mpapic[MaxAPICNO]; apic++){
			if((apic->flags & PcmpEN) && apic->type == PcmpPROCESSOR)
				apic->lintr[intin] = v;
		}
	}
	else{
		apic = &mpapic[p->apicno];
		if((apic->flags & PcmpEN) && apic->type == PcmpPROCESSOR)
			apic->lintr[intin] = v;
	}

	return v;
}

#define PDX(va)		((((ulong)(va))>>22) & 0x03FF)
#define PTX(va)		((((ulong)(va))>>12) & 0x03FF)

static void
squidboy(Apic* apic)
{
	int clkin;

	/*iprint("Hello Squidboy\n");*/

	machinit();
	mmuinit();

	cpuidentify();
	cpuidprint();

	lock(&mprdthilock);
	mprdthi |= (1<<apic->apicno)<<24;
	unlock(&mprdthilock);

	/*
	 * Restrain your octopus! Don't let it go out on the sea!
	 */
	while(MACHP(0)->ticks == 0)
		;

	clkin = lapicinit(apic);
	lapiconline(clkin);

	lock(&active);
	active.machs |= 1<<m->machno;
	unlock(&active);

	schedinit();
}

static void
mpstartap(Apic* apic)
{
	ulong *apbootp, *pdb, *pte;
	Mach *mach;
	int i, machno;
	uchar *p;

	/*
	 * Initialise the AP page-tables and Mach structure. These 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.
	 */
	pdb = xspanalloc(3*BY2PG, BY2PG, 0);
	memmove(pdb, (void*)CPU0PDB, BY2PG);

	pte = (ulong*)(((uchar*)pdb)+BY2PG);
	memmove(pte, (void*)CPU0MACHPTE, BY2PG);
	pdb[PDX(MACHADDR)] = PADDR(pte)|PTEWRITE|PTEVALID;

	mach = (Mach*)(((uchar*)pdb)+2*BY2PG);
	if((pte = mmuwalk(pdb, MACHADDR, 0)) == 0)
		return;
	*pte = PADDR(mach)|PTEWRITE|PTEVALID;

	machno = apic->machno;
	MACHP(machno) = mach;
	mach->machno = machno;
	mach->pdb = pdb;

	/*
	 * 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;
	*apbootp++ = PADDR(pdb);
	*apbootp = (ulong)apic;

	/*
	 * Universal Startup Algorithm.
	 */
	p = KADDR(0x467);
	*p++ = PADDR(APBOOTSTRAP);
	*p++ = PADDR(APBOOTSTRAP)>>8;
	i = (PADDR(APBOOTSTRAP) & ~0xFFFF)/16;
	*p++ = i;
	*p = i>>8;

	nvramwrite(0x0F, 0x0A);
	lapicstartap(apic, PADDR(APBOOTSTRAP));
	for(i = 0; i < 100000 && (active.machs & (1<<machno)) == 0; i++)
		microdelay(10);
	nvramwrite(0x0F, 0x00);
}

void
mpinit(void)
{
	PCMP *pcmp;
	ulong *pte;
	uchar *e, *p;
	Apic *apic;
	int clkin;

	i8259init();

	if(_mp_ == 0)
		return;
	pcmp = KADDR(_mp_->physaddr);
	clkin = 0;

	/*
	 * Map the local APIC. This should be in the same 4MB segment
	 * as MACHADDR so no new 2nd level table will be allocated.
	 */
	if((pte = mmuwalk(m->pdb, pcmp->lapicbase, 1)) == 0)
		return;
	if(!(*pte & PTEVALID)){
		*pte = pcmp->lapicbase|PTEWRITE|PTEUNCACHED|PTEVALID;
		mmuflushtlb(PADDR(m->pdb));
	}

	/*
	 * Run through the table saving information needed for starting application
	 * processors and initialising any I/O APICs. The table is guaranteed to be in
	 * order such that only one pass is necessary.
	 */
	p = ((uchar*)pcmp)+sizeof(PCMP);
	e = ((uchar*)pcmp)+pcmp->length;
	while(p < e) switch(*p){

	case PcmpPROCESSOR:
		if(apic = mkprocessor((PCMPprocessor*)p)){
			/*
			 * Must initialise the bootstrap processor APIC
			 * now as it will be needed in order to start the
			 * application processors later and there's no
			 * guarantee that the bootstrap processor appears
			 * first in the table before the others.
			 */
			apic->addr = KADDR(pcmp->lapicbase);
			if(apic->flags & PcmpBP)
				clkin = lapicinit(apic);
		}
		p += sizeof(PCMPprocessor);
		continue;

	case PcmpBUS:
		mkbus((PCMPbus*)p);
		p += sizeof(PCMPbus);
		continue;

	case PcmpIOAPIC:
		if(apic = mkioapic((PCMPioapic*)p))
			ioapicinit(apic, ((PCMPioapic*)p)->apicno);
		p += sizeof(PCMPioapic);
		continue;

	case PcmpIOINTR:
		mkiointr((PCMPintr*)p);
		p += sizeof(PCMPintr);
		continue;

	case PcmpLINTR:
		mklintr((PCMPintr*)p);
		p += sizeof(PCMPintr);
		continue;
	}

	/*
	 * Initialise all the processors.
	 */
	memmove((void*)APBOOTSTRAP, apbootstrap, sizeof(apbootstrap));
	for(apic = mpapic; apic <= &mpapic[MaxAPICNO]; apic++){
		if(!(apic->flags & PcmpEN) || apic->type != PcmpPROCESSOR)
			continue;

		if(apic->flags & PcmpBP){
			lock(&mprdthilock);
			mprdthi |= (1<<apic->apicno)<<24;
			unlock(&mprdthilock);

			/*
			 * These interrupts are local to the processor
			 * and do not appear in the I/O APIC so it is OK
			 * to set them now.
			 */
			intrenable(VectorTIMER, clockintr, 0, BUSUNKNOWN);
			intrenable(VectorERROR, lapicerror, 0, BUSUNKNOWN);
			intrenable(VectorSPURIOUS, lapicspurious, 0, BUSUNKNOWN);
			lapiconline(clkin);
			continue;
		}

		mpstartap(apic);
	}

	/*
	 * Remember to set conf.copymode here if nmach > 1.
	 * Look for an ExtINT line and enable it.
	 */
	if(conf.nmach > 1)
		conf.copymode = 1;
}

static int
mpintrenablex(int v, int tbdf, Irqctl* irqctl)
{
	Bus *bus;
	Aintr *aintr;
	Apic *apic;
	int bno, dno, lo, n, type;

	if(v >= VectorLAPIC && v <= MaxVectorLAPIC){
		if(v != VectorSPURIOUS)
			irqctl->isr = lapiceoi;
		irqctl->isintr = 1;
		return v;
	}

	if(v < VectorPIC || v > MaxVectorPIC)
		return 0;

	/*
	 * Find the bus, default is ISA.
	 * There cannot be multiple ISA or EISA buses.
	 */
	if(tbdf == BUSUNKNOWN){
		type = BusISA;
		bno = 0;
		dno = -1;
	}
	else{
		type = BUSTYPE(tbdf);
		bno = BUSBNO(tbdf);
		dno = BUSDNO(tbdf);
	}
	n = 0;
	for(bus = mpbus; bus; bus = bus->next){
		if(bus->type != type)
			continue;
		if(n == bno)
			break;
		n++;
	}
	if(bus == 0){
		/*
		 * The MP configuration table on some older systems
		 * (e.g. ASUS PCI/E-P54NP4) has an entry for the EISA bus
		 * but none for ISA. It also has the interrupt type and
		 * polarity set to 'default for this bus' which wouldn't
		 * be compatible with ISA.
		 */
		for(bus = mpbus; bus; bus = bus->next){
			if(bus->type == BusEISA)
				break;
		}
		if(bus == 0){
			print("ioapicirq: can't find bus type %d\n", type);
			return -1;
		}
	}

	/*
	 * Find the interrupt info.
	 */
	for(aintr = bus->aintr; aintr; aintr = aintr->next){
		if(bus->type == BusPCI){
			n = (aintr->intr->irq>>2) & 0x1F;
			if(n != dno)
				continue;
		}
		else{
			n = aintr->intr->irq;
			if(n != (v-VectorPIC))
				continue;
		}

		lo = mpintrinit(bus, aintr->intr, v);
		if(lo & ApicIMASK)
			return -1;
		lo |= ApicLOGICAL;

		apic = aintr->apic;
		if((apic->flags & PcmpEN) && apic->type == PcmpIOAPIC){
			lock(&mprdthilock);
 			ioapicrdtw(apic, aintr->intr->intin, mprdthi, lo);
			unlock(&mprdthilock);
		}

		irqctl->isr = lapicisr;
		irqctl->eoi = lapiceoi;
		irqctl->isintr = 1;

		return lo & 0xFF;
	}

	return -1;
}

int
mpintrenable(int v, int tbdf, Irqctl* irqctl)
{
	int r;

	r = mpintrenablex(v, tbdf, irqctl);
	if(r == -1 && tbdf != BUSUNKNOWN && _mp_->specrev == 1)
		return mpintrenablex(v, BUSUNKNOWN, irqctl);
	return r;
}

void
mpshutdown(void)
{
	/*
	 * To be done...
	 */
	if(m->machno){
		/*
		 * If this processor received the CTRL-ALT-DEL from
		 * the keyboard, acknowledge it. Send an INIT to self.
		 */
		if(lapicisr(VectorKBD))
			lapiceoi(VectorKBD);
		lapicicrw(0, 0x00040000|ApicINIT);	
		idle();
	}

	print("apshutdown: active = 0x%2.2uX\n", active.machs);
	delay(1000);
	splhi();

	/*
	 * INIT all excluding self.
	 */
	lapicicrw(0, 0x000C0000|ApicINIT);

#ifdef notdef
	/*
	 * Often the BIOS hangs during restart if a conventional 8042
	 * warm-boot sequence is tried. The following is Intel specific and
	 * seems to perform a cold-boot, but at least it comes back.
	 */
	*(ushort*)(KZERO|0x472) = 0x1234;		/* BIOS warm-boot flag */
	outb(0xCF9, 0x02);
	outb(0xCF9, 0x06);
#else
	pcireset();
	i8042reset();
#endif /* notdef */
}
.
## diffname pc/mp.c 1997/0405
## diff -e /n/emeliedump/1997/0327/sys/src/brazil/pc/mp.c /n/emeliedump/1997/0405/sys/src/brazil/pc/mp.c
464c
	/*
	 * Initialise the application processors.
	 */
	memmove((void*)APBOOTSTRAP, apbootstrap, sizeof(apbootstrap));
	for(apic = mpapic; apic <= &mpapic[MaxAPICNO]; apic++){
		if((apic->flags & (PcmpBP|PcmpEN)) == PcmpEN && apic->type == PcmpPROCESSOR)
			mpstartap(apic);
.
452,462c
	/*
	 * These interrupts are local to the processor
	 * and do not appear in the I/O APIC so it is OK
	 * to set them now.
	 */
	intrenable(VectorTIMER, clockintr, 0, BUSUNKNOWN);
	intrenable(VectorERROR, lapicerror, 0, BUSUNKNOWN);
	intrenable(VectorSPURIOUS, lapicspurious, 0, BUSUNKNOWN);
	lapiconline(clkin);
.
447,450c
	clkin = lapicinit(bpapic);
	lock(&mprdthilock);
	mprdthi |= (1<<bpapic->apicno)<<24;
	unlock(&mprdthilock);
.
442,445c
	if(bpapic == 0)
		return;
.
440c
	 * No bootstrap processor, no need to go further.
.
412c
				bpapic = apic;
.
404c
			 * Must take a note of bootstrap processor APIC
.
391a
	bpapic = 0;

.
379d
371c
	Apic *apic, *bpapic;
.
209,211d
197a
		po = PcmpHIGH;
		el = PcmpEDGE;
.
189a
	po = intr->flags & PcmpPOMASK;
	el = intr->flags & PcmpELMASK;

.
## diffname pc/mp.c 1997/0521
## diff -e /n/emeliedump/1997/0405/sys/src/brazil/pc/mp.c /n/emeliedump/1997/0521/sys/src/brazil/pc/mp.c
363a
	}
.
362c
	for(i = 0; i < 100000; i++){
		lock(&mprdthilock);
		if(mprdthi & ((1<<apic->apicno)<<24)){
			unlock(&mprdthilock);
			break;
		}
		unlock(&mprdthilock);
.
## diffname pc/mp.c 1997/0522
## diff -e /n/emeliedump/1997/0521/sys/src/brazil/pc/mp.c /n/emeliedump/1997/0522/sys/src/brazil/pc/mp.c
209a
po = PcmpHIGH;
el = PcmpEDGE;
.
## diffname pc/mp.c 1997/0524
## diff -e /n/emeliedump/1997/0522/sys/src/brazil/pc/mp.c /n/emeliedump/1997/0524/sys/src/brazil/pc/mp.c
590c
	if(r == -1 && tbdf != BUSUNKNOWN /*&& _mp_->specrev == 1*/)
.
212a
		/*
		 * 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;
.
210,211d
## diffname pc/mp.c 1997/1101
## diff -e /n/emeliedump/1997/0524/sys/src/brazil/pc/mp.c /n/emeliedump/1997/1101/sys/src/brazil/pc/mp.c
636c
	*(ushort*)KADDR(0x472) = 0x1234;		/* BIOS warm-boot flag */
.
406,409d
404c
	if(mmukmap(pcmp->lapicbase, 0, 1024) == 0)
.
401,402c
	 * Map the local APIC.
.
389d
342,343c
	mach = (Mach*)p;
	if((pte = mmuwalk(pdb, MACHADDR, 2, 0)) == nil)
.
338,340c
	if((pte = mmuwalk(pdb, MACHADDR, 1, 0)) == nil)
		return;
	memmove(p, KADDR(PPN(*pte)), BY2PG);
	*pte = PADDR(p)|PTEWRITE|PTEVALID;
	p += BY2PG;
.
335,336c
	p = xspanalloc(3*BY2PG, BY2PG, 0);
	pdb = (ulong*)p;
	memmove(pdb, mach0->pdb, BY2PG);
	p += BY2PG;
.
330,331c
	 * Initialise the AP page-tables and Mach structure. The page-tables
	 * are the same as for the bootstrap processor with the exception of
.
328a
	mach0 = MACHP(0);

.
325c
	Mach *mach, *mach0;
.
293,294c
//	iprint("Hello Squidboy\n");
.
147c
	apic->addr = KADDR(p->addr);
.
139,142d
136,137c
	if(mmukmap(p->addr, 0, 1024) == 0)
.
133,134c
	 * Map the I/O APIC.
.
127d
## diffname pc/mp.c 1998/0320
## diff -e /n/emeliedump/1997/1101/sys/src/brazil/pc/mp.c /n/emeliedump/1998/0320/sys/src/brazil/pc/mp.c
590,593c
	/*
	 * If the bus is known, try it.
	 * BUSUNKNOWN is given both by [E]ISA devices and by
	 * interrupts local to the processor (local APIC, coprocessor
	 * breakpoint and page-fault).
	 */
	if(tbdf != BUSUNKNOWN && (r = mpintrenablex(v, tbdf, irqctl)) != -1)
		return r;

	if(v >= VectorLAPIC && v <= MaxVectorLAPIC){
		if(v != VectorSPURIOUS)
			irqctl->isr = lapiceoi;
		irqctl->isintr = 1;
		return v;
	}
	if(v < VectorPIC || v > MaxVectorPIC){
		print("mpintrenable: vector %d out of range\n", v);
		return -1;
	}

	/*
	 * Either didn't find it or we have to try the default
	 * buses (ISA and EISA). This hack is due to either over-zealousness
	 * or laziness on the part of some manufacturers.
	 *
	 * The MP configuration table on some older systems
	 * (e.g. ASUS PCI/E-P54NP4) has an entry for the EISA bus
	 * but none for ISA. It also has the interrupt type and
	 * polarity set to 'default for this bus' which wouldn't
	 * be compatible with ISA.
	 */
	if(mpisabus != -1){
		r = mpintrenablex(v, MKBUS(BusISA, 0, 0, 0), irqctl);
		if(r != -1)
			return r;
	}
	if(mpeisabus != -1){
		r = mpintrenablex(v, MKBUS(BusEISA, 0, 0, 0), irqctl);
		if(r != -1)
			return r;
	}

	return -1;
.
555a
			/*
			 * Problem: there can be an entry for
			 * each of the device #INT[ABCD] lines.
			 * For now look only for #INTA.
			 */
			if(aintr->intr->irq & 0x03)
				continue;
.
531,545c
		print("ioapicirq: can't find bus type %d\n", type);
		return -1;
.
512,521c
	type = BUSTYPE(tbdf);
	bno = BUSBNO(tbdf);
	dno = BUSDNO(tbdf);
.
498,507d
100a
	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;
	}
.
95a
		if(mpeisabus != -1)
			print("mkbus: more than one EISA bus\n");
		mpeisabus = bus->busno;
.
14a
static int mpisabus = -1;
static int mpeisabus = -1;
.
## diffname pc/mp.c 1998/0401
## diff -e /n/emeliedump/1998/0320/sys/src/brazil/pc/mp.c /n/emeliedump/1998/0401/sys/src/brazil/pc/mp.c
641a
	if(active.machs){
		for(machno = 1; machno < conf.nmach; machno++){
			if(!(active.machs & (1<<machno)))
				continue;
			apicno = machno2apicno[machno];
			lapicicrw(1<<apicno, ApicNMI);
		}
	}
.
626a
	int apicno, machno;

.
499a

	consdebug = senddbgnmi;
	intrenable(VectorNMI, mpdbg, 0, BUSUNKNOWN);
.
496c
	 * Should look for an ExtINT line and enable it.
.
484a
	checkmtrr();

.
394a
static void
senddbgnmi(void)
{
	/*
	 * NMI all excluding self.
	 */
	lapicicrw(0, 0x000C0000|ApicNMI);
}

static void
mpdbg(Ureg* ureg, void*)
{
	iprint("MPDBG: cpu%d: PC 0x%uX\n", m->machno, ureg->pc);
}

.
303a
	checkmtrr();
.
289a
static void
checkmtrr(void)
{
	int i, vcnt;
	Mach *mach0;

	/*
	 * If there are MTRR registers, snarf them for validation.
	 */
	if(!(m->cpuiddx & 0x1000))
		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]);
	}
}

.
6a
#include "ureg.h"
.
## diffname pc/mp.c 1998/0605
## diff -e /n/emeliedump/1998/0401/sys/src/brazil/pc/mp.c /n/emeliedump/1998/0605/sys/src/brazil/pc/mp.c
718,725d
713d
706c
	if(!canlock(&mpshutdownlock)){
.
701,702d
697a
static Lock mpshutdownlock;

.
571,573d
449,463d
275a
	/*
	 * Pentium Pros have problems if LINT[01] are set to ExtINT
	 * so just bag it. Should be OK for SMP mode.
	 */
	if(p->intr == PcmpExtINT || p->intr == PcmpNMI)
		v = ApicIMASK;
	else
		v = mpintrinit(bus, p, VectorLAPIC+intin);

.
274d
## diffname pc/mp.c 1998/0716
## diff -e /n/emeliedump/1998/0605/sys/src/brazil/pc/mp.c /n/emeliedump/1998/0716/sys/src/brazil/pc/mp.c
545c
	lapiconline();
.
532c
	lapicinit(bpapic);
.
463d
443a
	rdmsr(0x10, &mach->clockstart);
.
377,378c
	lapicinit(apic);
	lapiconline();
.
359a

	/* synchronize cycle clocks */
	wrmsr(0x10, m->clockstart);

.
357,358d
## diffname pc/mp.c 1998/0717
## diff -e /n/emeliedump/1998/0716/sys/src/brazil/pc/mp.c /n/emeliedump/1998/0717/sys/src/brazil/pc/mp.c
446d
377c
		wrmsr(0x10, MACHP(0)->fastclock); /* synchronize fast counters */
.
359,361d
## diffname pc/mp.c 1998/0729
## diff -e /n/emeliedump/1998/0717/sys/src/brazil/pc/mp.c /n/emeliedump/1998/0729/sys/src/brazil/pc/mp.c
485a
	default:
		print("mpinit: unknown PCMP type 0x%uX (e-p 0x%uX)\n", *p, e-p);
		while(p < e){
			print("%uX ", *p);
			p++;
		}
		break;

.
## diffname pc/mp.c 1998/0825
## diff -e /n/emeliedump/1998/0729/sys/src/brazil/pc/mp.c /n/emeliedump/1998/0825/sys/src/brazil/pc/mp.c
487c
		print("mpinit: unknown PCMP type 0x%uX (e-p 0x%luX)\n", *p, e-p);
.
## diffname pc/mp.c 1998/0910
## diff -e /n/emeliedump/1998/0825/sys/src/brazil/pc/mp.c /n/emeliedump/1998/0910/sys/src/brazil/pc/mp.c
708a
#endif /* FIX THIS */
.
706a
#ifdef FIXTHIS
.
686,688c
		v->tbdf = MKBUS(BusEISA, 0, 0, 0);
		vno = mpintrenablex(v);
		if(vno != -1)
			return vno;
.
681,683c
		v->tbdf = MKBUS(BusISA, 0, 0, 0);
		vno = mpintrenablex(v);
		if(vno != -1)
			return vno;
.
670,671c
	 * Either didn't find it or have to try the default buses
	 * (ISA and EISA). This hack is due to either over-zealousness 
.
664,665c
	if(irq < 0 || irq > MaxIrqPIC){
		print("mpintrenable: irq %d out of range\n", irq);
.
658,662c
	irq = v->irq;
	if(irq >= IrqLINT0 && irq <= MaxIrqLAPIC){
		if(irq != IrqSPURIOUS)
			v->isr = lapiceoi;
		return VectorPIC+irq;
.
655,656c
	tbdf = v->tbdf;
	if(tbdf != BUSUNKNOWN && (vno = mpintrenablex(v)) != -1)
		return vno;
.
647c
	int irq, tbdf, vno;
.
645c
mpintrenable(Vctl* v)
.
638c
		return vno;
.
634,636c
		v->isr = lapicisr;
		v->eoi = lapiceoi;
.
627d
622c
		/*
		 * With the APIC a unique vector can be assigned to each request
		 * to enable an interrupt. There are two reasons this is a good idea:
		 * 1) to prevent lost interrupts, no more than 2 interrupts should
		 *    be assigned per block of 16 vectors (there is an in-service
		 *    entry and a holding entry for each priority level and there is
		 *    a priority level per block of 16 interrupts).
		 * 2) each input pin on the IOAPIC will receive a different vector
		 *    regardless of whether the devices on that pin use the same IRQ
		 *    as devices on another pin.
		 */
		vno = VectorAPIC + (incref(&mpvnoref)-1)*8;
		if(vno > MaxVectorAPIC){
			print("mpintrenable: vno %d, irq %d, tbdf %uX\n",
				vno, v->irq, v->tbdf);
			return -1;
		}
		lo = mpintrinit(bus, aintr->intr, vno);
.
616,620d
604,614c
		if(aintr->intr->irq != irq)
			continue;

		/*
		 * Check not already enabled. This is a bad thing as it implies the
		 * same device is requesting the same interrupt to be enabled multiple
		 * times. The RDT read here is safe for now as currently interrupts
		 * are never disabled once enabled.
		 */
		apic = aintr->apic;
		ioapicrdtr(apic, aintr->intr->intin, 0, &lo);
		if(!(lo & ApicIMASK)){
			print("mpintrenable: multiple enable of irq %d, tbdf %uX\n",
				v->irq, v->tbdf);
			return -1;
.
602a
	if(bus->type == BusPCI){
		pcidev = pcimatchtbdf(v->tbdf);
		if(pcidev != nil && (n = pcicfgr8(pcidev, PciINTP)) != 0)
			irq = (dno<<2)|(n-1);
		else
			irq = -1;
		//print("pcidev %uX: irq %uX v->irq %uX\n", v->tbdf, irq, v->irq);
	}
	else
		irq = v->irq;

	/*
	 * Find a matching interrupt entry from the list of interrupts
	 * attached to this bus.
	 */
.
601c
	 * For PCI devices the interrupt pin (INT[ABCD]) and device
	 * number are encoded into the entry irq field, so create something
	 * to match on. The interrupt pin used by the device has to be
	 * obtained from the PCI config space.
.
595c
	if(bus == nil){
.
588c
	for(bus = mpbus; bus != nil; bus = bus->next){
.
584,586c
	type = BUSTYPE(v->tbdf);
	bno = BUSBNO(v->tbdf);
	dno = BUSDNO(v->tbdf);
.
578c
	Pcidev *pcidev;
	int bno, dno, irq, lo, n, type, vno;
.
573c
mpintrenablex(Vctl* v)
.
548,550c
	intrenable(IrqTIMER, clockintr, 0, BUSUNKNOWN);
	intrenable(IrqERROR, lapicerror, 0, BUSUNKNOWN);
	intrenable(IrqSPURIOUS, lapicspurious, 0, BUSUNKNOWN);
.
277c
	 * so just bag it, SMP mode shouldn't need ExtINT anyway.
.
268,269d
233c
	if(bus->type == BusEISA && !po && !el){
.
194c
	v = vno;
.
186c
mpintrinit(Bus* bus, PCMPintr* intr, int vno)
.
57d
21a
static Ref mpvnoref;			/* unique vector assignment */
.
## diffname pc/mp.c 1998/1021
## diff -e /n/emeliedump/1998/0910/sys/src/brazil/pc/mp.c /n/emeliedump/1998/1021/sys/src/brazil/pc/mp.c
12,13d
## diffname pc/mp.c 1998/1022
## diff -e /n/emeliedump/1998/1021/sys/src/brazil/pc/mp.c /n/emeliedump/1998/1022/sys/src/brazil/pc/mp.c
653c
		lo = mpintrinit(bus, aintr->intr, vno, v->irq);
.
278c
		v = mpintrinit(bus, p, VectorLAPIC+intin, p->irq);
.
231c
	if(bus->type == BusEISA && !po && !el /*&& !(elcr & (1<<irq))*/){
.
184c
mpintrinit(Bus* bus, PCMPintr* intr, int vno, int /*irq*/)
.
15a
extern int elcr;			/* mask of level-triggered interrupts */
.
## diffname pc/mp.c 1999/0301
## diff -e /n/emeliedump/1998/1022/sys/src/brazil/pc/mp.c /n/emeliedump/1999/0301/sys/src/brazil/pc/mp.c
16c
extern int i8259elcr;			/* mask of level-triggered interrupts */
.
## diffname pc/mp.c 1999/0310
## diff -e /n/emeliedump/1999/0301/sys/src/brazil/pc/mp.c /n/emeliedump/1999/0310/sys/src/brazil/pc/mp.c
370,371c
	lock(&active);
	x = MACHP(0)->ticks;
	while(MACHP(0)->ticks == x)
		;
	wrmsr(0x10, MACHP(0)->fastclock); /* synchronize fast counters */
	unlock(&active);
.
353a
	ulong x;

.
## diffname pc/mp.c 1999/0320
## diff -e /n/emeliedump/1999/0310/sys/src/brazil/pc/mp.c /n/emeliedump/1999/0320/sys/src/brazil/pc/mp.c
377c
	iunlock(&clocksynclock);
.
372c
	ilock(&clocksynclock);
.
350a
Lock clocksynclock;

.
## diffname pc/mp.c 1999/0402
## diff -e /n/emeliedump/1999/0320/sys/src/brazil/pc/mp.c /n/emeliedump/1999/0402/sys/src/brazil/pc/mp.c
770c
	*(ushort*)KADDR(0x472) = 0x1234;	/* BIOS warm-boot flag */
.
725,727c
	if(mpisabus != -1){
		vno = mpintrenablex(v, MKBUS(BusISA, 0, 0, 0));
.
719,721c
	if(mpeisabus != -1){
		vno = mpintrenablex(v, MKBUS(BusEISA, 0, 0, 0));
.
694c
	if(tbdf != BUSUNKNOWN && (vno = mpintrenablex(v, tbdf)) != -1)
.
671a
		//else
		//	print("lo not enabled 0x%uX %d\n",
		//		apic->flags, apic->type);
.
662a
		//print("lo 0x%uX: busno %d intr %d vno %d irq %d elcr 0x%uX\n",
		//	lo, bus->busno, aintr->intr->irq, vno,
		//	v->irq, i8259elcr);
.
659c
				vno, v->irq, tbdf);
.
646,654c
		 * With the APIC a unique vector can be assigned to each
		 * request to enable an interrupt. There are two reasons this
		 * is a good idea:
		 * 1) to prevent lost interrupts, no more than 2 interrupts
		 *    should be assigned per block of 16 vectors (there is an
		 *    in-service entry and a holding entry for each priority
		 *    level and there is one priority level per block of 16
		 *    interrupts).
		 * 2) each input pin on the IOAPIC will receive a different
		 *    vector regardless of whether the devices on that pin use
		 *    the same IRQ as devices on another pin.
.
640,641c
			print("mpintrenable: multiple enable irq%d, tbdf %uX\n",
				v->irq, tbdf);
.
632,635c
		 * Check not already enabled. This is a bad thing as it implies
		 * the same device is requesting the same interrupt to be
		 * enabled multiple times. The RDT read here is safe for now
		 * as currently interrupts are never disabled once enabled.
.
618c
		//print("pcidev %uX: irq %uX v->irq %uX\n", tbdf, irq, v->irq);
.
613c
		pcidev = pcimatchtbdf(tbdf);
.
590,592c
	type = BUSTYPE(tbdf);
	bno = BUSBNO(tbdf);
	dno = BUSDNO(tbdf);
.
587,588c
	 * Find the bus.
.
578c
mpintrenablex(Vctl* v, int tbdf)
.
565c
		if((apic->flags & (PcmpBP|PcmpEN)) == PcmpEN
		&& apic->type == PcmpPROCESSOR)
.
492c
		print("mpinit: unknown PCMP type 0x%uX (e-p 0x%luX)\n",
			*p, e-p);
.
483,485c
	 * Run through the table saving information needed for starting
	 * application processors and initialising any I/O APICs. The table
	 * is guaranteed to be in order such that only one pass is necessary.
.
379c
	iunlock(&mpclocksynclock);
.
374c
	ilock(&mpclocksynclock);
.
351,352d
283c
			if((apic->flags & PcmpEN)
			&& apic->type == PcmpPROCESSOR)
.
232c
	if(bus->type == BusEISA && !po && !el /*&& !(i8259elcr & (1<<irq))*/){
.
217,223c
		 * 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.
.
21a
static Lock mpclocksynclock;
.
## diffname pc/mp.c 1999/0819
## diff -e /n/emeliedump/1999/0402/sys/src/brazil/pc/mp.c /n/emeliedump/1999/0819/sys/src/brazil/pc/mp.c
555,557c
	intrenable(IrqTIMER, clockintr, 0, BUSUNKNOWN, "clock");
	intrenable(IrqERROR, lapicerror, 0, BUSUNKNOWN, "lapicerror");
	intrenable(IrqSPURIOUS, lapicspurious, 0, BUSUNKNOWN, "lapicspurious");
.
## diffname pc/mp.c 2001/0127
## diff -e /n/emeliedump/1999/0819/sys/src/brazil/pc/mp.c /n/emeliedump/2001/0127/sys/src/9/pc/mp.c
569a
			ncpu--;
		}
.
568c
		&& apic->type == PcmpPROCESSOR){
.
566a
		if(ncpu <= 1)
			break;
.
564a
	if(cp = getconf("*ncpu")){
		ncpu = strtol(cp, 0, 0);
		if(ncpu < 1)
			ncpu = 1;
	}
	else
		ncpu = MaxAPICNO;
.
464a
	int ncpu;
	char *cp;
.
## diffname pc/mp.c 2001/0527
## diff -e /n/emeliedump/2001/0127/sys/src/9/pc/mp.c /n/emeliedump/2001/0527/sys/src/9/pc/mp.c
581,582d
579c
		&& apic->type == PcmpPROCESSOR)
.
576,577d
567,573d
465,466d
## diffname pc/mp.c 2001/0830
## diff -e /n/emeliedump/2001/0527/sys/src/9/pc/mp.c /n/emeliedump/2001/0830/sys/src/9/pc/mp.c
642,644c
			vno = lo & 0xFF;
			n = mpintrinit(bus, aintr->intr, vno, v->irq);
			n |= ApicLOGICAL;
			if(n != lo || !(n & ApicLEVEL)){
				print("mpintrenable: multiple botch irq%d, tbdf %uX, lo %8.8uX, n %8.8uX\n",
					v->irq, tbdf, lo, n);
				return -1;
			}

			v->isr = lapicisr;
			v->eoi = lapiceoi;

			return vno;
.
634,637c
		 * Check if already enabled. Multifunction devices may share
		 * INT[A-D]# so, if already enabled, check the polarity matches
		 * and the trigger is level.
		 *
		 * Should check the devices differ only in the function number,
		 * but that can wait for the planned enable/disable rewrite.
		 * The RDT read here is safe for now as currently interrupts
		 * are never disabled once enabled.
.
569a
			ncpu--;
		}
.
568c
		&& apic->type == PcmpPROCESSOR){
.
566a
		if(ncpu <= 1)
			break;
.
564a
	if(cp = getconf("*ncpu")){
		ncpu = strtol(cp, 0, 0);
		if(ncpu < 1)
			ncpu = 1;
	}
	else
		ncpu = MaxAPICNO;
.
464a
	int ncpu;
	char *cp;
.
## diffname pc/mp.c 2002/0109
## diff -e /n/emeliedump/2001/0830/sys/src/9/pc/mp.c /n/emeliedump/2002/0109/sys/src/9/pc/mp.c
351,353d
## diffname pc/mp.c 2002/0404
## diff -e /n/emeliedump/2002/0109/sys/src/9/pc/mp.c /n/emeliedump/2002/0404/sys/src/9/pc/mp.c
45a
void
apicclkenable(void)
{
	Apic *apic;

	i8259init();

	/* Uniprocessor local apic init for apic clock */
	apic = &mpapic[0];
	apic->type = PcmpPROCESSOR;
	apic->apicno = 0;
	apic->flags = 0;
	apic->lintr[0] = ApicIMASK;
	apic->lintr[1] = ApicIMASK;
	apic->machno = 0;
	apic->addr = (ulong*)0xfee00000;
	machno2apicno[0] = 0;

	/*
	 * Map the local APIC.
	 */
	if(mmukmap((ulong)apic->addr, 0, 1024) == 0)
		return;

	/*
	 * These interrupts are local to the processor
	 * and do not appear in the I/O APIC so it is OK
	 * to set them now.
	 */
	intrenable(IrqTIMER, clockintr, 0, BUSUNKNOWN, "clock");
	intrenable(IrqERROR, lapicerror, 0, BUSUNKNOWN, "lapicerror");
	intrenable(IrqSPURIOUS, lapicspurious, 0, BUSUNKNOWN, "lapicspurious");

	lapicinit(apic);
	lapiconline();
}

.
## diffname pc/mp.c 2002/0405
## diff -e /n/emeliedump/2002/0404/sys/src/9/pc/mp.c /n/emeliedump/2002/0405/sys/src/9/pc/mp.c
406,415c
	if(m->havetsc){
		/*
		 * Restrain your octopus! Don't let it go out on the sea!
		 */
		ilock(&mpclocksynclock);
		x = MACHP(0)->ticks;
		while(MACHP(0)->ticks == x)
			;
		wrmsr(0x10, MACHP(0)->lasttsc); /* synchronize fast counters */
		iunlock(&mpclocksynclock);
	}
.
## diffname pc/mp.c 2002/0409
## diff -e /n/emeliedump/2002/0405/sys/src/9/pc/mp.c /n/emeliedump/2002/0409/sys/src/9/pc/mp.c
51,52d
## diffname pc/mp.c 2002/0410
## diff -e /n/emeliedump/2002/0409/sys/src/9/pc/mp.c /n/emeliedump/2002/0410/sys/src/9/pc/mp.c
590c
	intrenable(IrqTIMER, lapicclock, 0, BUSUNKNOWN, "clock");
.
416a
	timersinit();
.
404,414d
389,390d
46,80d
22d
## diffname pc/mp.c 2002/0411
## diff -e /n/emeliedump/2002/0410/sys/src/9/pc/mp.c /n/emeliedump/2002/0411/sys/src/9/pc/mp.c
575a
	if(X86FAMILY(m->cpuidax) >= 5 && conf.nmach > 1)
		coherence = wbflush;
.
574c
	if(X86FAMILY(m->cpuidax) == 3 || conf.nmach > 1)
.
571,572c
	 *  we don't really know the number of processors till
	 *  here.
	 *
	 *  set conf.copymode here if nmach > 1.
	 *  Should look for an ExtINT line and enable it.
	 *
	 *  also need to flush write buffer to make things
	 *  visible to other processors.
.
## diffname pc/mp.c 2002/0412
## diff -e /n/emeliedump/2002/0411/sys/src/9/pc/mp.c /n/emeliedump/2002/0412/sys/src/9/pc/mp.c
412a
	mach->gdt = (Segdesc*)p;	/* filled by mmuinit */
.
407a
	if(mach0->havepge)
		*pte |= PTEGLOBAL;
	p += BY2PG;
.
401a
	if(mach0->havepge)
		*pte |= PTEGLOBAL;
.
393c
	p = xspanalloc(4*BY2PG, BY2PG, 0);
.
## diffname pc/mp.c 2002/0415
## diff -e /n/emeliedump/2002/0412/sys/src/9/pc/mp.c /n/emeliedump/2002/0415/sys/src/9/pc/mp.c
462a
	syncclock();
.
367a
	syncclock();
.
## diffname pc/mp.c 2003/0301
## diff -e /n/emeliedump/2002/0415/sys/src/9/pc/mp.c /n/emeliedump/2003/0301/sys/src/9/pc/mp.c
634c
		//print("pcidev %uX: irq %d v->irq %uX\n", tbdf, irq, v->irq);
.
## diffname pc/mp.c 2003/0326
## diff -e /n/emeliedump/2003/0301/sys/src/9/pc/mp.c /n/emeliedump/2003/0326/sys/src/9/pc/mp.c
634c
		//print("pcidev %uX: irq %uX v->irq %uX\n", tbdf, irq, v->irq);
.
590,591d
584,586d

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].