uchar data[];
};
+enum {
+ Tblsz = 4+4+1+1+6+8+4+4+4,
+};
+
static Rsd *rsd;
-static int ntbltab;
-static Tbl *tbltab[64];
-void*
-amlalloc(int n){
- void *p;
+/* physical addresses visited by maptable() */
+static int ntblpa;
+static uintptr tblpa[64];
- if((p = malloc(n)) == nil)
- panic("amlalloc: no memory");
- memset(p, 0, n);
- return p;
-}
-
-void
-amlfree(void *p){
- free(p);
-}
+/* successfully mapped tables */
+static int ntblmap;
+static Tbl *tblmap[64];
static ushort
get16(uchar *p){
static uint
tbldlen(Tbl *t){
- return get32(t->len) - sizeof(Tbl);
-}
-
-static int
-checksum(void *v, int n)
-{
- uchar *p, s;
-
- s = 0;
- p = v;
- while(n-- > 0)
- s += *p++;
- return s;
-}
-
-static void*
-rsdscan(uchar* addr, int len, char* sig)
-{
- int sl;
- uchar *e, *p;
-
- e = addr+len;
- sl = strlen(sig);
- for(p = addr; p+sl < e; p += 16){
- if(memcmp(p, sig, sl))
- continue;
- return p;
- }
- return nil;
-}
-
-static void*
-rsdsearch(char* sig)
-{
- 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 = KADDR(0x400);
- if((p = (bda[0x0F]<<8)|bda[0x0E]))
- if(rsd = rsdscan(KADDR(p), 1024, sig))
- return rsd;
- }
- return rsdscan(KADDR(0xE0000), 0x20000, sig);
-}
-
-static Tbl*
-findtable(void *sig){
- int i;
- for(i=0; i<ntbltab; i++)
- if(memcmp(tbltab[i]->sig, sig, 4) == 0)
- return tbltab[i];
- return nil;
+ return get32(t->len) - Tblsz;
}
static void
uintptr pa;
u32int l;
Tbl *t;
+ int i;
pa = xpa;
if((uvlong)pa != xpa || pa == 0)
return;
- if(ntbltab >= nelem(tbltab))
+ if(ntblpa >= nelem(tblpa) || ntblmap >= nelem(tblmap))
return;
+
+ for(i=0; i<ntblpa; i++){
+ if(pa == tblpa[i])
+ return;
+ }
+ tblpa[ntblpa++] = pa;
+
if((t = vmap(pa, 8)) == nil)
return;
l = get32(t->len);
- if(l < sizeof(Tbl) || findtable(t->sig)){
+ if(l < Tblsz){
vunmap(t, 8);
return;
}
vunmap(t, l);
return;
}
-
- tbltab[ntbltab++] = t;
+ tblmap[ntblmap++] = t;
p = (uchar*)t;
e = p + l;
static void
maptables(void)
{
- if(rsd == nil || ntbltab > 0)
+ if(rsd == nil || ntblmap > 0 || ntblpa > 0)
return;
if(!checksum(rsd, 20))
maptable(get32(rsd->raddr));
continue;
if((a->flags & PcmpEN) == 0)
continue;
- if(gsi >= a->gsibase && gsi < a->gsibase+a->mre){
+ if(gsi >= a->gsibase && gsi <= a->gsibase+a->mre){
if(pintin)
*pintin = gsi - a->gsibase;
return a;
bus->aintr = ai;
}
+static char*
+eisaid(void *v)
+{
+ static char id[8];
+ ulong b, l;
+ int i;
+
+ 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;
-
- p = nil;
- if(x = amlwalk(dot, "^_BBN")){
- if(amleval(x, "", &p) < 0)
+ char *id;
+
+ id = nil;
+ if((x = amlwalk(dot, "^_HID")) != nil)
+ if((p = amlval(x)) != nil)
+ id = eisaid(p);
+ if((x = amlwalk(dot, "^_BBN")) == nil)
+ if((x = amlwalk(dot, "^_ADR")) == nil)
return -1;
- return amlint(p);
- }
- if((x = amlwalk(dot, "^_ADR")) == nil)
- return -1;
- if(amleval(x, "", &p) < 0)
+ if((p = amlval(x)) == nil)
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;
return -1;
tbdf = MKBUS(BusPCI, bno, adr>>16, adr&0xFFFF);
pdev = pcimatchtbdf(tbdf);
- if(pdev == nil || pdev->bridge == nil){
- print("pcibusno: bridge tbdf %luX not found\n", (ulong)tbdf);
+ if(pdev == nil)
return -1;
- }
+ if(pdev->bridge == nil)
+ return bno;
return BUSBNO(pdev->bridge->tbdf);
}
+static int
+pciaddr(void *dot)
+{
+ int adr, bno;
+ void *x;
+
+ for(;;){
+ if((x = amlwalk(dot, "_ADR")) == nil){
+ x = amlwalk(dot, "^");
+ if(x == nil || x == dot)
+ break;
+ dot = x;
+ continue;
+ }
+ if((bno = pcibusno(x)) < 0)
+ break;
+ if((x = amlval(x)) == nil)
+ break;
+ adr = amlint(x);
+ return MKBUS(BusPCI, bno, adr>>16, adr&0xFFFF);
+ }
+ return -1;
+}
+
+static int
+getirqs(void *d, uchar pmask[32], int *pflags)
+{
+ int i, n, m;
+ uchar *p;
+
+ *pflags = 0;
+ memset(pmask, 0, 32);
+ if(amltag(d) != 'b')
+ return -1;
+ p = amlval(d);
+ if(amllen(d) >= 2 && (p[0] == 0x22 || p[0] == 0x23)){
+ pmask[0] = p[1];
+ pmask[1] = p[2];
+ if(amllen(d) >= 3 && p[0] == 0x23)
+ *pflags = ((p[3] & (1<<0)) ? PcmpEDGE : PcmpLEVEL)
+ | ((p[3] & (1<<3)) ? PcmpLOW : PcmpHIGH);
+ return 0;
+ }
+ if(amllen(d) >= 5 && p[0] == 0x89){
+ n = p[4];
+ if(amllen(d) < 5+n*4)
+ return -1;
+ for(i=0; i<n; i++){
+ m = get32(p+5 + i*4);
+ if(m >= 0 && m < 256)
+ pmask[m/8] |= 1<<(m%8);
+ }
+ *pflags = ((p[3] & (1<<1)) ? PcmpEDGE : PcmpLEVEL)
+ | ((p[3] & (1<<2)) ? PcmpLOW : PcmpHIGH);
+ return 0;
+ }
+ return -1;
+}
+
+static uchar*
+setirq(void *d, uint irq)
+{
+ uchar *p;
+
+ if(amltag(d) != 'b')
+ return nil;
+ p = amlnew('b', amllen(d));
+ memmove(p, d, amllen(p));
+ if(p[0] == 0x22 || p[0] == 0x23){
+ irq = 1<<irq;
+ p[1] = irq;
+ p[2] = irq>>8;
+ }
+ if(p[0] == 0x89){
+ p[4] = 1;
+ p[5] = irq;
+ p[6] = irq>>8;
+ p[7] = irq>>16;
+ p[8] = irq>>24;
+ }
+ return p;
+}
+
+static int
+setuplink(void *link, int *pflags)
+{
+ uchar im, pm[32], cm[32], *c;
+ static int lastirq = 1;
+ int gsi, i;
+ void *r;
+
+ if(amltag(link) != 'N')
+ return -1;
+
+ r = nil;
+ if(amleval(amlwalk(link, "_PRS"), "", &r) < 0)
+ return -1;
+ if(getirqs(r, pm, pflags) < 0)
+ return -1;
+
+ r = nil;
+ if(amleval(amlwalk(link, "_CRS"), "", &r) < 0)
+ return -1;
+ if(getirqs(r, cm, pflags) < 0)
+ return -1;
+
+ gsi = -1;
+ for(i=0; i<256; i++){
+ im = 1<<(i%8);
+ if(pm[i/8] & im){
+ if(cm[i/8] & im)
+ gsi = i;
+ }
+ }
+
+ if(gsi > 0 || getconf("*nopcirouting") != nil)
+ return gsi;
+
+ for(i=0; i<256; i++){
+ gsi = lastirq++ & 0xFF; /* round robin */
+ im = 1<<(gsi%8);
+ if(pm[gsi/8] & im){
+ if((c = setirq(r, gsi)) == nil)
+ break;
+ if(amleval(amlwalk(link, "_SRS"), "b", c, nil) < 0)
+ break;
+ return gsi;
+ }
+ }
+ return -1;
+}
+
static int
enumprt(void *dot, void *)
{
void *p, **a, **b;
- int bno, dno, pin;
+ int bno, dno, pin, gsi, flags;
int n, i;
bno = pcibusno(dot);
- if(bno < 0){
- print("enumprt: cannot get pci bus number for %V\n", dot);
+ if(bno < 0)
return 1;
- }
/* evalulate _PRT method */
p = nil;
if(amltag(p) != 'p')
return 1;
+ amltake(p);
n = amllen(p);
a = amlval(p);
for(i=0; i<n; i++){
continue;
if(amllen(a[i]) != 4)
continue;
+ flags = 0;
b = amlval(a[i]);
dno = amlint(b[0])>>16;
pin = amlint(b[1]);
- if(amltag(b[2]) == 'N' || amlint(b[2])){
- print("enumprt: interrupt link not handled %V\n", b[2]);
- continue;
+ gsi = amlint(b[3]);
+ if(gsi==0){
+ gsi = setuplink(b[2], &flags);
+ if(gsi <= 0)
+ continue;
}
- addirq(amlint(b[3]), BusPCI, bno, (dno<<2)|pin, 0);
+ addirq(gsi, BusPCI, bno, (dno<<2)|pin, flags);
}
+ amldrop(p);
+
return 1;
}
Tbl *t;
Apic *a;
void *va;
- uchar *p, *e;
+ uchar *s, *p, *e;
ulong lapicbase;
int machno, i, c;
amlinit();
- if(t = findtable("DSDT"))
- amlload(t->data, tbldlen(t));
- if(t = findtable("SSDT"))
- amlload(t->data, tbldlen(t));
+ /* load DSDT */
+ for(i=0; i<ntblmap; i++){
+ t = tblmap[i];
+ if(memcmp(t->sig, "DSDT", 4) == 0){
+ amlload(t->data, tbldlen(t));
+ break;
+ }
+ }
+
+ /* load SSDT, there can be multiple tables */
+ for(i=0; i<ntblmap; i++){
+ t = tblmap[i];
+ if(memcmp(t->sig, "SSDT", 4) == 0)
+ amlload(t->data, tbldlen(t));
+ }
/* set APIC mode */
amleval(amlwalk(amlroot, "_PIC"), "i", 1, nil);
- if((t = findtable("APIC")) == nil)
- panic("acpiinit: no APIC table found");
+ for(i=0; i<ntblmap; i++){
+ t = tblmap[i];
+ if(memcmp(t->sig, "APIC", 4) == 0)
+ goto Foundapic;
+ }
+ panic("acpiinit: no MADT (APIC) table");
+ return;
- p = t->data;
- e = p + tbldlen(t);
- lapicbase = get32(p); p += 8;
+Foundapic:
+ s = t->data;
+ e = s + tbldlen(t);
+ lapicbase = get32(s); s += 8;
va = vmap(lapicbase, 1024);
- print("LAPIC: %.8lux %.8lux\n", lapicbase, (ulong)va);
+ print("LAPIC: %.8lux %#p\n", lapicbase, va);
if(va == nil)
panic("acpiinit: cannot map lapic %.8lux", lapicbase);
machno = 0;
- for(; p < e; p += c){
+ for(p = s; p < e; p += c){
c = p[1];
if(c < 2 || (p+c) > e)
break;
a->addr = va;
a->lintr[0] = ApicIMASK;
a->lintr[1] = ApicIMASK;
- a->flags = (p[4] & PcmpEN);
- if(a->flags & PcmpEN){
- a->machno = machno++;
-
- /*
- * how do we know if this is the
- * bootstrap processors apic?
- */
- if(a->machno == 0)
- a->flags |= PcmpBP;
+ a->flags = p[4] & PcmpEN;
+
+ /* skip disabled processors */
+ if((a->flags & PcmpEN) == 0 || mpapic[a->apicno] != nil){
+ xfree(a);
+ break;
}
+ a->machno = machno++;
+
+ /*
+ * platform firmware should list the boot processor
+ * as the first processor entry in the MADT
+ */
+ if(a->machno == 0)
+ a->flags |= PcmpBP;
+
mpapic[a->apicno] = a;
break;
case 0x01: /* I/O APIC */
mpioapic[a->apicno] = a;
ioapicinit(a, a->apicno);
break;
+ }
+ }
+
+ /*
+ * need 2nd pass as vbox puts interrupt overrides
+ * *before* the ioapic entries (!)
+ */
+ for(p = s; p < e; p += c){
+ c = p[1];
+ if(c < 2 || (p+c) > e)
+ break;
+ switch(*p){
case 0x02: /* Interrupt Source Override */
addirq(get32(p+4), BusISA, 0, p[3], get16(p+8));
break;
maptables();
p = v;
- for(i=0; n > 0 && i < ntbltab; i++){
- t = tbltab[i];
+ for(i=0; n > 0 && i < ntblmap; i++){
+ t = tblmap[i];
l = get32(t->len);
if(o >= l){
o -= l;
static int
identify(void)
{
+ uintptr pa;
char *cp;
if((cp = getconf("*acpi")) == nil)
return 1;
- if((rsd = rsdsearch("RSD PTR ")) == nil)
+ pa = (uintptr)strtoull(cp, nil, 16);
+ if(pa <= 1)
+ rsd = sigsearch("RSD PTR ");
+ else
+ rsd = vmap(pa, sizeof(Rsd));
+ if(rsd == nil)
+ return 1;
+ if(checksum(rsd, 20) && checksum(rsd, 36))
return 1;
addarchfile("acpitbls", 0444, readtbls, nil);
if(strcmp(cp, "0") == 0)
return 1;
if((cp = getconf("*nomp")) != nil && strcmp(cp, "0") != 0)
return 1;
- if(cpuserver && m->havetsc)
+ if(m->havetsc && getconf("*notsc") == nil)
archacpi.fastclock = tscticks;
return 0;
}
+
+static int
+readpcicfg(Amlio *io, void *data, int n, int offset)
+{
+ ulong r, x;
+ Pcidev *p;
+ uchar *a;
+ int i;
+
+ a = data;
+ p = io->aux;
+ if(p == nil)
+ return -1;
+ offset += io->off;
+ if(offset > 256)
+ return 0;
+ if(n+offset > 256)
+ n = 256-offset;
+ r = offset;
+ if(!(r & 3) && n == 4){
+ x = pcicfgr32(p, r);
+ PBIT32(a, x);
+ return 4;
+ }
+ if(!(r & 1) && n == 2){
+ x = pcicfgr16(p, r);
+ PBIT16(a, x);
+ return 2;
+ }
+ for(i = 0; i < n; i++){
+ x = pcicfgr8(p, r);
+ PBIT8(a, x);
+ a++;
+ r++;
+ }
+ return i;
+}
+
+static int
+writepcicfg(Amlio *io, void *data, int n, int offset)
+{
+ ulong r, x;
+ Pcidev *p;
+ uchar *a;
+ int i;
+
+ a = data;
+ p = io->aux;
+ if(p == nil)
+ return -1;
+ offset += io->off;
+ if(offset > 256)
+ return 0;
+ if(n+offset > 256)
+ n = 256-offset;
+ r = offset;
+ if(!(r & 3) && n == 4){
+ x = GBIT32(a);
+ pcicfgw32(p, r, x);
+ return 4;
+ }
+ if(!(r & 1) && n == 2){
+ x = GBIT16(a);
+ pcicfgw16(p, r, x);
+ return 2;
+ }
+ for(i = 0; i < n; i++){
+ x = GBIT8(a);
+ pcicfgw8(p, r, x);
+ a++;
+ r++;
+ }
+ return i;
+}
+
+static int
+readioport(Amlio *io, void *data, int len, int port)
+{
+ uchar *a;
+
+ a = data;
+ port += io->off;
+ switch(len){
+ case 4:
+ PBIT32(a, inl(port));
+ return 4;
+ case 2:
+ PBIT16(a, ins(port));
+ return 2;
+ case 1:
+ PBIT8(a, inb(port));
+ return 1;
+ }
+ return -1;
+}
+
+static int
+writeioport(Amlio *io, void *data, int len, int port)
+{
+ uchar *a;
+
+ a = data;
+ port += io->off;
+ switch(len){
+ case 4:
+ outl(port, GBIT32(a));
+ return 4;
+ case 2:
+ outs(port, GBIT16(a));
+ return 2;
+ case 1:
+ outb(port, GBIT8(a));
+ return 1;
+ }
+ return -1;
+}
+
+int
+amlmapio(Amlio *io)
+{
+ int tbdf;
+ Pcidev *pdev;
+ char buf[64];
+
+ switch(io->space){
+ default:
+ print("amlmapio: address space %x not implemented\n", io->space);
+ break;
+ case MemSpace:
+ if((io->va = vmap(io->off, io->len)) == nil){
+ print("amlmapio: vmap failed\n");
+ break;
+ }
+ return 0;
+ case IoSpace:
+ snprint(buf, sizeof(buf), "%N", io->name);
+ if(ioalloc(io->off, io->len, 0, buf) < 0){
+ print("amlmapio: ioalloc failed\n");
+ break;
+ }
+ io->read = readioport;
+ io->write = writeioport;
+ return 0;
+ case PcicfgSpace:
+ if((tbdf = pciaddr(io->name)) < 0){
+ print("amlmapio: no address\n");
+ break;
+ }
+ if((pdev = pcimatchtbdf(tbdf)) == nil){
+ print("amlmapio: no device %T\n", tbdf);
+ break;
+ }
+ io->aux = pdev;
+ io->read = readpcicfg;
+ io->write = writepcicfg;
+ return 0;
+ }
+ print("amlmapio: mapping %N failed\n", io->name);
+ return -1;
+}
+
+void
+amlunmapio(Amlio *io)
+{
+ switch(io->space){
+ case MemSpace:
+ vunmap(io->va, io->len);
+ break;
+ case IoSpace:
+ iofree(io->off);
+ break;
+ }
+}
+
+void*
+amlalloc(int n){
+ void *p;
+
+ if((p = malloc(n)) == nil)
+ panic("amlalloc: no memory");
+ memset(p, 0, n);
+ return p;
+}
+
+void
+amlfree(void *p){
+ free(p);
+}
+
+void
+amldelay(int us)
+{
+ microdelay(us);
+}
+
+/*
+ * reset machine by writing acpi reset register.
+ */
+void
+acpireset(void)
+{
+ uchar *p;
+ Tbl *t;
+ int i;
+
+ for(i=0; i < ntblmap; i++){
+ t = tblmap[i];
+ if(memcmp(t->sig, "FACP", 4) != 0)
+ continue;
+ if(get32(t->len) <= 128)
+ break;
+ p = (uchar*)t;
+ if((get32(p + 112) & (1<<10)) == 0)
+ break;
+ if(p[116+0] != IoSpace)
+ break;
+ outb(get32(p+116+4), p[128]);
+ break;
+ }
+}