/* * 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;