/* * 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;
/*
* 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 uvlong
l64get(uchar *p)
{
return L64GET(p);
}
int
apicset(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;
}
int
mpnewproc(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 int
mpacpiproc(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 void
mpacpicpus(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 void
mpacpiscan(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 void
mpacpitbl(uchar *p)
{
/* for now, just activate any idle cpus */
if (memcmp(p, "APIC", 4) == 0)
mpacpicpus((Madt *)p);
}
static void
mpacpi(void)
{
mpdebug = getconf("*debugmp") != nil;
mpacpiscan(mpacpitbl);
}
void (*mpacpifunc)(void) = mpacpi;