Subversion Repositories planix.SVN

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

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