262 lines · 5.3 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. */ /* * minimal acpi support for multiprocessors. * * avoids AML but that's only enough to discover * the processors, not the interrupt routing details. */#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "mp.h"#include "mpacpi.h" /* 8c says: out of fixed registers */#define L64GET(p)	((uvlong)L32GET((p)+4) << 32 | L32GET(p)) enum {	/* apic types */	Apiclproc,	Apicio,	Apicintrsrcoverride,	Apicnmisrc,	Apiclnmi,	Apicladdroverride,	Apicios,	Apicls,	Apicintrsrc,	Apiclx2,	Apiclx2nmi, 	PcmpUsed = 1ul<<31,		/* Apic->flags addition */ 	Lapicbase = 0x1b,		/* msr */ 	Lapicae	= 1<<11,		/* apic enable in Lapicbase */}; #define dprint(...)	if(mpdebug) print(__VA_ARGS__); else USED(mpdebug) /* from mp.c */int	mpdebug;int	mpmachno;Apic	mpapic[MaxAPICNO+1];int	machno2apicno[MaxAPICNO+1];	/* inverse map: machno -> APIC ID */ Apic	*bootapic; static int nprocid; static uvlongl64get(uchar *p){	return L64GET(p);} intapicset(Apic *apic, int type, int apicno, int f){	if(apicno > MaxAPICNO)		return -1;	apic->type = type;	apic->apicno = apicno;	apic->flags = f | PcmpEN | PcmpUsed;	return 0;} intmpnewproc(Apic *apic, int apicno, int f){	if(apic->flags & PcmpUsed) {		print("mpnewproc: apic already enabled\n");		return -1;	}	if (apicset(apic, PcmpPROCESSOR, apicno, f) < 0)		return -1;	apic->lintr[1] = apic->lintr[0] = ApicIMASK;	/* botch! just enumerate */	if(apic->flags & PcmpBP)		apic->machno = 0;	else		apic->machno = ++mpmachno;	machno2apicno[apic->machno] = apicno;	return 0;} static intmpacpiproc(uchar *p, ulong laddr){	int id, f;	ulong *vladdr;	vlong base;	char *already;	Apic *apic; 	/* p bytes: type (0), len (8), cpuid, cpu_lapic id, flags[4] */	id = p[3];	/* cpu unusable flag or id out of range? */	if((L32GET(p+4) & 1) == 0 || id > MaxAPICNO)		return -1; 	vladdr = nil;	already = "";	f = 0;	apic = &mpapic[id];	dprint("\tmpacpiproc: apic %#p\n", apic);	apic->paddr = laddr;	if (nprocid++ == 0) {		f = PcmpBP;		vladdr = vmap(apic->paddr, 1024);		if(apic->addr == nil){			print("proc apic %d: failed to map %#p\n", id,				apic->paddr);			already = "(fail)";		}		bootapic = apic;	}	apic->addr = vladdr; 	if(apic->flags & PcmpUsed)		already = "(on)";	else		mpnewproc(apic, id, f); 	if (0)		dprint("\tapic proc %d/%d apicid %d flags%s%s %s\n", nprocid-1,			apic->machno, id, f & PcmpBP? " boot": "",			f & PcmpEN? " enabled": "", already);	USED(already); 	rdmsr(Lapicbase, &base);	if (!(base & Lapicae)) {		dprint("mpacpiproc: enabling lapic\n");		wrmsr(Lapicbase, base | Lapicae);	}	return 0;} static voidmpacpicpus(Madt *madt){	int i, n;	ulong laddr;	uchar *p; 	laddr = L32GET(madt->addr);	dprint("APIC mpacpicpus(%#p) lapic addr %#lux, flags %#ux\n",		madt, laddr, L32GET(madt->flags)); 	n = L32GET(&madt->sdthdr[4]);	p = madt->structures;	dprint("\t%d structures at %#p\n",n, p);	/* byte 0 is assumed to be type, 1 is assumed to be length */	for(i = offsetof(Madt, structures[0]); i < n; i += p[1], p += p[1])		switch(p[0]){		case Apiclproc:			mpacpiproc(p, laddr);			break;		}} /* returns nil iff checksum is bad */static void *mpacpirsdchecksum(void* addr, int length){	uchar *p, sum; 	sum = 0;	for(p = addr; length-- > 0; p++)		sum += *p;	return sum == 0? addr: nil;} /* call func for each acpi table found */static voidmpacpiscan(void (*func)(uchar *)){	int asize, i, tbllen, sdtlen;	uintptr dhpa, sdtpa;	uchar *tbl, *sdt;	Rsd *rsd; 	dprint("ACPI...");	if((rsd = sigsearch("RSD PTR ")) == nil) {		dprint("none\n");		return;	} 	dprint("rsd %#p physaddr %#ux length %ud %#llux rev %d oem %.6s\n",		rsd, L32GET(rsd->raddr), L32GET(rsd->length),		l64get(rsd->xaddr), rsd->revision, (char*)rsd->oemid); 	if(rsd->revision == 2){		if(mpacpirsdchecksum(rsd, 36) == nil)			return;		asize = 8;		sdtpa = l64get(rsd->xaddr);	} else {		if(mpacpirsdchecksum(rsd, 20) == nil)			return;		asize = 4;		sdtpa = L32GET(rsd->raddr);	} 	if((sdt = vmap(sdtpa, 8)) == nil)		return;	if((sdt[0] != 'R' && sdt[0] != 'X') || memcmp(sdt+1, "SDT", 3) != 0){		vunmap(sdt, 8);		return;	}	sdtlen = L32GET(sdt + 4);	vunmap(sdt, 8); 	if((sdt = vmap(sdtpa, sdtlen)) == nil)		return;	if(mpacpirsdchecksum(sdt, sdtlen) != nil)		for(i = 36; i < sdtlen; i += asize){			if(asize == 8)				dhpa = l64get(sdt+i);			else				dhpa = L32GET(sdt+i);				if((tbl = vmap(dhpa, 8)) == nil)				continue;			tbllen = L32GET(tbl + 4);			vunmap(tbl, 8);				if((tbl = vmap(dhpa, tbllen)) == nil)				continue;			if(mpacpirsdchecksum(tbl, tbllen) != nil)				(*func)(tbl);			vunmap(tbl, tbllen);		}	vunmap(sdt, sdtlen);} static voidmpacpitbl(uchar *p){	/* for now, just activate any idle cpus */	if (memcmp(p, "APIC", 4) == 0)		mpacpicpus((Madt *)p);} static voidmpacpi(void){	mpdebug = getconf("*debugmp") != nil;	mpacpiscan(mpacpitbl);} void	(*mpacpifunc)(void) = mpacpi;