code for boot of a pure acpi nix system. the goal is to drop mp as soon as this is tested on enough systems. to boot pure acpi, replace mpsinit() in main with acpiinit() and switch the comment in the kernel configuration to the mp line. the ugly bit: this does not fit in with the existing devacpi structure, which should be considered depricated, and should be swept away as time permits. Reference: /n/atom/patch/applied2013/acpiirqs Date: Fri Aug 23 03:42:44 CES 2013 Signed-off-by: quanstro@quanstro.net --- /sys/src/nix/k10/cpu Fri Aug 23 03:38:57 2013 +++ /sys/src/nix/k10/cpu Fri Aug 23 03:38:57 2013 @@ -69,6 +69,7 @@ misc +dev mp mpacpi lapic ioapic msi pci sipi +# acpi lapic ioapic msi pci sipi boot cpu il @@ -169,6 +170,7 @@ port lib + libaml libip libsec libmp --- /sys/src/nix/k10/term Fri Aug 23 03:38:58 2013 +++ /sys/src/nix/k10/term Fri Aug 23 03:38:59 2013 @@ -75,6 +75,7 @@ misc +dev mp mpacpi lapic ioapic msi pci sipi +# acpi lapic ioapic msi pci sipi boot terminal il @@ -179,6 +180,7 @@ libmemlayer libmemdraw libdraw + libaml libip libsec libmp --- /sys/src/nix/k10/acpi.c Thu Jan 1 00:00:00 1970 +++ /sys/src/nix/k10/acpi.c Fri Aug 23 03:39:00 2013 @@ -0,0 +1,565 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "adr.h" + +#include "apic.h" +#include + +typedef struct Rsd Rsd; +typedef struct Tbl Tbl; + +struct Rsd { + uchar sig[8]; + uchar csum; + uchar oemid[6]; + uchar rev; + uchar raddr[4]; + uchar len[4]; + uchar xaddr[8]; + uchar xcsum; + uchar reserved[3]; +}; + +struct Tbl { + uchar sig[4]; + uchar len[4]; + uchar rev; + uchar csum; + uchar oemid[6]; + uchar oemtid[8]; + uchar oemrev[4]; + uchar cid[4]; + uchar crev[4]; + uchar data[]; +}; + +enum { + Tblsz = 4+4+1+1+6+8+4+4+4, + Rdsz = 8+1+6+1+4+4+8+1+3, +}; + +static Rsd *rsd; +static int ntblpa; /* physical addresses visited by maptable() */ +static uintmem tblpa[64]; +static int ntblmap; /* successfully mapped tables */ +static Tbl *tblmap[64]; + +static int +checksum(void *v, int n) +{ + uchar *p, s; + + s = 0; + p = v; + while(n-- > 0) + s += *p++; + return s; +} + +void* +amlalloc(usize n) +{ + void *p; + + if((p = malloc(n)) != nil){ + setmalloctag(&p, getcallerpc(&n)); + setrealloctag(&p, 0); + } + return p; +} + +void +amlfree(void *p) +{ + free(p); +} + +#define get16(p) getle((p), 2) +#define get32(p) getle((p), 4) +#define get64(p) getle((p), 8) + +extern uvlong getle(uchar*, int); + +static uint +tbldlen(Tbl *t) +{ + return get32(t->len) - Tblsz; +} + +static Tbl* +findtable(void *sig) +{ + int i; + for(i=0; isig, sig, 4) == 0) + return tblmap[i]; + return nil; +} + +#define vunmap(...) ; + +/* argument is uvlong to prevent caller from caring */ +static void +maptable(uvlong xpa) +{ + uchar *p, *e; + int i; + uintmem pa; + u32int l; + Tbl *t; + + pa = xpa; + if(pa != xpa || pa == 0) + return; + if(ntblpa >= nelem(tblpa) || ntblmap >= nelem(tblmap)) + return; + for(i=0; ilen); + if(l < Tblsz){ + vunmap(t, 8); + return; + } + vunmap(t, 8); + if((t = vmapoverlap(pa, l)) == nil) + return; + if(checksum(t, l)){ + vunmap(t, l); + return; + } + tblmap[ntblmap++] = t; + + p = (uchar*)t; + e = p + l; + if(memcmp("RSDT", t->sig, 4) == 0){ + for(p = t->data; p+3 < e; p += 4) + maptable(get32(p)); + return; + } + if(memcmp("XSDT", t->sig, 4) == 0){ + for(p = t->data; p+7 < e; p += 8) + maptable(get64(p)); + return; + } + if(memcmp("FACP", t->sig, 4) == 0){ + if(l < 44) + return; + maptable(get32(p + 40)); + if(l < 148) + return; + maptable(get64(p + 140)); + return; + } +} + +static void* +rsdscan(uchar* addr, int len, char* signature) +{ + int sl; + uchar *e, *p; + + e = addr+len; + sl = strlen(signature); + for(p = addr; p+sl < e; p += 16){ + if(memcmp(p, signature, sl)) + continue; + return p; + } + + return nil; +} + +static void* +rsdsearch(char* signature) +{ + uintptr p; + uchar *bda; + Rsd *rsd; + + /* + * Search for the data structure signature: + * 1) in the first KB of the EBDA; + * 2) in the BIOS ROM between 0xE0000 and 0xFFFFF. + */ + if(strncmp((char*)KADDR(0xFFFD9), "EISA", 4) == 0){ + bda = BIOSSEG(0x40); + if((p = (bda[0x0F]<<8)|bda[0x0E])){ + if(rsd = rsdscan(KADDR(p), 1024, signature)) + return rsd; + } + } + return rsdscan(BIOSSEG(0xE000), 0x20000, signature); +} + +static void +loadrsd(void) +{ + if((rsd = rsdsearch("RSD PTR ")) == nil) + panic("acpi: no rsd ptr"); + if(checksum(rsd, 20) && checksum(rsd, 36)) + panic("acpi: acpi checksum"); +} + +static void +maptables(void) +{ + loadrsd(); + if(ntblmap > 0 || ntblpa > 0) + return; + if(!checksum(rsd, 20)) + maptable(get32(rsd->raddr)); + if(rsd->rev >= 2) + if(!checksum(rsd, 36)) + maptable(get64(rsd->xaddr)); +} + +enum { + Iointr, + Localintr, +}; + +static u32int +apicmkintr(uint src, uint inttype, int polarity, int trigger, uint apicno, uint intin) +{ + u32int v; + Apic *apic; + + /* + * Check valid bus, interrupt input pin polarity + * and trigger mode. If the APIC ID is 0xff it means + * all APICs of this type so those checks for useable + * APIC and valid INTIN must also be done later in + * the appropriate init routine in that case. It's hard + * to imagine routing a signal to all IOAPICs, the + * usual case is routing NMI and ExtINT to all LAPICs. + */ + if(apicno != 0xff){ + if(Napic < 256 && apicno >= Napic){ + print("apic: id out-of-range: %d\n", apicno); + return 0; + } + switch(src){ + default: + print("apic: intin botch: %d\n", intin); + return 0; + case Iointr: + if((apic = ioapiclookup(apicno)) == nil){ + print("apic: ioapic unusable %d\n", apicno); + return 0; + } + if(intin >= apic->nrdt){ + print("apic: id beyond nrdt: %d\n", apicno); + return 0; + } + break; + case Localintr: + if((apic = lapiclookup(apicno)) == nil){ + print("apic: lapic unusable %d\n", apicno); + return 0; + } + if(intin >= nelem(apic->lvt)){ + print("apic: intin beyond lvt: %d\n", intin); + return 0; + } + USED(apic); + break; + } + } + + /* + * Create the low half of the vector table entry (LVT or RDT). + * For the NMI, SMI and ExtINT cases, the polarity and trigger + * are fixed (but are not always consistent over IA-32 generations). + * For the INT case, either the polarity/trigger are given or + * it defaults to that of the source bus; + * whether INT is Fixed or Lowest Priority is left until later. + */ + v = Im; + switch(inttype){ + default: + print("apic: bad irq type %d\n", inttype); + return 0; + case 0: /* INT (fake type, same as fixed) */ + v |= polarity | trigger; + break; + case MTnmi: /* NMI */ + case MTsmi: /* SMI */ + case MTei: /* ExtINT */ + v |= TMedge|IPhigh|inttype; + break; + } + + return v; +} + +int +flagstopolarity(int bustype, int flags) +{ + switch(flags & 3){ + case 1: + return IPhigh; + case 3: + return IPlow; + case 2: + return -1; + } + switch(bustype){ + case BusISA: + return IPhigh; + case BusPCI: + return IPlow; + break; + default: + return -1; + } +} + +int +flagstotrigger(int bustype, int flags) +{ + switch((flags>>3) & 3){ + case 1: + return TMedge; + case 3: + return TMlevel; + case 2: + return -1; + } + switch(bustype){ + case BusISA: + return TMedge; + case BusPCI: + return TMlevel; + break; + default: + return -1; + } +} + +static void +addirq(int gsi, int bustype, int busno, int irq, int flags) +{ + uint apicno, intin, polarity, trigger; + u32int i; + + if((apicno = gsitoapicid(gsi, &intin)) == -1) + return; + DBG("addirq: gsi %d %s busno %d irq %d flags %.8ux\n", + gsi, bustype == BusPCI? "pci": "isa", busno, irq, flags); + polarity = flagstopolarity(bustype, flags); + trigger = flagstotrigger(bustype, flags); + if(polarity == -1 || trigger == -1){ + print("addirq: bad polarity: gsi %d %s busno %d irq %d flags %.8ux\n", + gsi, bustype == BusPCI? "pci": "isa", busno, irq, flags); + return; + } + + i = apicmkintr(Iointr, 0, polarity, trigger, apicno, gsi); + ioapicintrinit(bustype, busno, apicno, gsi, irq, i); +} + +static char* +eisaid(void *v) +{ + uint b, l; + int i; + static char id[8]; + + if(amltag(v) == 's') + return v; + b = amlint(v); + for(l = 0, i=24; i>=0; i -= 8, b >>= 8) + l |= (b & 0xFF) << i; + id[7] = 0; + for(i=6; i>=3; i--, l >>= 4) + id[i] = "0123456789ABCDEF"[l & 0xF]; + for(i=2; i>=0; i--, l >>= 5) + id[i] = '@' + (l & 0x1F); + return id; +} + +static int +pcibusno(void *dot) +{ + int bno, adr, tbdf; + Pcidev *pdev; + void *p, *x; + char *id; + + id = nil; + if(x = amlwalk(dot, "^_HID")){ + p = nil; + if(amleval(x, "", &p) == 0) + id = eisaid(p); + } + if((x = amlwalk(dot, "^_BBN")) == nil) + if((x = amlwalk(dot, "^_ADR")) == nil) + return -1; + p = nil; + if(amleval(x, "", &p) < 0) + return -1; + adr = amlint(p); + /* if root bridge, then we are done here */ + if(id != nil && (strcmp(id, "PNP0A03")==0 || strcmp(id, "PNP0A08")==0)) + return adr; + x = amlwalk(dot, "^"); + if(x == nil || x == dot) + return -1; + if((bno = pcibusno(x)) < 0) + return -1; + tbdf = MKBUS(BusPCI, bno, adr>>16, adr&0xFFFF); + pdev = pcimatchtbdf(tbdf); + if(pdev == nil){ + DBG("acpi: pcibusno: bridge not found: %T \n", tbdf); + return -1; + } + if(pdev->bridge == nil){ + DBG("pcibusno: nothing bridged: %T\n", tbdf); + return -1; + } + return BUSBNO(pdev->bridge->tbdf); +} + +static int +enumprt(void *dot, void *) +{ + void *p, **a, **b; + int bno, dno, pin, gsi; + int n, i; + + bno = pcibusno(dot); + if(bno < 0){ + DBG("enumprt: pci not found %V\n", dot); + return 1; + } + + /* evalulate _PRT method */ + p = nil; + if(amleval(dot, "", &p) < 0) + return 1; + if(amltag(p) != 'p') + return 1; + + n = amllen(p); + a = amlval(p); + for(i=0; i>16; + pin = amlint(b[1]); + if(amltag(b[2]) == 'N' || amlint(b[2]) != 0){ + print("enumprt: interrupt link not handled %V\n", b[2]); + continue; + } + gsi = amlint(b[3]); + addirq(gsi, BusPCI, bno, (dno<<2)|pin, 0); + } + return 1; +} + +static void +loadtbls(char *name, int all) +{ + int i; + Tbl *t; + + for(i = 0; i < ntblmap; i++){ + t = tblmap[i]; + if(memcmp(t->sig, name, 4) == 0){ + amlload(t->data, tbldlen(t)); + if(!all) + break; + } + } +} + +enum { + Lapicen = 1, +}; + +void +acpiinit(int maxmach) +{ + uchar *p, *e; + int i, c, nmach; + uintmem lapicbase; + Tbl *t; + + print("acpiinit\n"); + maptables(); + amlinit(); + loadtbls("DSDT", 0); + loadtbls("SSDT", 1); + + /* set APIC mode */ + amleval(amlwalk(amlroot, "_PIC"), "i", 1, nil); + if((t = findtable("APIC")) == nil) + panic("acpiinit: no APIC table"); + + p = t->data; + e = p + tbldlen(t); + lapicbase = get32(p); + p += 8; + + nmach = 0; + for(; p < e; p += c){ + c = p[1]; + if(c < 2 || (p+c) > e) + break; + switch(*p){ + case 0x00: /* Processor Local APIC */ + if(p[4] & Lapicen && conf.nmach < maxmach){ + lapicinit(p[3], lapicbase, nmach==0); + conf.nmach = ++nmach; + } + break; + case 0x01: /* I/O APIC */ + ioapicinit(p[2], get32(p+8), get32(p+4)); + break; + case 0x02: /* Interrupt Source Override */ + addirq(get32(p+4), BusISA, 0, p[3], get16(p+8)); + break; + case 0x03: /* NMI Source */ + print("acpi: ignoring nmi source\n"); + break; + case 0x04: /* Local APIC NMI */ + print("acpi: lapic nmi %.2ux flags %.4ux lint# %d (ignored)\n", + p[2], (uint)get16(p+3), p[5]); + break; + case 0x05: /* Local APIC Address Override */ + case 0x06: /* I/O SAPIC */ + case 0x07: /* Local SAPIC */ + case 0x08: /* Platform Interrupt Sources */ + case 0x09: /* Processor Local x2APIC */ + case 0x0A: /* x2APIC NMI */ + case 0x0B: /* GIC */ + case 0x0C: /* GICD */ + print("acpi: ignoring entry: %.2ux\n", *p); + break; + } + } + + /* look for PCI interrupt mappings */ + amlenum(amlroot, "_PRT", enumprt, nil); + + /* add identity mapped legacy isa interrupts */ + for(i=0; i<16; i++) + addirq(i, BusISA, 0, i, 0); + + /* free the AML interpreter */ + amlexit(); + + print("acpiinit: %d maches\n", nmach); +}