ref: 8d8022281735dcf8a6c0c2c52568180e1d4ba17c
parent: d468a21a8275e1af3c23a46d50b1531415e65e33
author: 9ferno <gophone2015@gmail.com>
date: Wed Sep 15 04:23:09 EDT 2021
9front acpi
--- /dev/null
+++ b/libaml/aml.c
@@ -1,0 +1,2412 @@
+#include <u.h>
+#include <libc.h>
+#include <aml.h>
+
+typedef struct Interp Interp;
+typedef struct Frame Frame;
+typedef struct Heap Heap;
+
+typedef struct Method Method;
+typedef struct Region Region;
+typedef struct Field Field;
+
+typedef struct Name Name;
+typedef struct Ref Ref;
+typedef struct Env Env;
+typedef struct Op Op;
+
+struct Heap {
+ Heap *link;
+ int size;
+ uchar mark;
+ char tag;
+};
+
+#define H2D(h) (((Heap*)(h))+1)
+#define D2H(d) (((Heap*)(d))-1)
+#define TAG(d) D2H(d)->tag
+#define SIZE(d) D2H(d)->size
+
+static char *spacename[] = {
+ "Mem",
+ "Io",
+ "Pcicfg",
+ "Ebctl",
+ "Smbus",
+ "Cmos",
+ "Pcibar",
+ "Ipmi",
+};
+
+/* field flags */
+enum {
+ AnyAcc = 0x00,
+ ByteAcc = 0x01,
+ WordAcc = 0x02,
+ DWordAcc = 0x03,
+ QWordAcc = 0x04,
+ BufferAcc = 0x05,
+ AccMask = 0x07,
+
+ NoLock = 0x10,
+
+ Preserve = 0x00,
+ WriteAsOnes = 0x20,
+ WriteAsZeros = 0x40,
+ UpdateMask = 0x60,
+};
+
+struct Method {
+ Name *name;
+ int narg;
+ void* (*eval)(void);
+ uchar *start;
+ uchar *end;
+};
+
+struct Region {
+ Amlio;
+ char mapped;
+};
+
+struct Field {
+ void *reg; /* Buffer or Region or data Field */
+ void *bank; /* bank value */
+ Field *index; /* bank or index Field */
+ int flags;
+ int bitoff;
+ int bitlen;
+};
+
+struct Name {
+ void *v;
+
+ Name *up;
+ Name *next;
+ Name *fork;
+ Name *down;
+
+ char seg[4];
+};
+
+struct Ref {
+ void *ref;
+ void **ptr;
+};
+
+struct Env {
+ void *loc[8];
+ void *arg[8];
+};
+
+struct Op {
+ char *name;
+ char *sequence;
+ void* (*eval)(void);
+};
+
+struct Frame {
+ int tag;
+ int cond;
+ char *phase;
+ uchar *start;
+ uchar *end;
+ Op *op;
+ Env *env;
+ Name *dot;
+ void *ref;
+ void *aux;
+ int narg;
+ void *arg[8];
+};
+
+struct Interp {
+ uchar *pc;
+ Frame *fp;
+ Frame *fb;
+};
+
+static Interp interp;
+static Frame stack[32];
+
+#define PC interp.pc
+#define FP interp.fp
+#define FB interp.fb
+
+#define F0 stack
+#define FT &stack[nelem(stack)]
+
+static Heap *hp;
+
+enum {
+ Obad, Onop, Odebug,
+ Ostr, Obyte, Oword, Odword, Oqword, Oconst,
+ Onamec, Oname, Oscope, Oalias,
+ Oreg, Ofld, Oxfld, Obfld, Opkg, Ovpkg, Oenv, Obuf, Omet,
+ Odev, Ocpu, Othz, Oprc,
+ Oadd, Osub, Omod, Omul, Odiv, Oshl, Oshr, Oand, Onand, Oor,
+ Onor, Oxor, Onot, Olbit, Orbit, Oinc, Odec,
+ Oland, Olor, Olnot, Oleq, Olgt, Ollt,
+ Oindex, Omatch, Omutex, Oevent,
+ Ocfld, Ocfld0, Ocfld1, Ocfld2, Ocfld4, Ocfld8,
+ Oif, Oelse, Owhile, Obreak, Oret, Ocall,
+ Ostore, Oderef, Osize, Oref, Ocref, Ocat,
+ Oacq, Osignal, Orel, Ostall, Osleep, Oload, Ounload,
+ Otodec, Otohex, Otoint,
+};
+
+static Op optab[];
+static uchar octab1[];
+static uchar octab2[];
+
+static Name*
+rootname(Name *dot)
+{
+ while(dot != dot->up)
+ dot = dot->up;
+ return dot;
+}
+
+static void
+gcmark(void *p)
+{
+ int i;
+ Env *e;
+ Field *f;
+ Heap *h;
+ Name *n, *d;
+
+ if(p == nil)
+ return;
+ h = D2H(p);
+ if(h->mark & 1)
+ return;
+ h->mark |= 1;
+ switch(h->tag){
+ case 'E':
+ e = p;
+ for(i=0; i<nelem(e->loc); i++)
+ gcmark(e->loc[i]);
+ for(i=0; i<nelem(e->arg); i++)
+ gcmark(e->arg[i]);
+ break;
+ case 'R':
+ case 'A':
+ case 'L':
+ gcmark(((Ref*)p)->ref);
+ break;
+ case 'N':
+ n = p;
+ gcmark(n->v);
+ for(d = n->down; d; d = d->next)
+ gcmark(d);
+ gcmark(n->fork);
+ gcmark(n->up);
+ break;
+ case 'p':
+ for(i=0; i<(SIZE(p)/sizeof(void*)); i++)
+ gcmark(((void**)p)[i]);
+ break;
+ case 'r':
+ gcmark(((Region*)p)->name);
+ break;
+ case 'm':
+ gcmark(((Method*)p)->name);
+ break;
+ case 'f':
+ case 'u':
+ f = p;
+ gcmark(f->reg);
+ gcmark(f->bank);
+ gcmark(f->index);
+ break;
+ }
+}
+
+static int
+gc(void)
+{
+ int i;
+ Heap *h, **hh;
+ Frame *f;
+
+ for(h = hp; h; h = h->link)
+ h->mark &= ~1;
+
+ for(h = hp; h; h = h->link)
+ if(h->mark & 2)
+ gcmark(H2D(h));
+
+ for(f = FP; f >= F0; f--){
+ for(i=0; i<f->narg; i++)
+ gcmark(f->arg[i]);
+ gcmark(f->env);
+ gcmark(f->dot);
+ gcmark(f->ref);
+ }
+
+ gcmark(amlroot);
+
+ i = 0;
+ hh = &hp;
+ while(h = *hh){
+ if(h->mark){
+ hh = &h->link;
+ continue;
+ }
+ *hh = h->link;
+ if(h->tag == 'r'){
+ Region *r = (void*)H2D(h);
+ if(r->mapped > 0){
+ if(amldebug)
+ print("\namlunmapio(%N): %-8s %llux - %llux\n",
+ (Name*)r->name, spacename[r->space],
+ r->off, r->off + r->len);
+ amlunmapio(r);
+ }
+ r->mapped = 0;
+ r->write = nil;
+ r->read = nil;
+ r->aux = nil;
+ r->va = nil;
+ }
+ memset(h, ~0, sizeof(Heap)+h->size);
+ amlfree(h);
+ i++;
+ }
+
+ return i;
+}
+
+static void*
+mk(int tag, int size)
+{
+ Heap *h;
+ int a;
+
+ a = sizeof(Heap) + size;
+ assert(a >= sizeof(Heap));
+ h = amlalloc(a);
+ h->size = size;
+ h->tag = tag;
+ h->link = hp;
+ hp = h;
+ return h+1;
+}
+
+static uvlong*
+mki(uvlong i)
+{
+ uvlong *v;
+
+ v = mk('i', sizeof(uvlong));
+ *v = i & amlintmask;
+ return v;
+}
+
+static char*
+mks(char *s)
+{
+ char *r = mk('s', strlen(s)+1);
+ strcpy(r, s);
+ return r;
+}
+
+static int
+pkglen(uchar *p, uchar *e, uchar **np)
+{
+ ulong n;
+ uchar b;
+
+ if(p >= e)
+ return -1;
+ b = *p++;
+ if(b <= 0x3F)
+ n = b;
+ else {
+ n = b & 0xF;
+ if(p >= e)
+ return -1;
+ n += *p++ << 4;
+ if(b >= 0x80){
+ if(p >= e)
+ return -1;
+ n += *p++ << 12;
+ }
+ if(b >= 0xC0){
+ if(p >= e)
+ return -1;
+ n += *p++ << 20;
+ }
+ }
+ if(np)
+ *np = p;
+ return n;
+}
+
+static Name*
+forkname(Name *dot)
+{
+ Name *n;
+
+ n = mk('N', sizeof(Name));
+ *n = *dot;
+ n->fork = dot;
+ n->next = n->down = nil;
+ if(dot->v == dot)
+ n->v = n;
+ if(dot->up == dot)
+ n->up = n;
+ else {
+ if(n->up = forkname(dot->up))
+ n->up->down = n;
+ }
+ return n;
+}
+
+static Name*
+getseg(Name *dot, void *seg, int new)
+{
+ Name *n, *l;
+
+ for(n = l = nil; dot; dot = dot->fork){
+ for(n = dot->down; n; n = n->next){
+ if(memcmp(seg, n->seg, 4) == 0)
+ return n;
+ l = n;
+ }
+ if(new){
+ n = mk('N', sizeof(Name));
+ memmove(n->seg, seg, sizeof(n->seg));
+ n->up = dot;
+ if(l == nil)
+ dot->down = n;
+ else
+ l->next = n;
+ n->v = n;
+ break;
+ }
+ }
+ return n;
+}
+
+Name*
+getname(Name *dot, char *path, int new)
+{
+ char seg[4];
+ int i, s;
+ Name *x;
+
+ if(dot == nil)
+ return nil;
+
+ s = !new;
+ if(*path == '\\'){
+ path++;
+ dot = rootname(dot);
+ s = 0;
+ }
+ while(*path == '^'){
+ path++;
+ dot = dot->up;
+ s = 0;
+ }
+ do {
+ for(i=0; i<4; i++){
+ if(*path == 0 || *path == '.')
+ break;
+ seg[i] = *path++;
+ }
+ if(i == 0)
+ break;
+ while(i < 4)
+ seg[i++] = '_';
+ if(s && *path == 0){
+ for(;;){
+ if(x = getseg(dot, seg, 0))
+ break;
+ if(dot == dot->up)
+ break;
+ dot = dot->up;
+ }
+ return x;
+ }
+ s = 0;
+ dot = getseg(dot, seg, new);
+ } while(*path++ == '.');
+
+ return dot;
+}
+
+static int
+fixnames(void *dot, void *arg)
+{
+ void **r, *v;
+ int i;
+
+ if(arg == nil)
+ r = &((Name*)dot)->v;
+ else
+ r = arg;
+ v = *r;
+ if(v == nil || v == dot)
+ return 0;
+ if(TAG(v) == 'p'){
+ r = (void**)v;
+ for(i=0; i<(SIZE(r)/sizeof(void*)); i++)
+ fixnames(dot, r+i);
+ return 0;
+ }
+ if(TAG(v) == 'n' && (v = getname(dot, v, 0)) != nil)
+ *r = v;
+ return 0;
+}
+
+static uvlong
+getle(uchar *p, int len)
+{
+ uvlong v;
+ int i;
+
+ v = 0ULL;
+ for(i=0; i<len; i++)
+ v |= ((uvlong)p[i]) << i*8;
+ return v;
+}
+
+static void
+putle(uchar *p, int len, uvlong v)
+{
+ int i;
+
+ for(i=0; i<len; i++){
+ p[i] = v;
+ v >>= 8;
+ }
+}
+
+static uvlong
+rwreg(void *reg, int off, int len, uvlong v, int write)
+{
+ Interp save;
+ uchar buf[8], *p;
+ Region *r;
+
+ save = interp; /* save, in case we reenter the interpreter */
+ FB = FP+1; /* allocate new base */
+
+ switch(TAG(reg)){
+ case 'b':
+ p = reg;
+ if((off+len) > SIZE(p))
+ break;
+ p += off;
+ if(write)
+ putle(p, len, v);
+ else
+ v = getle(p, len);
+ goto Out;
+
+ case 'r':
+ r = reg;
+ if((off+len) > r->len)
+ break;
+ if(r->mapped == 0){
+ if(amldebug)
+ print("\namlmapio(%N): %-8s %llux - %llux\n",
+ (Name*)r->name, spacename[r->space],
+ r->off, r->off + r->len);
+ r->mapped = 1;
+ if(amlmapio(r) < 0)
+ r->mapped = -1;
+ }
+ if(r->mapped <= 0)
+ break;
+
+ if(r->va != nil)
+ p = r->va + off;
+ else {
+ if(len > sizeof(buf))
+ break;
+ p = buf;
+ }
+
+ if(write){
+ if(amldebug)
+ print("\nrwreg(%N): %-8s [%llux+%x]/%d <- %llux\n",
+ (Name*)r->name, spacename[r->space],
+ r->off, off, len, v);
+ putle(p, len, v);
+ if(r->write != nil){
+ if((*r->write)(r, p, len, off) != len)
+ break;
+ } else if(p == buf)
+ break;
+ } else {
+ if(r->read != nil){
+ if((*r->read)(r, p, len, off) != len)
+ break;
+ } else if(p == buf)
+ break;
+ v = getle(p, len);
+ if(amldebug)
+ print("\nrwreg(%N): %-8s [%llux+%x]/%d -> %llux\n",
+ (Name*)r->name, spacename[r->space],
+ r->off, off, len, v);
+ }
+ goto Out;
+ }
+
+ v = -1;
+Out:
+ interp = save; /* restore */
+ return v;
+}
+
+static uvlong
+ival(void *p)
+{
+ int n;
+
+ if(p != nil){
+ switch(TAG(p)){
+ case 'i':
+ return *((uvlong*)p);
+ case 's':
+ if(*((char*)p) == 0)
+ break;
+ return strtoull((char*)p, 0, 16);
+ case 'b':
+ n = SIZE(p);
+ if(n > 0){
+ if(n > 8) n = 8;
+ return rwreg(p, 0, n, 0, 0);
+ }
+ }
+ }
+ return 0;
+}
+
+static void *deref(void *p);
+static void *store(void *s, void *d);
+
+static int
+fieldalign(int flags)
+{
+ switch(flags & AccMask){
+ default:
+ case AnyAcc:
+ case ByteAcc:
+ case BufferAcc:
+ return 1;
+ case WordAcc:
+ return 2;
+ case DWordAcc:
+ return 4;
+ case QWordAcc:
+ return 8;
+ }
+}
+
+static void *rwfield(Field *f, void *v, int write);
+
+static uvlong
+rwfieldunit(Field *f, int off, int len, uvlong v, int write)
+{
+ if(f->index){
+ if(TAG(f->reg) == 'f'){
+ /* set index field */
+ rwfield(f->index, mki(off), 1);
+
+ /* set or get data field */
+ if(write){
+ void *b = mk('b', len);
+ putle(b, len, v);
+ rwfield(f->reg, b, 1);
+ }else{
+ v = ival(rwfield(f->reg, nil, 0));
+ }
+ return v;
+ }
+
+ /* set bank field */
+ rwfield(f->index, f->bank, 1);
+ }
+ return rwreg(f->reg, off, len, v, write);
+}
+
+static void*
+rwfield(Field *f, void *v, int write)
+{
+ int boff, blen, wo, ws, wl, wa, wd, i;
+ uvlong w, m;
+ uchar *b;
+
+ if(f == nil)
+ return nil;
+ blen = f->bitlen;
+ if(write){
+ if(v && TAG(v) == 'b'){
+ b = v;
+ if(SIZE(b)*8 < blen)
+ blen = SIZE(b)*8;
+ } else {
+ w = ival(v);
+ b = mk('b', (blen+7)/8);
+ putle(b, SIZE(b), w);
+ }
+ } else
+ b = mk('b', (blen+7)/8);
+ /*
+ * don't free b while in rwfieldunit()/rwreg(),
+ * gc can't find this temporary object referenced.
+ */
+ amltake(b);
+ wa = fieldalign(f->flags);
+ wd = wa*8;
+ boff = 0;
+ while((wl = (blen-boff)) > 0){
+ wo = (f->bitoff+boff) / wd;
+ ws = (f->bitoff+boff) % wd;
+ if(wl > (wd - ws))
+ wl = wd - ws;
+ if(write){
+ w = 0;
+ for(i = 0; i < wl; i++, boff++)
+ if(b[boff/8] & (1<<(boff%8)))
+ w |= 1ULL<<i;
+ w <<= ws;
+ if(wl != wd){
+ m = ((1ULL<<wl)-1) << ws;
+ w |= rwfieldunit(f, wo*wa, wa, 0, 0) & ~m;
+ }
+ rwfieldunit(f, wo*wa, wa, w, 1);
+ } else {
+ w = rwfieldunit(f, wo*wa, wa, 0, 0) >> ws;
+ for(i = 0; i < wl; i++, boff++){
+ b[boff/8] |= (w&1)<<(boff%8);
+ w >>= 1;
+ }
+ }
+ }
+ amldrop(b);
+ if(write)
+ return nil;
+ if(blen > 64)
+ return b;
+ w = getle(b, SIZE(b));
+ return mki(w);
+}
+
+static void*
+deref(void *p)
+{
+ if(p) switch(TAG(p)){
+ case 'N':
+ return ((Name*)p)->v;
+ case 'R': case 'A': case 'L':
+ return *((Ref*)p)->ptr;
+ case 'f': case 'u':
+ return rwfield(p, nil, 0);
+ }
+ return p;
+}
+
+static char*
+todecstr(uchar *buf, int len, int sep)
+{
+ char *r, *d;
+ int i, v;
+
+ r = d = mk('s', len*4 + 1);
+ if(len == 0){
+ *d = 0;
+ return r;
+ }
+ if(sep == 0)
+ sep = ' ';
+ for(i=0; i<len; i++){
+ v = buf[i];
+ if((*d = '0' + ((v/100) % 10)) != '0')
+ d++;
+ if((*d = '0' + ((v/10) % 10)) != '0')
+ d++;
+ *d++ = '0' + (v % 10);
+ *d++ = sep;
+ }
+ d[-1] = 0;
+ return r;
+}
+
+static char hex[] = "0123456789ABCDEF";
+
+static char*
+tohexstr(uchar *buf, int len, int sep)
+{
+ char *r, *d;
+ int i;
+
+ r = d = mk('s', len*3 + 1);
+ if(len == 0){
+ *d = 0;
+ return r;
+ }
+ if(sep == 0)
+ sep = ' ';
+ for(i=0; i<len; i++){
+ *d++ = hex[buf[i] >> 4];
+ *d++ = hex[buf[i] & 0xF];
+ *d++ = sep;
+ }
+ d[-1] = 0;
+ return r;
+}
+
+static void*
+copy(int tag, void *s)
+{
+ uvlong v;
+ void *d;
+ int n;
+
+ if(tag == 0){
+ if(s == nil)
+ return nil;
+ tag = TAG(s);
+ }
+ if(s == nil || TAG(s) == 'i'){
+ n = 4;
+ v = ival(s);
+ if(v > 0xFFFFFFFFULL)
+ n <<= 1;
+ switch(tag){
+ case 'b':
+ d = mk(tag, n);
+ rwreg(d, 0, n, v, 1);
+ return d;
+ case 's':
+ n <<= 1;
+ d = mk(tag, n+1);
+ ((char*)d)[n] = 0;
+ while(n > 0){
+ ((char*)d)[--n] = hex[v & 0xF];
+ v >>= 4;
+ }
+ return d;
+ case 'i':
+ if(v == 0ULL)
+ return nil;
+ return mki(v);
+ }
+ } else {
+ n = SIZE(s);
+ switch(tag){
+ case 's':
+ if(TAG(s) == 'b')
+ return tohexstr(s, n, ' ');
+ /* no break */
+ case 'b':
+ if(TAG(s) == 's'){
+ n = strlen(s);
+ /* zero length string is converted to zero length buffer */
+ if(n > 0) n++;
+ }
+ d = mk(tag, n);
+ memmove(d, s, n);
+ return d;
+ }
+ }
+ return s;
+}
+
+static void*
+store(void *s, void *d)
+{
+ void *p, **pp;
+
+ if(d == nil)
+ return nil;
+ switch(TAG(d)){
+ default:
+ return nil;
+ case 'A':
+ s = deref(s);
+ /* no break */
+ case 'R': case 'L':
+ pp = ((Ref*)d)->ptr;
+ while((p = *pp) != nil){
+ switch(TAG(p)){
+ case 'R': case 'A': case 'L':
+ pp = ((Ref*)p)->ptr;
+ break;
+ case 'N':
+ pp = &((Name*)p)->v;
+ break;
+ }
+ if(*pp == p)
+ break;
+ }
+ break;
+ case 'N':
+ pp = &((Name*)d)->v;
+ break;
+ }
+ p = *pp;
+ if(p != nil && TAG(p) != 'N'){
+ switch(TAG(p)){
+ case 'f':
+ case 'u':
+ rwfield(p, s, 1);
+ return d;
+ }
+ if(TAG(d) != 'A' && TAG(d) != 'L'){
+ *pp = copy(TAG(p), s);
+ return d;
+ }
+ }
+ *pp = copy(0, s);
+ return d;
+}
+
+static int
+Nfmt(Fmt *f)
+{
+ char buf[5];
+ int i;
+ Name *n;
+
+ n = va_arg(f->args, Name*);
+ if(n == nil)
+ return fmtprint(f, "?NIL");
+ if(n == n->up)
+ return fmtprint(f, "\\");
+ strncpy(buf, n->seg, 4);
+ buf[4] = 0;
+ for(i=3; i>0; i--){
+ if(buf[i] != '_')
+ break;
+ buf[i] = 0;
+ }
+ if(n->up == n->up->up)
+ return fmtprint(f, "\\%s", buf);
+ return fmtprint(f, "%N.%s", n->up, buf);
+}
+
+static int
+Vfmt(Fmt *f)
+{
+ void *p;
+ int i, n, c;
+ Env *e;
+ Field *l;
+ Name *nm;
+ Method *m;
+ Region *g;
+ Ref *r;
+
+ p = va_arg(f->args, void*);
+ if(p == nil)
+ return fmtprint(f, "nil");
+ c = TAG(p);
+ switch(c){
+ case 'N':
+ nm = p;
+ if(nm->v != nm)
+ return fmtprint(f, "%N=%V", nm, nm->v);
+ return fmtprint(f, "%N=*", nm);
+ case 'A':
+ case 'L':
+ r = p;
+ e = r->ref;
+ if(c == 'A')
+ return fmtprint(f, "Arg%zd=%V", r->ptr - e->arg, *r->ptr);
+ if(c == 'L')
+ return fmtprint(f, "Local%zd=%V", r->ptr - e->loc, *r->ptr);
+ case 'n':
+ return fmtprint(f, "%s", (char*)p);
+ case 's':
+ return fmtprint(f, "\"%s\"", (char*)p);
+ case 'i':
+ return fmtprint(f, "%#llux", *((uvlong*)p));
+ case 'p':
+ n = SIZE(p)/sizeof(void*);
+ fmtprint(f, "Package(%d){", n);
+ for(i=0; i<n; i++){
+ if(i > 0)
+ fmtprint(f, ", ");
+ fmtprint(f, "%V", ((void**)p)[i]);
+ }
+ fmtprint(f, "}");
+ return 0;
+ case 'b':
+ n = SIZE(p);
+ fmtprint(f, "Buffer(%d){", n);
+ for(i=0; i<n; i++){
+ if(i > 0)
+ fmtprint(f, ", ");
+ fmtprint(f, "%.2uX", ((uchar*)p)[i]);
+ }
+ fmtprint(f, "}");
+ return 0;
+ case 'r':
+ g = p;
+ return fmtprint(f, "Region(%s, %#llux, %#llux)",
+ spacename[g->space & 7], g->off, g->len);
+ case 'm':
+ m = p;
+ fmtprint(f, "%N(", m->name);
+ for(i=0; i < m->narg; i++){
+ if(i > 0)
+ fmtprint(f, ", ");
+ fmtprint(f, "Arg%d", i);
+ }
+ fmtprint(f, ")");
+ return 0;
+ case 'u':
+ fmtprint(f, "Buffer");
+ /* no break */
+ case 'f':
+ l = p;
+ if(l->index){
+ if(TAG(l->reg) == 'f')
+ return fmtprint(f, "IndexField(%x, %x, %x) @ %V[%V]",
+ l->flags, l->bitoff, l->bitlen, l->reg, l->index);
+ else
+ return fmtprint(f, "BankField(%x, %x, %x, %V=%V) @ %V",
+ l->flags, l->bitoff, l->bitlen, l->index, l->bank, l->reg);
+ }
+ return fmtprint(f, "Field(%x, %x, %x) @ %V",
+ l->flags, l->bitoff, l->bitlen, l->reg);
+ default:
+ return fmtprint(f, "%c:%p", c, p);
+ }
+}
+
+static void
+dumpregs(void)
+{
+ Frame *f;
+ Env *e;
+ int i;
+
+ print("\n*** dumpregs: PC=%p FP=%p\n", PC, FP);
+ e = nil;
+ for(f = FP; f >= FB; f--){
+ print("%.8p.%.2zx: %-8s %N\t", f->start, f-FB, f->phase, f->dot);
+ if(f->op)
+ print("%s", f->op->name);
+ print("(");
+ for(i=0; i<f->narg; i++){
+ if(i > 0)
+ print(", ");
+ print("%V", f->arg[i]);
+ }
+ print(")\n");
+ if(e == f->env)
+ continue;
+ if(e = f->env){
+ for(i=0; i<nelem(e->arg); i++)
+ print("Arg%d=%V ", i, e->arg[i]);
+ print("\n");
+ for(i=0; i<nelem(e->loc); i++)
+ print("Local%d=%V ", i, e->loc[i]);
+ print("\n");
+ }
+ }
+}
+
+static int
+xec(uchar *pc, uchar *end, Name *dot, Env *env, void **pret)
+{
+ static int loop;
+ int i, c;
+ void *r;
+
+ PC = pc;
+ if(FB < F0 || FB >= FT)
+ goto Out;
+ FP = FB;
+
+ FP->tag = 0;
+ FP->cond = 0;
+ FP->narg = 0;
+ FP->phase = "}";
+ FP->start = PC;
+ FP->end = end;
+ FP->aux = end;
+ FP->ref = nil;
+ FP->dot = dot;
+ FP->env = env;
+ FP->op = nil;
+
+ for(;;){
+ if((++loop & 127) == 0)
+ gc();
+ if(amldebug)
+ print("\n%.8p.%.2zx %-8s\t%N\t", PC, FP - FB, FP->phase, FP->dot);
+ r = nil;
+ c = *FP->phase++;
+ switch(c){
+ default:
+ if(PC >= FP->end){
+ Overrun:
+ print("aml: PC overrun frame end");
+ goto Out;
+ }
+ FP++;
+ if(FP >= FT){
+ print("aml: frame stack overflow");
+ goto Out;
+ }
+ *FP = FP[-1];
+ FP->aux = nil;
+ FP->ref = nil;
+ FP->tag = c;
+ FP->start = PC;
+ c = *PC++;
+ if(amldebug) print("%.2X", c);
+ if(c == '['){
+ if(PC >= FP->end)
+ goto Overrun;
+ c = *PC++;
+ if(amldebug) print("%.2X", c);
+ c = octab2[c];
+ }else
+ c = octab1[c];
+ FP->op = &optab[c];
+ FP->narg = 0;
+ FP->phase = FP->op->sequence;
+ if(amldebug) print("\t%s %s", FP->op->name, FP->phase);
+ continue;
+ case '{':
+ end = PC;
+ c = pkglen(PC, FP->end, &PC);
+ end += c;
+ if(c < 0 || end > FP->end)
+ goto Overrun;
+ FP->end = end;
+ continue;
+ case ',':
+ FP->start = PC;
+ continue;
+ case 's':
+ if(end = memchr(PC, 0, FP->end - PC))
+ end++;
+ else
+ end = FP->end;
+ c = end - PC;
+ r = mk('s', c+1);
+ memmove(r, PC, c);
+ ((uchar*)r)[c] = 0;
+ PC = end;
+ break;
+ case '1':
+ case '2':
+ case '4':
+ case '8':
+ end = PC+(c-'0');
+ if(end > FP->end)
+ goto Overrun;
+ else {
+ r = mki(*PC++);
+ for(i = 8; PC < end; i += 8)
+ *((uvlong*)r) |= ((uvlong)*PC++) << i;
+ }
+ break;
+ case '}':
+ case 0:
+ if(FP->op){
+ if(amldebug){
+ print("*%p,%V\t%s(", FP->aux, FP->ref, FP->op->name);
+ for(i = 0; i < FP->narg; i++){
+ if(i > 0)
+ print(", ");
+ print("%V", FP->arg[i]);
+ }
+ print(")");
+ }
+ for(i = FP->narg; i < nelem(FP->arg); i++)
+ FP->arg[i] = nil;
+ r = FP->op->eval();
+ if(amldebug)
+ print(" -> %V", r);
+ }
+
+ c = FP->phase[-1];
+ if(c == '}' && PC < FP->end){
+ FP->narg = 0;
+ FP->phase = "*}";
+ continue;
+ }
+
+ if(r) switch(FP->tag){
+ case '@':
+ break;
+ case 'n':
+ case 'N':
+ if(TAG(r) != 'N')
+ r = nil;
+ break;
+ default:
+ if((r = deref(r)) == nil)
+ break;
+ switch(TAG(r)){
+ case 'f': case 'u':
+ r = rwfield(r, nil, 0);
+ break;
+ case 'm': {
+ Method *m = r;
+ FP->ref = m;
+ FP->narg = 0;
+ FP->phase = "********}" + (8 - m->narg);
+ FP->op = &optab[Ocall];
+ continue;
+ }
+ }
+ }
+ FP--;
+ break;
+ }
+ if(FP < FB){
+ if(pret){
+ if(amldebug) print(" -> %V\n", r);
+ *pret = r;
+ }
+ return 0;
+ }
+ FP->arg[FP->narg++] = r;
+ }
+Out:
+ if(amldebug)
+ dumpregs();
+ return -1;
+}
+
+static void*
+evalnamec(void)
+{
+ static char name[1024];
+ char *d;
+ void *r;
+ int c;
+
+ c = 1;
+ d = name;
+ PC = FP->start;
+ if(*PC == '\\')
+ *d++ = *PC++;
+ while(*PC == '^'){
+ if(d >= &name[sizeof(name)-1]){
+ Toolong:
+ *d = 0;
+ print("aml: name too long: %s\n", name);
+ PC = FP->end;
+ return nil;
+ }
+ *d++ = *PC++;
+ }
+ if(*PC == '.'){
+ PC++;
+ c = 2;
+ } else if(*PC == '/'){
+ PC++;
+ c = *PC++;
+ } else if(*PC == 0){
+ PC++;
+ c = 0;
+ }
+ while(c > 0){
+ if(d >= &name[sizeof(name)-5])
+ goto Toolong;
+ *d++ = *PC++;
+ *d++ = *PC++;
+ *d++ = *PC++;
+ *d++ = *PC++;
+ while((d > &name[0]) && (d[-1] == '_' || d[-1] == 0))
+ d--;
+ if(--c > 0)
+ *d++ = '.';
+ }
+ *d = 0;
+ if((r = getname(FP->dot, name, FP->tag == 'N')) == nil){
+ r = mks(name);
+ D2H(r)->tag = 'n'; /* unresolved name */
+ }
+ return r;
+}
+
+static void*
+evaliarg0(void)
+{
+ return FP->arg[0];
+}
+
+static void*
+evalconst(void)
+{
+ switch(FP->start[0]){
+ case 0x01:
+ return mki(1);
+ case 0xFF:
+ return mki(~0ULL);
+ }
+ return nil;
+}
+
+static void*
+evalbuf(void)
+{
+ int n, m;
+ uchar *p;
+
+ n = ival(FP->arg[0]);
+ p = mk('b', n);
+ m = FP->end - PC;
+ if(m > n)
+ m = n;
+ memmove(p, PC, m);
+ PC = FP->end;
+ return p;
+}
+
+static void*
+evalpkg(void)
+{
+ void **p, **x;
+ int n;
+
+ if((p = FP->ref) != nil){
+ x = FP->aux;
+ if(x >= p && x < (p+(SIZE(p)/sizeof(void*)))){
+ *x++ = FP->arg[0];
+ FP->aux = x;
+ }
+ }else {
+ n = ival(FP->arg[0]);
+ if(n < 0) n = 0;
+ p = mk('p', n*sizeof(void*));
+ FP->aux = p;
+ FP->ref = p;
+ }
+ return p;
+}
+
+static void*
+evalname(void)
+{
+ Name *n;
+
+ if(n = FP->arg[0])
+ n->v = FP->arg[1];
+ else
+ PC = FP->end;
+ return nil;
+}
+
+static void*
+evalscope(void)
+{
+ Name *n;
+
+ if(n = FP->arg[0])
+ FP->dot = n;
+ else
+ PC = FP->end;
+ FP->op = nil;
+ return nil;
+}
+
+static void*
+evalalias(void)
+{
+ Name *n;
+
+ if(n = FP->arg[1])
+ n->v = deref(FP->arg[0]);
+ return nil;
+}
+
+static void*
+evalmet(void)
+{
+ Name *n;
+ Method *m;
+
+ if((n = FP->arg[0]) != nil){
+ m = mk('m', sizeof(Method));
+ m->narg = ival(FP->arg[1]) & 7;
+ m->start = PC;
+ m->end = FP->end;
+ m->name = n;
+ n->v = m;
+ }
+ PC = FP->end;
+ return nil;
+}
+
+static void*
+evalreg(void)
+{
+ Name *n;
+ Region *r;
+
+ if((n = FP->arg[0]) != nil){
+ r = mk('r', sizeof(Region));
+ r->space = ival(FP->arg[1]);
+ r->off = ival(FP->arg[2]);
+ r->len = ival(FP->arg[3]);
+ r->name = n;
+ r->va = nil;
+ n->v = r;
+ }
+ return nil;
+}
+
+static void*
+evalcfield(void)
+{
+ void *r;
+ Field *f;
+ Name *n;
+ int c;
+
+ r = FP->arg[0];
+ if(r == nil || (TAG(r) != 'b' && TAG(r) != 'r'))
+ return nil;
+ c = FP->op - optab;
+ if(c == Ocfld)
+ n = FP->arg[3];
+ else
+ n = FP->arg[2];
+ if(n == nil || TAG(n) != 'N')
+ return nil;
+ if(TAG(r) == 'b')
+ f = mk('u', sizeof(Field));
+ else
+ f = mk('f', sizeof(Field));
+ switch(c){
+ case Ocfld:
+ f->bitoff = ival(FP->arg[1]);
+ f->bitlen = ival(FP->arg[2]);
+ break;
+ case Ocfld0:
+ f->bitoff = ival(FP->arg[1]);
+ f->bitlen = 1;
+ break;
+ case Ocfld1:
+ case Ocfld2:
+ case Ocfld4:
+ case Ocfld8:
+ f->bitoff = 8*ival(FP->arg[1]);
+ f->bitlen = 8*(c - Ocfld0);
+ break;
+ }
+ f->reg = r;
+ n->v = f;
+ return nil;
+}
+
+static void*
+evalfield(void)
+{
+ int flags, bitoff, n;
+ Field *f, *index;
+ void *reg, *bank;
+ Name *d;
+ uchar *p;
+
+ bank = nil;
+ index = nil;
+ bitoff = 0;
+ switch(FP->op - optab){
+ default:
+ goto Out;
+ case Ofld:
+ if((reg = deref(FP->arg[0])) == nil || TAG(reg) != 'r')
+ goto Out;
+ flags = ival(FP->arg[1]);
+ break;
+ case Oxfld:
+ if((index = deref(FP->arg[0])) == nil || TAG(index) != 'f')
+ goto Out;
+ if((reg = deref(FP->arg[1])) == nil || TAG(reg) != 'f') /* data field */
+ goto Out;
+ flags = ival(FP->arg[2]);
+ break;
+ case Obfld:
+ if((reg = deref(FP->arg[0])) == nil || TAG(reg) != 'r')
+ goto Out;
+ if((index = deref(FP->arg[1])) == nil || TAG(index) != 'f')
+ goto Out;
+ bank = FP->arg[2];
+ flags = ival(FP->arg[3]);
+ break;
+ }
+ p = PC;
+ if(p >= FP->end)
+ return nil;
+ while(p < FP->end){
+ if(*p == 0x00){
+ p++;
+ if((n = pkglen(p, FP->end, &p)) < 0)
+ break;
+ bitoff += n;
+ continue;
+ }
+ if(*p == 0x01){
+ p++;
+ flags = *p;
+ p += 2;
+ continue;
+ }
+ if(p+4 >= FP->end)
+ break;
+ if((d = getseg(FP->dot, p, 1)) == nil)
+ break;
+ if((n = pkglen(p+4, FP->end, &p)) < 0)
+ break;
+ f = mk('f', sizeof(Field));
+ f->flags = flags;
+ f->bitlen = n;
+ f->bitoff = bitoff;
+ f->reg = reg;
+ f->bank = bank;
+ f->index = index;
+ bitoff += n;
+ d->v = f;
+ }
+Out:
+ PC = FP->end;
+ return nil;
+}
+
+static void*
+evalnop(void)
+{
+ return nil;
+}
+
+static void*
+evalbad(void)
+{
+ int i;
+
+ print("aml: bad opcode %p: ", PC);
+ for(i=0; i < 8 && (FP->start+i) < FP->end; i++){
+ if(i > 0)
+ print(" ");
+ print("%.2X", FP->start[i]);
+ }
+ if((FP->start+i) < FP->end)
+ print("...");
+ print("\n");
+ PC = FP->end;
+ return nil;
+}
+
+static void*
+evalcond(void)
+{
+ switch(FP->op - optab){
+ case Oif:
+ if(FP <= FB)
+ break;
+ FP[-1].cond = ival(FP->arg[0]) != 0;
+ if(!FP[-1].cond)
+ PC = FP->end;
+ break;
+ case Oelse:
+ if(FP <= FB)
+ break;
+ if(FP[-1].cond)
+ PC = FP->end;
+ break;
+ case Owhile:
+ if(FP->aux){
+ if(PC >= FP->end){
+ PC = FP->start;
+ FP->aux = nil;
+ }
+ return nil;
+ }
+ FP->aux = FP->end;
+ if(ival(FP->arg[0]) == 0){
+ PC = FP->end;
+ break;
+ }
+ return nil;
+ }
+ FP->op = nil;
+ return nil;
+}
+
+static vlong
+cmp1(void *a, void *b)
+{
+ vlong c;
+ int tag;
+
+ if(a == nil || TAG(a) == 'i'){
+ c = ival(a) - ival(b);
+ } else {
+ tag = TAG(a);
+ if(b == nil || TAG(b) != tag)
+ b = copy(tag, b);
+ if(b == nil || TAG(b) != tag)
+ return -1; /* botch */
+ switch(tag){
+ default:
+ return -1; /* botch */
+ case 's':
+ c = strcmp((char*)a, (char*)b);
+ break;
+ case 'b':
+ c = SIZE(a) - SIZE(b);
+ if(c == 0)
+ c = memcmp(a, b, SIZE(a));
+ break;
+ }
+ }
+ return c;
+}
+
+static void*
+evalcmp(void)
+{
+ vlong c = cmp1(FP->arg[0], FP->arg[1]);
+ switch(FP->op - optab){
+ case Oleq:
+ if(c == 0) return mki(1);
+ break;
+ case Olgt:
+ if(c > 0) return mki(1);
+ break;
+ case Ollt:
+ if(c < 0) return mki(1);
+ break;
+ }
+ return nil;
+}
+
+static void*
+evalcall(void)
+{
+ Method *m;
+ Env *e;
+ int i;
+
+ if(FP->aux){
+ if(PC >= FP->end){
+ PC = FP->aux;
+ FP->end = PC;
+ }
+ return nil;
+ }
+ m = FP->ref;
+ e = mk('E', sizeof(Env));
+ for(i=0; i<FP->narg; i++)
+ e->arg[i] = deref(FP->arg[i]);
+ FP->env = e;
+ FP->narg = 0;
+ FP->dot = m->name;
+ if(m->eval != nil){
+ FP->op = nil;
+ FP->end = PC;
+ return (*m->eval)();
+ }
+ FP->dot = forkname(FP->dot);
+ FP->aux = PC;
+ FP->start = m->start;
+ FP->end = m->end;
+ PC = FP->start;
+ return nil;
+}
+
+static void*
+evalret(void)
+{
+ void *r = FP->arg[0];
+ int brk = (FP->op - optab) != Oret;
+ while(--FP >= FB){
+ switch(FP->op - optab){
+ case Owhile:
+ if(!brk)
+ continue;
+ PC = FP->end;
+ return nil;
+ case Ocall:
+ PC = FP->aux;
+ return r;
+ }
+ }
+ FP = FB;
+ PC = FB->end;
+ return r;
+}
+
+static void*
+evalenv(void)
+{
+ Ref *r;
+ Env *e;
+ int c;
+
+ if((e = FP->env) == nil)
+ return nil;
+ c = FP->start[0];
+ if(c >= 0x60 && c <= 0x67){
+ r = mk('L', sizeof(Ref));
+ r->ptr = &e->loc[c - 0x60];
+ } else if(c >= 0x68 && c <= 0x6E){
+ r = mk('A', sizeof(Ref));
+ r->ptr = &e->arg[c - 0x68];
+ } else
+ return nil;
+ r->ref = e;
+ return r;
+}
+
+static void*
+evalstore(void)
+{
+ return store(FP->arg[0], FP->arg[1]);
+}
+
+static void*
+evalcat(void)
+{
+ void *r, *a, *b;
+ int tag, n, m;
+
+ a = FP->arg[0];
+ b = FP->arg[1];
+ if(a == nil || TAG(a) == 'i')
+ a = copy('b', a); /* Concat(Int, ???) -> Buf */
+ tag = TAG(a);
+ if(b == nil || TAG(b) != tag)
+ b = copy(tag, b);
+ if(TAG(b) != tag)
+ return nil; /* botch */
+ switch(tag){
+ default:
+ return nil; /* botch */
+ case 'b':
+ n = SIZE(a);
+ m = SIZE(b);
+ r = mk('b', n + m);
+ memmove(r, a, n);
+ memmove((uchar*)r + n, b, m);
+ break;
+ case 's':
+ n = strlen((char*)a);
+ m = strlen((char*)b);
+ r = mk('s', n + m + 1);
+ memmove(r, a, n);
+ memmove((char*)r + n, b, m);
+ ((char*)r)[n+m] = 0;
+ break;
+ }
+ store(r, FP->arg[2]);
+ return r;
+}
+
+static void*
+evalindex(void)
+{
+ Field *f;
+ void *p;
+ Ref *r;
+ uvlong x;
+
+ x = ival(FP->arg[1]);
+ if(p = deref(FP->arg[0])) switch(TAG(p)){
+ case 's':
+ if(x >= strlen((char*)p))
+ break;
+ /* no break */
+ case 'b':
+ if(x >= SIZE(p))
+ break;
+ f = mk('u', sizeof(Field));
+ f->reg = p;
+ f->bitlen = 8;
+ f->bitoff = 8*x;
+ store(f, FP->arg[2]);
+ return f;
+ case 'p':
+ if(x >= (SIZE(p)/sizeof(void*)))
+ break;
+ if(TAG(FP->arg[0]) == 'A' || TAG(FP->arg[0]) == 'L')
+ r = mk(TAG(FP->arg[0]), sizeof(Ref));
+ else
+ r = mk('R', sizeof(Ref));
+ r->ref = p;
+ r->ptr = ((void**)p) + x;
+ store(r, FP->arg[2]);
+ return r;
+ }
+ return nil;
+}
+
+static int
+match1(int op, void *a, void *b)
+{
+ vlong c = cmp1(a, b);
+ switch(op){
+ case 0: return 1;
+ case 1: return c == 0;
+ case 2: return c <= 0;
+ case 3: return c < 0;
+ case 4: return c >= 0;
+ case 5: return c > 0;
+ }
+ return 0;
+}
+
+static void*
+evalmatch(void)
+{
+ void **p = FP->arg[0];
+ if(p != nil && TAG(p) == 'p'){
+ uvlong i, n = SIZE(p)/sizeof(void*);
+ int o1 = ival(FP->arg[1]), o2 = ival(FP->arg[3]);
+ for(i=ival(FP->arg[5]); i<n; i++){
+ void *a = deref(p[i]);
+ if(match1(o1, a, FP->arg[2]) && match1(o2, a, FP->arg[4]))
+ return mki(i);
+ }
+ }
+ return mki(~0ULL);
+}
+
+static void*
+evalcondref(void)
+{
+ void *s;
+ if((s = FP->arg[0]) == nil)
+ return nil;
+ store(s, FP->arg[1]);
+ return mki(1);
+}
+
+static void*
+evalsize(void)
+{
+ return mki(amllen(FP->arg[0]));
+}
+
+static void*
+evalderef(void)
+{
+ void *p;
+
+ if(p = FP->arg[0]){
+ if(TAG(p) == 's' || TAG(p) == 'n')
+ p = getname(FP->dot, (char*)p, 0);
+ p = deref(p);
+ }
+ return p;
+}
+
+static void*
+evalarith(void)
+{
+ uvlong v, d;
+ void *r;
+ int i;
+
+ r = nil;
+ switch(FP->op - optab){
+ case Oadd:
+ r = mki(ival(FP->arg[0]) + ival(FP->arg[1]));
+ break;
+ case Osub:
+ r = mki(ival(FP->arg[0]) - ival(FP->arg[1]));
+ break;
+ case Omul:
+ r = mki(ival(FP->arg[0]) * ival(FP->arg[1]));
+ break;
+ case Omod:
+ case Odiv:
+ v = ival(FP->arg[0]);
+ d = ival(FP->arg[1]);
+ if(d == 0){
+ print("aml: division by zero: PC=%#p\n", PC);
+ return nil;
+ }
+ r = mki(v % d);
+ store(r, FP->arg[2]);
+ if((FP->op - optab) != Odiv)
+ return r;
+ r = mki(v / d);
+ store(r, FP->arg[3]);
+ return r;
+ case Oshl:
+ r = mki(ival(FP->arg[0]) << ival(FP->arg[1]));
+ break;
+ case Oshr:
+ r = mki(ival(FP->arg[0]) >> ival(FP->arg[1]));
+ break;
+ case Oand:
+ r = mki(ival(FP->arg[0]) & ival(FP->arg[1]));
+ break;
+ case Onand:
+ r = mki(~(ival(FP->arg[0]) & ival(FP->arg[1])));
+ break;
+ case Oor:
+ r = mki(ival(FP->arg[0]) | ival(FP->arg[1]));
+ break;
+ case Onor:
+ r = mki(~(ival(FP->arg[0]) | ival(FP->arg[1])));
+ break;
+ case Oxor:
+ r = mki(ival(FP->arg[0]) ^ ival(FP->arg[1]));
+ break;
+ case Onot:
+ r = mki(~ival(FP->arg[0]));
+ store(r, FP->arg[1]);
+ return r;
+ case Olbit:
+ v = ival(FP->arg[0]);
+ if(v == 0)
+ break;
+ for(i=0; (v & 1) == 0; i++)
+ v >>= 1;
+ r = mki(i+1);
+ break;
+ case Orbit:
+ v = ival(FP->arg[0]);
+ if(v == 0)
+ break;
+ for(i=0; v != 0; i++)
+ v >>= 1;
+ r = mki(i);
+ break;
+ case Oland:
+ return mki(ival(FP->arg[0]) && ival(FP->arg[1]));
+ case Olor:
+ return mki(ival(FP->arg[0]) || ival(FP->arg[1]));
+ case Olnot:
+ return mki(ival(FP->arg[0]) == 0);
+
+ case Oinc:
+ r = mki(ival(deref(FP->arg[0]))+1);
+ store(r, FP->arg[0]);
+ return r;
+ case Odec:
+ r = mki(ival(deref(FP->arg[0]))-1);
+ store(r, FP->arg[0]);
+ return r;
+ }
+
+ store(r, FP->arg[2]);
+ return r;
+}
+
+static void*
+evalload(void)
+{
+ enum { LenOffset = 4, HdrLen = 36 };
+ uvlong *tid;
+ Region *r;
+ int l;
+
+ tid = nil;
+ if(FP->aux){
+ if(PC >= FP->end){
+ amlenum(amlroot, nil, fixnames, nil);
+ FP->aux = nil;
+ FP->end = PC;
+ tid = mki(1); /* fake */
+ }
+ } else {
+ store(nil, FP->arg[1]);
+ if(FP->arg[0] == nil)
+ return nil;
+
+ l = rwreg(FP->arg[0], LenOffset, 4, 0, 0);
+ if(l <= HdrLen)
+ return nil;
+
+ FP->aux = PC; /* save */
+ FP->ref = FP->arg[0];
+ switch(TAG(FP->ref)){
+ case 'b':
+ if(SIZE(FP->ref) < l)
+ return nil;
+ PC = (uchar*)FP->ref + HdrLen;
+ break;
+ case 'r':
+ r = FP->ref;
+ if(r->len < l || r->va == nil || r->mapped <= 0)
+ return nil;
+ PC = (uchar*)r->va + HdrLen;
+ break;
+ default:
+ return nil;
+ }
+ FP->end = PC + (l - HdrLen);
+ FP->dot = amlroot;
+ FP->env = nil;
+
+ tid = mki(1); /* fake */
+ store(tid, FP->arg[1]);
+ }
+ return tid;
+}
+
+static void*
+evalstall(void)
+{
+ amldelay(ival(FP->arg[0]));
+ return nil;
+}
+
+static void*
+evalsleep(void)
+{
+ amldelay(ival(FP->arg[0])*1000);
+ return nil;
+}
+
+static void*
+evalconv(void)
+{
+ void *r, *a;
+
+ r = nil;
+ a = FP->arg[0];
+ switch(FP->op - optab){
+ case Otodec:
+ if(a == nil)
+ break;
+ if(TAG(a) == 's'){
+ r = a;
+ break;
+ }
+ if(TAG(a) == 'b'){
+ r = todecstr(a, SIZE(a), ',');
+ break;
+ }
+ break;
+ case Otohex:
+ if(a == nil)
+ break;
+ if(TAG(a) == 's'){
+ r = a;
+ break;
+ }
+ if(TAG(a) == 'b'){
+ r = tohexstr(a, SIZE(a), ',');
+ break;
+ }
+ break;
+ case Otoint:
+ if(a != nil && TAG(a) == 's')
+ r = mki(strtoull((char*)a, 0, 0));
+ else
+ r = mki(ival(a));
+ break;
+ }
+ store(r, FP->arg[1]);
+ return r;
+}
+
+static Op optab[] = {
+ [Obad] "", "", evalbad,
+ [Onop] "Noop", "", evalnop,
+ [Odebug] "Debug", "", evalnop,
+
+ [Ostr] ".str", "s", evaliarg0,
+ [Obyte] ".byte", "1", evaliarg0,
+ [Oword] ".word", "2", evaliarg0,
+ [Odword] ".dword", "4", evaliarg0,
+ [Oqword] ".qword", "8", evaliarg0,
+ [Oconst] ".const", "", evalconst,
+ [Onamec] ".name", "", evalnamec,
+ [Oenv] ".env", "", evalenv,
+
+ [Oname] "Name", "N*", evalname,
+ [Oscope] "Scope", "{n}", evalscope,
+ [Oalias] "Alias", "nN", evalalias,
+
+ [Odev] "Device", "{N}", evalscope,
+ [Ocpu] "Processor", "{N141}", evalscope,
+ [Othz] "ThermalZone", "{N}", evalscope,
+ [Oprc] "PowerResource", "{N12}", evalscope,
+
+ [Oreg] "OperationRegion", "N1ii", evalreg,
+ [Ofld] "Field", "{n1", evalfield,
+ [Oxfld] "IndexField", "{nn1", evalfield,
+ [Obfld] "BankField", "{nni1", evalfield,
+
+ [Ocfld] "CreateField", "*iiN", evalcfield,
+ [Ocfld0] "CreateBitField", "*iN", evalcfield,
+ [Ocfld1] "CreateByteField", "*iN", evalcfield,
+ [Ocfld2] "CreateWordField", "*iN", evalcfield,
+ [Ocfld4] "CreateDWordField", "*iN", evalcfield,
+ [Ocfld8] "CreateQWordField", "*iN", evalcfield,
+
+ [Opkg] "Package", "{1}", evalpkg,
+ [Ovpkg] "VarPackage", "{i}", evalpkg,
+ [Obuf] "Buffer", "{i", evalbuf,
+ [Omet] "Method", "{N1", evalmet,
+
+ [Oadd] "Add", "ii@", evalarith,
+ [Osub] "Subtract", "ii@", evalarith,
+ [Omod] "Mod", "ii@", evalarith,
+ [Omul] "Multiply", "ii@", evalarith,
+ [Odiv] "Divide", "ii@@", evalarith,
+ [Oshl] "ShiftLef", "ii@", evalarith,
+ [Oshr] "ShiftRight", "ii@", evalarith,
+ [Oand] "And", "ii@", evalarith,
+ [Onand] "Nand", "ii@", evalarith,
+ [Oor] "Or", "ii@", evalarith,
+ [Onor] "Nor", "ii@", evalarith,
+ [Oxor] "Xor", "ii@", evalarith,
+ [Onot] "Not", "i@", evalarith,
+
+ [Olbit] "FindSetLeftBit", "i@", evalarith,
+ [Orbit] "FindSetRightBit", "i@", evalarith,
+
+ [Oinc] "Increment", "@", evalarith,
+ [Odec] "Decrement", "@", evalarith,
+
+ [Oland] "LAnd", "ii", evalarith,
+ [Olor] "LOr", "ii", evalarith,
+ [Olnot] "LNot", "i", evalarith,
+
+ [Oleq] "LEqual", "**", evalcmp,
+ [Olgt] "LGreater", "**", evalcmp,
+ [Ollt] "LLess", "**", evalcmp,
+
+ [Omutex] "Mutex", "N1", evalnop,
+ [Oevent] "Event", "N", evalnop,
+
+ [Oif] "If", "{i}", evalcond,
+ [Oelse] "Else", "{}", evalcond,
+ [Owhile] "While", "{,i}", evalcond,
+ [Obreak] "Break", "", evalret,
+ [Oret] "Return", "*", evalret,
+ [Ocall] "Call", "", evalcall,
+
+ [Ostore] "Store", "*@", evalstore,
+ [Oindex] "Index", "@i@", evalindex,
+ [Omatch] "Match", "*1*1*i", evalmatch,
+ [Osize] "SizeOf", "*", evalsize,
+ [Oref] "RefOf", "@", evaliarg0,
+ [Ocref] "CondRefOf", "@@", evalcondref,
+ [Oderef] "DerefOf", "@", evalderef,
+ [Ocat] "Concatenate", "**@", evalcat,
+
+ [Oacq] "Acquire", "@2", evalnop,
+ [Osignal] "Signal", "@", evalnop,
+ [Orel] "Release", "@", evalnop,
+ [Ostall] "Stall", "i", evalstall,
+ [Osleep] "Sleep", "i", evalsleep,
+ [Oload] "Load", "*@}", evalload,
+ [Ounload] "Unload", "@", evalnop,
+
+ [Otodec] "ToDecimalString", "*@", evalconv,
+ [Otohex] "ToHexString", "*@", evalconv,
+ [Otoint] "ToInteger", "*@", evalconv,
+};
+
+static uchar octab1[] = {
+/* 00 */ Oconst, Oconst, Obad, Obad, Obad, Obad, Oalias, Obad,
+/* 08 */ Oname, Obad, Obyte, Oword, Odword, Ostr, Oqword, Obad,
+/* 10 */ Oscope, Obuf, Opkg, Ovpkg, Omet, Obad, Obad, Obad,
+/* 18 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* 20 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* 28 */ Obad, Obad, Obad, Obad, Obad, Obad, Onamec, Onamec,
+/* 30 */ Onamec, Onamec, Onamec, Onamec, Onamec, Onamec, Onamec, Onamec,
+/* 38 */ Onamec, Onamec, Obad, Obad, Obad, Obad, Obad, Obad,
+/* 40 */ Obad, Onamec, Onamec, Onamec, Onamec, Onamec, Onamec, Onamec,
+/* 48 */ Onamec, Onamec, Onamec, Onamec, Onamec, Onamec, Onamec, Onamec,
+/* 50 */ Onamec, Onamec, Onamec, Onamec, Onamec, Onamec, Onamec, Onamec,
+/* 58 */ Onamec, Onamec, Onamec, Obad, Onamec, Obad, Onamec, Onamec,
+/* 60 */ Oenv, Oenv, Oenv, Oenv, Oenv, Oenv, Oenv, Oenv,
+/* 68 */ Oenv, Oenv, Oenv, Oenv, Oenv, Oenv, Oenv, Obad,
+/* 70 */ Ostore, Oref, Oadd, Ocat, Osub, Oinc, Odec, Omul,
+/* 78 */ Odiv, Oshl, Oshr, Oand, Onand, Oor, Onor, Oxor,
+/* 80 */ Onot, Olbit, Orbit, Oderef, Obad, Omod, Obad, Osize,
+/* 88 */ Oindex, Omatch, Ocfld4, Ocfld2, Ocfld1, Ocfld0, Obad, Ocfld8,
+/* 90 */ Oland, Olor, Olnot, Oleq, Olgt, Ollt, Obad, Otodec,
+/* 98 */ Otohex, Otoint, Obad, Obad, Obad, Obad, Obad, Obad,
+/* A0 */ Oif, Oelse, Owhile, Onop, Oret, Obreak, Obad, Obad,
+/* A8 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* B0 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* B8 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* C0 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* C8 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* D0 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* D8 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* E0 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* E8 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* F0 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* F8 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Oconst,
+};
+
+static uchar octab2[] = {
+/* 00 */ Obad, Omutex, Oevent, Obad, Obad, Obad, Obad, Obad,
+/* 08 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* 10 */ Obad, Obad, Ocref, Ocfld, Obad, Obad, Obad, Obad,
+/* 18 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* 20 */ Oload, Ostall, Osleep, Oacq, Osignal,Obad, Obad, Orel,
+/* 28 */ Obad, Obad, Ounload,Obad, Obad, Obad, Obad, Obad,
+/* 30 */ Obad, Odebug, Obad, Obad, Obad, Obad, Obad, Obad,
+/* 38 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* 40 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* 48 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* 50 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* 58 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* 60 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* 68 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* 70 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* 78 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* 80 */ Oreg, Ofld, Odev, Ocpu, Oprc, Othz, Oxfld, Obfld,
+/* 88 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* 90 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* 98 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* A0 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* A8 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* B0 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* B8 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* C0 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* C8 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* D0 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* D8 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* E0 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* E8 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* F0 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+/* F8 */ Obad, Obad, Obad, Obad, Obad, Obad, Obad, Obad,
+};
+
+int
+amltag(void *p)
+{
+ return p ? TAG(p) : 0;
+}
+
+void*
+amlval(void *p)
+{
+ return deref(p);
+}
+
+uvlong
+amlint(void *p)
+{
+ return ival(p);
+}
+
+int
+amllen(void *p)
+{
+ while(p){
+ switch(TAG(p)){
+ case 'R':
+ p = *((Ref*)p)->ptr;
+ continue;
+ case 'n':
+ case 's':
+ return strlen((char*)p);
+ case 'p':
+ return SIZE(p)/sizeof(void*);
+ default:
+ return SIZE(p);
+ }
+ }
+ return 0;
+}
+
+void*
+amlnew(char tag, int len)
+{
+ switch(tag){
+ case 'i':
+ return mki(0);
+ case 'n':
+ case 's':
+ return mk(tag, len + 1);
+ case 'p':
+ return mk(tag, len * sizeof(void*));
+ default:
+ return mk(tag, len);
+ }
+}
+
+static void*
+evalosi(void)
+{
+ static char *w[] = {
+ "Windows 2001",
+ "Windows 2001 SP1",
+ "Windows 2001 SP2",
+ "Windows 2006",
+ };
+ char *s;
+ int i;
+
+ s = FP->env->arg[0];
+ if(s == nil || TAG(s) != 's')
+ return nil;
+ for(i = 0; i < nelem(w); i++)
+ if(strcmp(s, w[i]) == 0)
+ return mki(0xFFFFFFFF);
+ return nil;
+}
+
+void
+amlinit(void)
+{
+ Name *n;
+
+ FB = F0;
+ FP = F0-1;
+
+ fmtinstall('V', Vfmt);
+ fmtinstall('N', Nfmt);
+
+ if(!amlintmask)
+ amlintmask = ~0ULL;
+
+ n = mk('N', sizeof(Name));
+ n->up = n;
+
+ amlroot = n;
+
+ getname(amlroot, "_GPE", 1);
+ getname(amlroot, "_PR", 1);
+ getname(amlroot, "_SB", 1);
+ getname(amlroot, "_TZ", 1);
+ getname(amlroot, "_SI", 1);
+ getname(amlroot, "_GL", 1);
+
+ if(n = getname(amlroot, "_REV", 1))
+ n->v = mki(2);
+ if(n = getname(amlroot, "_OS", 1))
+ n->v = mks("Microsoft Windows");
+ if(n = getname(amlroot, "_OSI", 1)){
+ Method *m;
+
+ m = mk('m', sizeof(Method));
+ m->narg = 1;
+ m->eval = evalosi;
+ m->name = n;
+ n->v = m;
+ }
+}
+
+void
+amlexit(void)
+{
+ amlroot = nil;
+ FB = F0;
+ FP = F0-1;
+ gc();
+}
+
+int
+amlload(uchar *data, int len)
+{
+ int ret;
+
+ ret = xec(data, data+len, amlroot, nil, nil);
+ amlenum(amlroot, nil, fixnames, nil);
+ return ret;
+}
+
+void*
+amlwalk(void *dot, char *name)
+{
+ return getname(dot, name, 0);
+}
+
+void
+amlenum(void *dot, char *seg, int (*proc)(void *, void *), void *arg)
+{
+ Name *n, *d;
+ int rec;
+
+ d = dot;
+ if(d == nil || TAG(d) != 'N')
+ return;
+ do {
+ rec = 1;
+ if(seg == nil || memcmp(seg, d->seg, sizeof(d->seg)) == 0)
+ rec = (*proc)(d, arg) == 0;
+ for(n = d->down; n && rec; n = n->next)
+ amlenum(n, seg, proc, arg);
+ d = d->fork;
+ } while(d);
+}
+
+int
+amleval(void *dot, char *fmt, ...)
+{
+ va_list a;
+ Method *m;
+ void **r;
+ Env *e;
+ int i;
+
+ va_start(a, fmt);
+ e = mk('E', sizeof(Env));
+ for(i=0;*fmt;fmt++){
+ switch(*fmt){
+ default:
+ return -1;
+ case 's':
+ e->arg[i++] = mks(va_arg(a, char*));
+ break;
+ case 'i':
+ e->arg[i++] = mki(va_arg(a, int));
+ break;
+ case 'I':
+ e->arg[i++] = mki(va_arg(a, uvlong));
+ break;
+ case 'b':
+ case 'p':
+ case '*':
+ e->arg[i++] = va_arg(a, void*);
+ break;
+ }
+ }
+ r = va_arg(a, void**);
+ va_end(a);
+ if(dot = deref(dot))
+ if(TAG(dot) == 'm'){
+ m = dot;
+ if(i != m->narg)
+ return -1;
+ if(m->eval == nil)
+ return xec(m->start, m->end, forkname(m->name), e, r);
+ if(FB < F0 || FB >= FT)
+ return -1;
+ FP = FB;
+ FP->op = nil;
+ FP->env = e;
+ FP->narg = 0;
+ FP->dot = m->name;
+ FP->ref = FP->aux = nil;
+ dot = (*m->eval)();
+ }
+ if(r != nil)
+ *r = dot;
+ return 0;
+}
+
+void
+amltake(void *p)
+{
+ if(p != nil)
+ D2H(p)->mark |= 2;
+}
+
+void
+amldrop(void *p)
+{
+ if(p != nil)
+ D2H(p)->mark &= ~2;
+}
--- /dev/null
+++ b/libaml/amlalloc.c
@@ -1,0 +1,15 @@
+#include <u.h>
+#include <libc.h>
+#include <aml.h>
+
+void*
+amlalloc(int n)
+{
+ return mallocz(n, 1);
+}
+
+void
+amlfree(void *p)
+{
+ free(p);
+}
--- /dev/null
+++ b/libaml/amldelay.c
@@ -1,0 +1,8 @@
+#include <u.h>
+#include <libc.h>
+#include <aml.h>
+
+void
+amldelay(int)
+{
+}
--- /dev/null
+++ b/libaml/amlmapio.c
@@ -1,0 +1,9 @@
+#include <u.h>
+#include <libc.h>
+#include <aml.h>
+
+int
+amlmapio(Amlio*)
+{
+ return -1;
+}
--- /dev/null
+++ b/libaml/amlunmapio.c
@@ -1,0 +1,8 @@
+#include <u.h>
+#include <libc.h>
+#include <aml.h>
+
+void
+amlunmapio(Amlio*)
+{
+}
--- /dev/null
+++ b/libaml/mkfile
@@ -1,0 +1,13 @@
+<../mkconfig
+
+LIB=libaml.a
+OFILES=\
+ aml.$O\
+ amlmapio.$O\
+ amlunmapio.$O\
+ amlalloc.$O\
+ amldelay.$O\
+
+HFILES=$ROOT/include/aml.h
+
+<$ROOT/mkfiles/mksyslib-$SHELLTYPE
--- a/mkfile
+++ b/mkfile
@@ -260,7 +260,7 @@
bind /env/plan9.ini /n/src9/plan9.ini
cat /n/src9/plan9.ini
disk/mk9660 -c9j -B Inferno/386/9bootiso -E Inferno/386/efiboot.fat \
- -p <{echo ipc64; echo 9pc64; \
+ -p <{echo ipc64; echo 9pc64; echo plan9.ini; \
cat /n/src9/lib/proto/^(9boot inferno os src utils);} \
-s /n/src9 -v 'Inferno 9 Front ('^$objtype^')' $target
if(test -r /n/src9/Inferno/386/9boothyb){
--- /dev/null
+++ b/os/pc/archacpi.c
@@ -1,0 +1,1064 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/pci.h"
+#include "../port/error.h"
+
+#include "mp.h"
+
+#include <aml.h>
+
+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,
+};
+
+static Rsd *rsd;
+
+/* physical addresses visited by maptable() */
+static int ntblpa;
+static uvlong tblpa[64];
+
+/* successfully mapped tables */
+static int ntblmap;
+static Tbl *tblmap[64];
+
+static ushort
+get16(uchar *p){
+ return p[1]<<8 | p[0];
+}
+
+static uint
+get32(uchar *p){
+ return p[3]<<24 | p[2]<<16 | p[1]<<8 | p[0];
+}
+
+static uvlong
+get64(uchar *p){
+ uvlong u;
+
+ u = get32(p+4);
+ return u<<32 | get32(p);
+}
+
+static uint
+tbldlen(Tbl *t){
+ return get32(t->len) - Tblsz;
+}
+
+static long
+memcheck(uintptr pa, long len)
+{
+ int i;
+ uintptr pe;
+ Confmem *cm;
+
+ if(len <= 0)
+ return len;
+ pe = pa + len-1;
+ if(pe < pa){
+ len = -pa;
+ pe = pa + len-1;
+ }
+ if(pa < PADDR(CPU0END))
+ return 0;
+ if(pe >= PADDR(KTZERO) && pa < PADDR(end))
+ return PADDR(KTZERO) - pa;
+ for(i=0; i<nelem(conf.mem); i++){
+ cm = &conf.mem[i];
+ if(cm->npage == 0)
+ continue;
+ if(pe >= cm->base && pa <= cm->base + cm->npage*BY2PG - 1)
+ return cm->base - pa;
+ }
+ return len;
+}
+
+static void
+maptable(uvlong pa)
+{
+ uchar *p, *e;
+ u32int l;
+ Tbl *t;
+ int i;
+
+ if(-pa < 8)
+ return;
+
+ if(ntblpa >= nelem(tblpa) || ntblmap >= nelem(tblmap))
+ return;
+
+ for(i=0; i<ntblpa; i++){
+ if(pa == tblpa[i])
+ return;
+ }
+ tblpa[ntblpa++] = pa;
+
+ memreserve(pa, 8);
+ if((t = vmap(pa, 8)) == nil)
+ return;
+ l = get32(t->len);
+ if(l < Tblsz
+ || l >= 0x10000000
+ || -pa < l){
+ vunmap(t, 8);
+ return;
+ }
+ memreserve(pa, l);
+ vunmap(t, 8);
+ if((t = vmap(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
+maptables(void)
+{
+ if(rsd == nil || 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));
+ }
+}
+
+static Tbl*
+findtable(char sig[4])
+{
+ Tbl *t;
+ int i;
+
+ for(i=0; i<ntblmap; i++){
+ t = tblmap[i];
+ if(memcmp(t->sig, sig, 4) == 0)
+ return t;
+ }
+ return nil;
+}
+
+static Apic*
+findapic(int gsi, int *pintin)
+{
+ Apic *a;
+ int i;
+
+ for(i=0; i<=MaxAPICNO; i++){
+ if((a = mpioapic[i]) == nil)
+ continue;
+ if((a->flags & PcmpEN) == 0)
+ continue;
+ if(gsi >= a->gsibase && gsi <= a->gsibase+a->mre){
+ if(pintin)
+ *pintin = gsi - a->gsibase;
+ return a;
+ }
+ }
+ print("findapic: no ioapic found for gsi %d\n", gsi);
+ return nil;
+}
+
+static void
+addirq(int gsi, int type, int busno, int irq, int flags)
+{
+ Apic *a;
+ Bus *bus;
+ Aintr *ai;
+ PCMPintr *pi;
+ int intin;
+
+ if((a = findapic(gsi, &intin)) == nil)
+ return;
+
+ for(bus = mpbus; bus; bus = bus->next)
+ if(bus->type == type && bus->busno == busno)
+ goto Foundbus;
+
+ if((bus = xalloc(sizeof(Bus))) == nil)
+ panic("addirq: no memory for Bus");
+ bus->busno = busno;
+ bus->type = type;
+ if(type == BusISA){
+ bus->po = PcmpHIGH;
+ bus->el = PcmpEDGE;
+ if(mpisabus == -1)
+ mpisabus = busno;
+ } else {
+ bus->po = PcmpLOW;
+ bus->el = PcmpLEVEL;
+ }
+ if(mpbus)
+ mpbuslast->next = bus;
+ else
+ mpbus = bus;
+ mpbuslast = bus;
+
+Foundbus:
+ for(ai = bus->aintr; ai; ai = ai->next)
+ if(ai->intr->irq == irq)
+ return;
+
+ if((pi = xalloc(sizeof(PCMPintr))) == nil)
+ panic("addirq: no memory for PCMPintr");
+ pi->type = PcmpIOINTR;
+ pi->intr = PcmpINT;
+ pi->flags = flags & (PcmpPOMASK|PcmpELMASK);
+ pi->busno = busno;
+ pi->irq = irq;
+ pi->apicno = a->apicno;
+ pi->intin = intin;
+
+ if((ai = xalloc(sizeof(Aintr))) == nil)
+ panic("addirq: no memory for Aintr");
+ ai->intr = pi;
+ ai->apic = a;
+ ai->next = bus->aintr;
+ ai->bus = bus;
+ 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;
+ 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;
+ 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)
+ return -1;
+ if(pdev->bridge == nil)
+ return bno;
+ return BUSBNO(pdev->bridge->tbdf);
+}
+
+static int
+pciaddr(void *dot)
+{
+ int adr, bno;
+ void *x, *p;
+
+ 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;
+ p = nil;
+ if(amleval(x, "", &p) < 0)
+ break;
+ adr = amlint(p);
+ 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, gsi, flags;
+ int n, i;
+
+ bno = pcibusno(dot);
+ if(bno < 0)
+ return 1;
+
+ /* evalulate _PRT method */
+ p = nil;
+ if(amleval(dot, "", &p) < 0)
+ return 1;
+ if(amltag(p) != 'p')
+ return 1;
+
+ amltake(p);
+ n = amllen(p);
+ a = amlval(p);
+ for(i=0; i<n; i++){
+ if(amltag(a[i]) != 'p')
+ continue;
+ if(amllen(a[i]) != 4)
+ continue;
+ flags = 0;
+ b = amlval(a[i]);
+ dno = amlint(b[0])>>16;
+ pin = amlint(b[1]);
+ gsi = amlint(b[3]);
+ if(gsi==0){
+ gsi = setuplink(b[2], &flags);
+ if(gsi <= 0)
+ continue;
+ }
+ addirq(gsi, BusPCI, bno, (dno<<2)|pin, flags);
+ }
+ amldrop(p);
+
+ return 1;
+}
+
+static int
+enumec(void *dot, void *)
+{
+ int cmdport, dataport;
+ uchar *b;
+ void *x;
+ char *id;
+
+ b = nil;
+ id = eisaid(amlval(amlwalk(dot, "^_HID")));
+ if(id == nil || strcmp(id, "PNP0C09") != 0)
+ return 1;
+ if((x = amlwalk(dot, "^_CRS")) == nil)
+ return 1;
+ if(amleval(x, "", &b) < 0 || amltag(b) != 'b' || amllen(b) < 16)
+ return 1;
+ if(b[0] != 0x47 || b[8] != 0x47) /* two i/o port descriptors */
+ return 1;
+ dataport = b[0+2] | b[0+3]<<8;
+ cmdport = b[8+2] | b[8+3]<<8;
+ ecinit(cmdport, dataport);
+ return 1;
+}
+
+static s32
+readmem(Chan*, void *v, s32 n, s64 o)
+{
+ uvlong pa = (uvlong)o;
+ void *t;
+
+ if((n = memcheck(pa, n)) <= 0)
+ return 0;
+ if((t = vmap(pa, n)) == nil)
+ error(Enovmem);
+ if(waserror()){
+ vunmap(t, n);
+ nexterror();
+ }
+ memmove(v, t, n);
+ vunmap(t, n);
+ poperror();
+ return n;
+}
+
+static s32
+writemem(Chan*, void *v, s32 n, s64 o)
+{
+ uvlong pa = (uvlong)o;
+ void *t;
+
+ if(memcheck(pa, n) != n)
+ error(Eio);
+ if((t = vmap(pa, n)) == nil)
+ error(Enovmem);
+ if(waserror()){
+ vunmap(t, n);
+ nexterror();
+ }
+ memmove(t, v, n);
+ vunmap(t, n);
+ poperror();
+ return n;
+}
+
+static void
+acpiinit(void)
+{
+ Tbl *t;
+ Apic *a;
+ void *va;
+ uchar *s, *p, *e;
+ ulong lapicbase;
+ int machno, i, c;
+
+ amlinit();
+
+ /* load DSDT */
+ if((t = findtable("DSDT")) != nil){
+ amlintmask = (~0ULL) >> (t->rev <= 1)*32;
+ amlload(t->data, tbldlen(t));
+ }
+
+ /* 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);
+
+ t = findtable("APIC");
+ if(t == nil)
+ panic("acpiinit: no MADT (APIC) table");
+
+ s = t->data;
+ e = s + tbldlen(t);
+ lapicbase = get32(s); s += 8;
+ va = vmap(lapicbase, 1024);
+ print("LAPIC: %.8lux %#p\n", lapicbase, va);
+ if(va == nil)
+ panic("acpiinit: cannot map lapic %.8lux", lapicbase);
+
+ machno = 0;
+ for(p = s; p < e; p += c){
+ c = p[1];
+ if(c < 2 || (p+c) > e)
+ break;
+ switch(*p){
+ case 0x00: /* Processor Local APIC */
+ if(p[3] > MaxAPICNO)
+ break;
+ if((a = xalloc(sizeof(Apic))) == nil)
+ panic("acpiinit: no memory for Apic");
+ a->type = PcmpPROCESSOR;
+ a->apicno = p[3];
+ a->paddr = lapicbase;
+ a->addr = va;
+ a->lintr[0] = ApicIMASK;
+ a->lintr[1] = ApicIMASK;
+ 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 */
+ if(p[2] > MaxAPICNO)
+ break;
+ if((a = xalloc(sizeof(Apic))) == nil)
+ panic("acpiinit: no memory for io Apic");
+ a->type = PcmpIOAPIC;
+ a->apicno = p[2];
+ a->paddr = get32(p+4);
+ if((a->addr = vmap(a->paddr, 1024)) == nil)
+ panic("acpiinit: cannot map ioapic %.8lux", a->paddr);
+ a->gsibase = get32(p+8);
+ a->flags = PcmpEN;
+ 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;
+ case 0x03: /* NMI Source */
+ case 0x04: /* Local APIC NMI */
+ 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 */
+ break;
+ }
+ }
+
+ /* find embedded controller */
+ amlenum(amlroot, "_HID", enumec, nil);
+
+ /* 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();
+
+ /*
+ * Ininitalize local APIC and start application processors.
+ */
+ mpinit();
+}
+
+static void
+acpireset(void)
+{
+ uchar *p;
+ Tbl *t;
+
+ /* stop application processors */
+ mpshutdown();
+
+ /* locate and write platform reset register */
+ while((t = findtable("FACP")) != nil){
+ 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;
+ }
+
+ /* acpi shutdown failed, try generic reset */
+ archreset();
+}
+
+static int identify(void);
+extern int i8259irqno(int, int);
+extern void i8253init(void);
+
+extern int hpetprobe(uvlong);
+extern void hpetinit(void);
+extern uvlong hpetread(uvlong*);
+
+PCArch archacpi = {
+.id= "ACPI",
+.ident= identify,
+.reset= acpireset,
+.intrinit= acpiinit,
+.intrassign= mpintrassign,
+.intrirqno= i8259irqno,
+.intrenable= lapicintron,
+.intrdisable= lapicintroff,
+.clockinit= i8253init,
+.fastclock= i8253read,
+.timerset= lapictimerset,
+};
+
+static s32
+readtbls(Chan*, void *v, s32 n, s64 o)
+{
+ int i, l, m;
+ uchar *p;
+ Tbl *t;
+
+ maptables();
+
+ p = v;
+ for(i=0; n > 0 && i < ntblmap; i++){
+ t = tblmap[i];
+ l = get32(t->len);
+ if(o >= l){
+ o -= l;
+ continue;
+ }
+ m = l - o;
+ if(m > n)
+ m = n;
+ memmove(p, (uchar*)t + o, m);
+ p += m;
+ n -= m;
+ o = 0;
+ }
+ return p - (uchar*)v;
+}
+
+static int
+identify(void)
+{
+ uvlong v;
+ char *cp;
+ Tbl *t;
+
+ if((cp = getconf("*acpi")) == nil)
+ cp = "1"; /* search for rsd by default */
+ v = (uintptr)strtoull(cp, nil, 16);
+ if(v <= 1)
+ rsd = rsdsearch();
+ else {
+ memreserve(v, sizeof(Rsd));
+ rsd = vmap(v, sizeof(Rsd));
+ }
+ if(rsd == nil)
+ return 1;
+ if(checksum(rsd, 20) && checksum(rsd, 36))
+ return 1;
+ maptables();
+ addarchfile("acpitbls", 0444, readtbls, nil);
+ addarchfile("acpimem", 0600, readmem, writemem);
+ if(v == 0 || findtable("APIC") == nil)
+ return 1;
+ if((cp = getconf("*nomp")) != nil && strcmp(cp, "0") != 0)
+ return 1;
+ if(getconf("*nohpet") == nil
+ && (t = findtable("HPET")) != nil
+ && ((uchar*)t)[40] == 0
+ && hpetprobe(get64((uchar*)t+44)) == 0){
+ archacpi.clockinit = hpetinit;
+ archacpi.fastclock = hpetread;
+ }
+ 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
+readec(Amlio *io, void *data, int n, int off)
+{
+ int port, v;
+ uchar *p;
+
+ USED(io);
+ if(off < 0 || off >= 256)
+ return 0;
+ if(off+n > 256)
+ n = 256 - off;
+ p = data;
+ for(port = off; port < off+n; port++){
+ if((v = ecread(port)) < 0)
+ break;
+ *p++ = v;
+ }
+ return n;
+}
+
+static int
+writeec(Amlio *io, void *data, int n, int off)
+{
+ int port;
+ uchar *p;
+
+ USED(io);
+ if(off < 0 || off+n > 256)
+ return -1;
+ p = data;
+ for(port = off; port < off+n; port++)
+ if(ecwrite(port, *p++) < 0)
+ break;
+ return n;
+}
+
+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(memcheck(io->off, io->len) != io->len){
+ print("amlmapio: [%#p-%#p) overlaps usable memory\n",
+ (uintptr)io->off, (uintptr)(io->off+io->len));
+ break;
+ }
+ 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;
+ case EbctlSpace:
+ io->read = readec;
+ io->write = writeec;
+ 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);
+}
--- a/os/pc/pci.c
+++ /dev/null
@@ -1,1182 +1,0 @@
-#include "u.h"
-#include "../port/lib.h"
-#include "mem.h"
-#include "dat.h"
-#include "fns.h"
-#include "io.h"
-#include "../port/pci.h"
-
-typedef struct Pcisiz Pcisiz;
-struct Pcisiz
-{
- Pcidev* dev;
- int siz;
- int bar;
- int typ;
-};
-
-int pcimaxdno;
-
-static Lock pcicfglock;
-static Pcidev *pcilist, **pcitail;
-
-static char* bustypes[] = {
- "CBUSI",
- "CBUSII",
- "EISA",
- "FUTURE",
- "INTERN",
- "ISA",
- "MBI",
- "MBII",
- "MCA",
- "MPI",
- "MPSA",
- "NUBUS",
- "PCI",
- "PCMCIA",
- "TC",
- "VL",
- "VME",
- "XPRESS",
-};
-
-int
-tbdffmt(Fmt* fmt)
-{
- int type, tbdf;
-
- switch(fmt->r){
- default:
- return fmtstrcpy(fmt, "(tbdffmt)");
-
- case 'T':
- tbdf = va_arg(fmt->args, int);
- if(tbdf == BUSUNKNOWN) {
- return fmtstrcpy(fmt, "unknown");
- } else {
- type = BUSTYPE(tbdf);
- if(type < nelem(bustypes)) {
- return fmtprint(fmt, "%s.%d.%d.%d",
- bustypes[type], BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
- } else {
- return fmtprint(fmt, "%d.%d.%d.%d",
- type, BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
- }
- }
- }
-}
-
-static Pcidev*
-pcidevalloc(void)
-{
- Pcidev *p;
-
- p = xalloc(sizeof(*p));
- if(p == nil)
- panic("pci: no memory for Pcidev");
- return p;
-}
-
-void
-pcidevfree(Pcidev *p)
-{
- Pcidev **l;
-
- if(p == nil)
- return;
-
- while(p->bridge != nil)
- pcidevfree(p->bridge);
-
- if(p->parent != nil){
- for(l = &p->parent->bridge; *l != nil; l = &(*l)->link) {
- if(*l == p) {
- *l = p->link;
- break;
- }
- }
- }
- for(l = &pcilist; *l != nil; l = &(*l)->list) {
- if(*l == p) {
- if((*l = p->list) == nil)
- pcitail = l;
- break;
- }
- }
- /* leaked */
-}
-
-int
-pcicfgr8(Pcidev* p, int rno)
-{
- int data;
-
- ilock(&pcicfglock);
- data = pcicfgrw8(p->tbdf, rno, 0, 1);
- iunlock(&pcicfglock);
-
- return data;
-}
-void
-pcicfgw8(Pcidev* p, int rno, int data)
-{
- ilock(&pcicfglock);
- pcicfgrw8(p->tbdf, rno, data, 0);
- iunlock(&pcicfglock);
-}
-int
-pcicfgr16(Pcidev* p, int rno)
-{
- int data;
-
- ilock(&pcicfglock);
- data = pcicfgrw16(p->tbdf, rno, 0, 1);
- iunlock(&pcicfglock);
-
- return data;
-}
-void
-pcicfgw16(Pcidev* p, int rno, int data)
-{
- ilock(&pcicfglock);
- pcicfgrw16(p->tbdf, rno, data, 0);
- iunlock(&pcicfglock);
-}
-int
-pcicfgr32(Pcidev* p, int rno)
-{
- int data;
-
- ilock(&pcicfglock);
- data = pcicfgrw32(p->tbdf, rno, 0, 1);
- iunlock(&pcicfglock);
-
- return data;
-}
-void
-pcicfgw32(Pcidev* p, int rno, int data)
-{
- ilock(&pcicfglock);
- pcicfgrw32(p->tbdf, rno, data, 0);
- iunlock(&pcicfglock);
-}
-
-u32
-pcibarsize(Pcidev *p, int rno)
-{
- int v, size;
-
- ilock(&pcicfglock);
- v = pcicfgrw32(p->tbdf, rno, 0, 1);
- pcicfgrw32(p->tbdf, rno, -1, 0);
- size = pcicfgrw32(p->tbdf, rno, 0, 1);
- pcicfgrw32(p->tbdf, rno, v, 0);
- iunlock(&pcicfglock);
-
- if(rno == PciEBAR0 || rno == PciEBAR1){
- size &= ~0x7FF;
- } else if(v & 1){
- size = (short)size;
- size &= ~3;
- } else {
- size &= ~0xF;
- }
-
- return -size;
-}
-
-void
-pcisetbar(Pcidev *p, int rno, uvlong bar)
-{
- ilock(&pcicfglock);
- pcicfgrw32(p->tbdf, rno, bar, 0);
- if((bar&7) == 4 && rno >= PciBAR0 && rno < PciBAR0+4*(nelem(p->mem)-1))
- pcicfgrw32(p->tbdf, rno+4, bar>>32, 0);
- iunlock(&pcicfglock);
-}
-
-void
-pcisetwin(Pcidev *p, uvlong base, uvlong limit)
-{
- ilock(&pcicfglock);
- if(base & 1){
- pcicfgrw16(p->tbdf, PciIBR, (limit & 0xF000)|((base & 0xF000)>>8), 0);
- pcicfgrw32(p->tbdf, PciIUBR, (limit & 0xFFFF0000)|(base>>16), 0);
- } else if(base & 8){
- pcicfgrw32(p->tbdf, PciPMBR, (limit & 0xFFF00000)|((base & 0xFFF00000)>>16), 0);
- pcicfgrw32(p->tbdf, PciPUBR, base >> 32, 0);
- pcicfgrw32(p->tbdf, PciPULR, limit >> 32, 0);
- } else {
- pcicfgrw32(p->tbdf, PciMBR, (limit & 0xFFF00000)|((base & 0xFFF00000)>>16), 0);
- }
- iunlock(&pcicfglock);
-}
-
-static int
-pcisizcmp(void *a, void *b)
-{
- Pcisiz *aa, *bb;
-
- aa = a;
- bb = b;
- return aa->siz - bb->siz;
-}
-
-static ulong
-pcimask(ulong v)
-{
- ulong m;
-
- m = BI2BY*sizeof(v);
- for(m = 1<<(m-1); m != 0; m >>= 1) {
- if(m & v)
- break;
- }
-
- m--;
- if((v & m) == 0)
- return v;
-
- v |= m;
- return v+1;
-}
-
-void
-pcibusmap(Pcidev *root, uvlong *pmema, ulong *pioa, int wrreg)
-{
- Pcidev *p;
- int ntb, i, size, rno, hole;
- uvlong mema, smema;
- ulong ioa, sioa, v;
- Pcisiz *table, *tptr, *mtb, *itb;
-
- ioa = *pioa;
- mema = *pmema;
-
- ntb = 0;
- for(p = root; p != nil; p = p->link)
- ntb++;
-
- ntb *= (PciCIS-PciBAR0)/4;
- table = malloc((2*ntb+1)*sizeof(Pcisiz));
- if(table == nil)
- panic("pcibusmap: can't allocate memory");
- itb = table;
- mtb = table+ntb;
-
- /*
- * Build a table of sizes
- */
- for(p = root; p != nil; p = p->link) {
- if(p->ccrb == 0x06) {
- /* carbus bridge? */
- if(p->ccru == 0x07){
- if(pcicfgr32(p, PciBAR0) & 1)
- continue;
- size = pcibarsize(p, PciBAR0);
- if(size == 0)
- continue;
- mtb->dev = p;
- mtb->bar = 0;
- mtb->siz = size;
- mtb->typ = 0;
- mtb++;
- continue;
- }
-
- /* pci bridge? */
- if(p->ccru != 0x04 || p->bridge == nil)
- continue;
-
- sioa = ioa;
- smema = mema;
- pcibusmap(p->bridge, &smema, &sioa, 0);
-
- hole = pcimask(sioa-ioa);
- if(hole < (1<<12))
- hole = 1<<12;
- itb->dev = p;
- itb->bar = -1;
- itb->siz = hole;
- itb->typ = 0;
- itb++;
-
- hole = pcimask(smema-mema);
- if(hole < (1<<20))
- hole = 1<<20;
- mtb->dev = p;
- mtb->bar = -1;
- mtb->siz = hole;
- mtb->typ = 0;
- mtb++;
-
- size = pcibarsize(p, PciEBAR1);
- if(size != 0){
- mtb->dev = p;
- mtb->bar = -3;
- mtb->siz = size;
- mtb->typ = 0;
- mtb++;
- }
- continue;
- }
-
- size = pcibarsize(p, PciEBAR0);
- if(size != 0){
- mtb->dev = p;
- mtb->bar = -2;
- mtb->siz = size;
- mtb->typ = 0;
- mtb++;
- }
-
- for(i = 0; i < nelem(p->mem); i++) {
- rno = PciBAR0 + i*4;
- v = pcicfgr32(p, rno);
- size = pcibarsize(p, rno);
- if(size == 0)
- continue;
- if(v & 1) {
- itb->dev = p;
- itb->bar = i;
- itb->siz = size;
- itb->typ = 1;
- itb++;
- } else {
- mtb->dev = p;
- mtb->bar = i;
- mtb->siz = size;
- mtb->typ = v & 7;
- if(mtb->typ & 4)
- i++;
- mtb++;
- }
- }
- }
-
- /*
- * Sort both tables IO smallest first, Memory largest
- */
- qsort(table, itb-table, sizeof(Pcisiz), pcisizcmp);
- tptr = table+ntb;
- qsort(tptr, mtb-tptr, sizeof(Pcisiz), pcisizcmp);
-
- /*
- * Allocate IO address space on this bus
- */
- for(tptr = table; tptr < itb; tptr++) {
- hole = tptr->siz;
- if(tptr->bar == -1)
- hole = 1<<12;
- ioa = (ioa+hole-1) & ~(hole-1);
- if(wrreg){
- p = tptr->dev;
- if(tptr->bar == -1) {
- p->ioa.bar = ioa;
- p->ioa.size = tptr->siz;
- } else {
- p->mem[tptr->bar].size = tptr->siz;
- p->mem[tptr->bar].bar = ioa|1;
- pcisetbar(p, PciBAR0+tptr->bar*4, p->mem[tptr->bar].bar);
- }
- }
- ioa += tptr->siz;
- }
-
- /*
- * Allocate Memory address space on this bus
- */
- for(tptr = table+ntb; tptr < mtb; tptr++) {
- hole = tptr->siz;
- if(tptr->bar == -1)
- hole = 1<<20;
- mema = (mema+hole-1) & ~((uvlong)hole-1);
- if(wrreg){
- p = tptr->dev;
- if(tptr->bar == -1) {
- p->mema.bar = mema;
- p->mema.size = tptr->siz;
- } else if(tptr->bar == -2) {
- p->rom.bar = mema|1;
- p->rom.size = tptr->siz;
- pcisetbar(p, PciEBAR0, p->rom.bar);
- } else if(tptr->bar == -3) {
- p->rom.bar = mema|1;
- p->rom.size = tptr->siz;
- pcisetbar(p, PciEBAR1, p->rom.bar);
- } else {
- p->mem[tptr->bar].size = tptr->siz;
- p->mem[tptr->bar].bar = mema|tptr->typ;
- pcisetbar(p, PciBAR0+tptr->bar*4, p->mem[tptr->bar].bar);
- }
- }
- mema += tptr->siz;
- }
-
- *pmema = mema;
- *pioa = ioa;
- free(table);
-
- if(wrreg == 0)
- return;
-
- /*
- * Finally set all the bridge addresses & registers
- */
- for(p = root; p != nil; p = p->link) {
- if(p->bridge == nil) {
- pcienable(p);
- continue;
- }
-
- /* Set I/O and Mem windows */
- pcisetwin(p, p->ioa.bar|1, p->ioa.bar+p->ioa.size-1);
- pcisetwin(p, p->mema.bar|0, p->mema.bar+p->mema.size-1);
-
- /* Disable prefetch */
- pcisetwin(p, 0xFFF00000|8, 0);
-
- /* Enable the bridge */
- pcienable(p);
-
- sioa = p->ioa.bar;
- smema = p->mema.bar;
- pcibusmap(p->bridge, &smema, &sioa, 1);
- }
-}
-
-static int
-pcivalidwin(Pcidev *p, uvlong base, uvlong limit)
-{
- Pcidev *bridge = p->parent;
- char *typ;
-
- if(base & 1){
- typ = "io";
- base &= ~3;
- if(base > limit)
- return 0;
- if(bridge == nil)
- return 1;
- if(base >= bridge->ioa.bar && limit < (bridge->ioa.bar + bridge->ioa.size))
- return 1;
- } else {
- typ = "mem";
- base &= ~0xFULL;
- if(base > limit)
- return 0;
- if(bridge == nil)
- return 1;
- if(base >= bridge->mema.bar && limit < (bridge->mema.bar + bridge->mema.size))
- return 1;
- if(base >= bridge->prefa.bar && limit < (bridge->prefa.bar + bridge->prefa.size))
- return 1;
- }
- print("%T: %.2uX invalid %s-window: %.8llux-%.8llux\n", p->tbdf, p->ccrb, typ, base, limit);
- return 0;
-}
-
-static int
-pcivalidbar(Pcidev *p, uvlong bar, int size)
-{
- if(bar & 1){
- bar &= ~3;
- if(bar == 0 || size < 4 || (bar & (size-1)) != 0)
- return 0;
- return pcivalidwin(p, bar|1, bar+size-1);
- } else {
- bar &= ~0xFULL;
- if(bar == 0 || size < 16 || (bar & (size-1)) != 0)
- return 0;
- return pcivalidwin(p, bar|0, bar+size-1);
- }
-}
-
-int
-pciscan(int bno, Pcidev** list, Pcidev *parent)
-{
- Pcidev *p, *head, **tail;
- int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn;
-
- maxubn = bno;
- head = nil;
- tail = nil;
- for(dno = 0; dno <= pcimaxdno; dno++){
- maxfno = 0;
- for(fno = 0; fno <= maxfno; fno++){
- /*
- * For this possible device, form the
- * bus+device+function triplet needed to address it
- * and try to read the vendor and device ID.
- * If successful, allocate a device struct and
- * start to fill it in with some useful information
- * from the device's configuration space.
- */
- tbdf = MKBUS(BusPCI, bno, dno, fno);
-
- lock(&pcicfglock);
- l = pcicfgrw32(tbdf, PciVID, 0, 1);
- unlock(&pcicfglock);
-
- if(l == 0xFFFFFFFF || l == 0)
- continue;
- p = pcidevalloc();
- p->tbdf = tbdf;
- p->vid = l;
- p->did = l>>16;
-
- p->pcr = pcicfgr16(p, PciPCR);
- p->rid = pcicfgr8(p, PciRID);
- p->ccrp = pcicfgr8(p, PciCCRp);
- p->ccru = pcicfgr8(p, PciCCRu);
- p->ccrb = pcicfgr8(p, PciCCRb);
- p->cls = pcicfgr8(p, PciCLS);
- p->ltr = pcicfgr8(p, PciLTR);
- p->intl = pcicfgr8(p, PciINTL);
-
- /*
- * If the device is a multi-function device adjust the
- * loop count so all possible functions are checked.
- */
- hdt = pcicfgr8(p, PciHDT);
- if(hdt & 0x80)
- maxfno = MaxFNO;
-
- /*
- * If appropriate, read the base address registers
- * and work out the sizes.
- */
- switch(p->ccrb) {
- case 0x00: /* prehistoric */
- case 0x01: /* mass storage controller */
- case 0x02: /* network controller */
- case 0x03: /* display controller */
- case 0x04: /* multimedia device */
- case 0x07: /* simple comm. controllers */
- case 0x08: /* base system peripherals */
- case 0x09: /* input devices */
- case 0x0A: /* docking stations */
- case 0x0B: /* processors */
- case 0x0C: /* serial bus controllers */
- case 0x0D: /* wireless controllers */
- case 0x0E: /* intelligent I/O controllers */
- case 0x0F: /* sattelite communication controllers */
- case 0x10: /* encryption/decryption controllers */
- case 0x11: /* signal processing controllers */
- if((hdt & 0x7F) != 0)
- break;
- rno = PciBAR0;
- for(i = 0; i < nelem(p->mem); i++) {
- p->mem[i].bar = (ulong)pcicfgr32(p, rno);
- p->mem[i].size = pcibarsize(p, rno);
- if((p->mem[i].bar & 7) == 4 && i < nelem(p->mem)-1){
- rno += 4;
- p->mem[i++].bar |= (uvlong)pcicfgr32(p, rno) << 32;
- p->mem[i].bar = 0;
- p->mem[i].size = 0;
- }
- rno += 4;
- }
-
- p->rom.bar = (ulong)pcicfgr32(p, PciEBAR0);
- p->rom.size = pcibarsize(p, PciEBAR0);
- break;
-
- case 0x06: /* bridge device */
- /* cardbus bridge? */
- if(p->ccru == 0x07){
- p->mem[0].bar = (ulong)pcicfgr32(p, PciBAR0);
- p->mem[0].size = pcibarsize(p, PciBAR0);
- break;
- }
-
- /* pci bridge? */
- if(p->ccru != 0x04)
- break;
-
- p->rom.bar = (ulong)pcicfgr32(p, PciEBAR1);
- p->rom.size = pcibarsize(p, PciEBAR1);
- break;
- case 0x05: /* memory controller */
- default:
- break;
- }
-
- p->parent = parent;
- if(head != nil)
- *tail = p;
- else
- head = p;
- tail = &p->link;
-
- if(pcilist != nil)
- *pcitail = p;
- else
- pcilist = p;
- pcitail = &p->list;
- }
- }
-
- *list = head;
- for(p = head; p != nil; p = p->link){
- /*
- * Find PCI-PCI bridges and recursively descend the tree.
- */
- switch(p->ccrb) {
- case 0x06:
- if(p->ccru == 0x04)
- break;
- default:
- /* check and clear invalid membars for non bridges */
- for(i = 0; i < nelem(p->mem); i++) {
- if(p->mem[i].size == 0)
- continue;
- if(!pcivalidbar(p, p->mem[i].bar, p->mem[i].size)){
- if(p->mem[i].bar & 1)
- p->mem[i].bar &= 3;
- else
- p->mem[i].bar &= 0xF;
- pcisetbar(p, PciBAR0 + i*4, p->mem[i].bar);
- }
- }
- if(p->rom.size) {
- if((p->rom.bar & 1) == 0
- || !pcivalidbar(p, p->rom.bar & ~0x7FFULL, p->rom.size)){
- p->rom.bar = 0;
- pcisetbar(p, PciEBAR0, p->rom.bar);
- }
- }
- continue;
- }
-
- if(p->rom.size) {
- if((p->rom.bar & 1) == 0
- || !pcivalidbar(p, p->rom.bar & ~0x7FFULL, p->rom.size)){
- p->rom.bar = 0;
- pcisetbar(p, PciEBAR1, p->rom.bar);
- }
- }
-
- /*
- * If the secondary or subordinate bus number is not
- * initialised try to do what the PCI BIOS should have
- * done and fill in the numbers as the tree is descended.
- * On the way down the subordinate bus number is set to
- * the maximum as it's not known how many buses are behind
- * this one; the final value is set on the way back up.
- */
- sbn = pcicfgr8(p, PciSBN);
- ubn = pcicfgr8(p, PciUBN);
-
- if(sbn == 0 || ubn == 0) {
- sbn = maxubn+1;
- /*
- * Make sure memory, I/O and master enables are
- * off, set the primary, secondary and subordinate
- * bus numbers and clear the secondary status before
- * attempting to scan the secondary bus.
- *
- * Initialisation of the bridge should be done here.
- */
- p->pcr = 0;
- pcicfgw32(p, PciPCR, 0xFFFF0000);
- l = (MaxUBN<<16)|(sbn<<8)|bno;
- pcicfgw32(p, PciPBN, l);
- pcicfgw16(p, PciSPSR, 0xFFFF);
-
- p->ioa.bar = 0;
- p->ioa.size = 0;
- p->mema.bar = 0;
- p->mema.size = 0;
- p->prefa.bar = 0;
- p->prefa.size = 0;
-
- pcisetwin(p, 0xFFFFF000|1, 0);
- pcisetwin(p, 0xFFF00000|0, 0);
- pcisetwin(p, 0xFFF00000|8, 0);
-
- maxubn = pciscan(sbn, &p->bridge, p);
- l = (maxubn<<16)|(sbn<<8)|bno;
-
- pcicfgw32(p, PciPBN, l);
- }
- else {
- uvlong base, limit;
- ulong v;
-
- v = pcicfgr16(p, PciIBR);
- limit = (v & 0xF000) | 0x0FFF;
- base = (v & 0x00F0) << 8;
- if((v & 0x0F) == 0x01){
- v = pcicfgr32(p, PciIUBR);
- limit |= (v & 0xFFFF0000);
- base |= (v & 0x0000FFFF) << 16;
- }
- if(pcivalidwin(p, base|1, limit)){
- p->ioa.bar = base;
- p->ioa.size = (limit - base)+1;
- } else {
- pcisetwin(p, 0xFFFFF000|1, 0);
- p->ioa.bar = 0;
- p->ioa.size = 0;
- }
-
- v = pcicfgr32(p, PciMBR);
- limit = (v & 0xFFF00000) | 0x000FFFFF;
- base = (v & 0x0000FFF0) << 16;
- if(pcivalidwin(p, base|0, limit)){
- p->mema.bar = base;
- p->mema.size = (limit - base)+1;
- } else {
- pcisetwin(p, 0xFFF00000|0, 0);
- p->mema.bar = 0;
- p->mema.size = 0;
- }
-
- v = pcicfgr32(p, PciPMBR);
- limit = (v & 0xFFF00000) | 0x000FFFFF;
- limit |= (uvlong)pcicfgr32(p, PciPULR) << 32;
- base = (v & 0x0000FFF0) << 16;
- base |= (uvlong)pcicfgr32(p, PciPUBR) << 32;
- if(pcivalidwin(p, base|8, limit)){
- p->prefa.bar = base;
- p->prefa.size = (limit - base)+1;
- } else {
- pcisetwin(p, 0xFFF00000|8, 0);
- p->prefa.bar = 0;
- p->prefa.size = 0;
- }
-
- if(ubn > maxubn)
- maxubn = ubn;
- pciscan(sbn, &p->bridge, p);
- }
- }
-
- return maxubn;
-}
-
-void
-pcibussize(Pcidev *root, uvlong *msize, ulong *iosize)
-{
- *msize = 0;
- *iosize = 0;
- pcibusmap(root, msize, iosize, 0);
-}
-
-Pcidev*
-pcimatch(Pcidev* prev, int vid, int did)
-{
- if(prev == nil)
- prev = pcilist;
- else
- prev = prev->list;
-
- while(prev != nil){
- if((vid == 0 || prev->vid == vid)
- && (did == 0 || prev->did == did))
- break;
- prev = prev->list;
- }
- return prev;
-}
-
-Pcidev*
-pcimatchtbdf(int tbdf)
-{
- Pcidev *pcidev;
-
- for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) {
- if(pcidev->tbdf == tbdf)
- break;
- }
- return pcidev;
-}
-
-uchar
-pciipin(Pcidev *pci, uchar pin)
-{
- if (pci == nil)
- pci = pcilist;
-
- while (pci != nil) {
- uchar intl;
-
- if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff)
- return pci->intl;
-
- if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0)
- return intl;
-
- pci = pci->list;
- }
- return 0;
-}
-
-static void
-pcilhinv(Pcidev* p)
-{
- int i;
- Pcidev *t;
-
- for(t = p; t != nil; t = t->link) {
- print("%d %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d ",
- BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf),
- t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl);
- for(i = 0; i < nelem(p->mem); i++) {
- if(t->mem[i].size == 0)
- continue;
- print("%d:%.8llux %d ", i, t->mem[i].bar, t->mem[i].size);
- }
- if(t->rom.bar || t->rom.size)
- print("rom:%.8llux %d ", t->rom.bar, t->rom.size);
- if(t->ioa.bar || t->ioa.size)
- print("ioa:%.8llux-%.8llux %d ", t->ioa.bar, t->ioa.bar+t->ioa.size, t->ioa.size);
- if(t->mema.bar || t->mema.size)
- print("mema:%.8llux-%.8llux %d ", t->mema.bar, t->mema.bar+t->mema.size, t->mema.size);
- if(t->prefa.bar || t->prefa.size)
- print("prefa:%.8llux-%.8llux %llud ", t->prefa.bar, t->prefa.bar+t->prefa.size, t->prefa.size);
- if(t->bridge)
- print("->%d", BUSBNO(t->bridge->tbdf));
- print("\n");
- }
- while(p != nil) {
- if(p->bridge != nil)
- pcilhinv(p->bridge);
- p = p->link;
- }
-}
-
-void
-pcihinv(Pcidev* p)
-{
- print("bus dev type vid did intl memory\n");
- pcilhinv(p);
-}
-
-void
-pcireset(void)
-{
- Pcidev *p;
-
- for(p = pcilist; p != nil; p = p->list) {
- /* don't mess with the bridges */
- if(p->ccrb == 0x06)
- continue;
- pcidisable(p);
- }
-}
-
-void
-pcisetioe(Pcidev* p)
-{
- p->pcr |= IOen;
- pcicfgw16(p, PciPCR, p->pcr);
-}
-
-void
-pciclrioe(Pcidev* p)
-{
- p->pcr &= ~IOen;
- pcicfgw16(p, PciPCR, p->pcr);
-}
-
-void
-pcisetbme(Pcidev* p)
-{
- p->pcr |= MASen;
- pcicfgw16(p, PciPCR, p->pcr);
-}
-
-void
-pciclrbme(Pcidev* p)
-{
- p->pcr &= ~MASen;
- pcicfgw16(p, PciPCR, p->pcr);
-}
-
-void
-pcisetmwi(Pcidev* p)
-{
- p->pcr |= MemWrInv;
- pcicfgw16(p, PciPCR, p->pcr);
-}
-
-void
-pciclrmwi(Pcidev* p)
-{
- p->pcr &= ~MemWrInv;
- pcicfgw16(p, PciPCR, p->pcr);
-}
-
-static int
-enumcaps(Pcidev *p, int (*fmatch)(Pcidev*, int, int, int), int arg)
-{
- int i, r, cap, off;
-
- /* status register bit 4 has capabilities */
- if((pcicfgr16(p, PciPSR) & 1<<4) == 0)
- return -1;
- switch(pcicfgr8(p, PciHDT) & 0x7F){
- default:
- return -1;
- case 0: /* etc */
- case 1: /* pci to pci bridge */
- off = 0x34;
- break;
- case 2: /* cardbus bridge */
- off = 0x14;
- break;
- }
- for(i = 48; i--;){
- off = pcicfgr8(p, off);
- if(off < 0x40 || (off & 3))
- break;
- off &= ~3;
- cap = pcicfgr8(p, off);
- if(cap == 0xff)
- break;
- r = (*fmatch)(p, cap, off, arg);
- if(r < 0)
- break;
- if(r == 0)
- return off;
- off++;
- }
- return -1;
-}
-
-static int
-matchcap(Pcidev *, int cap, int, int arg)
-{
- return cap != arg;
-}
-
-static int
-matchhtcap(Pcidev *p, int cap, int off, int arg)
-{
- int mask;
-
- if(cap != PciCapHTC)
- return 1;
- if(arg == 0x00 || arg == 0x20)
- mask = 0xE0;
- else
- mask = 0xF8;
- cap = pcicfgr8(p, off+3);
- return (cap & mask) != arg;
-}
-
-int
-pcicap(Pcidev *p, int cap)
-{
- return enumcaps(p, matchcap, cap);
-}
-
-int
-pcihtcap(Pcidev *p, int cap)
-{
- return enumcaps(p, matchhtcap, cap);
-}
-
-static int
-pcigetmsi(Pcidev *p)
-{
- if(p->msi != 0)
- return p->msi;
- return p->msi = pcicap(p, PciCapMSI);
-}
-
-enum {
- MSICtrl = 0x02, /* message control register (16 bit) */
- MSIAddr = 0x04, /* message address register (64 bit) */
- MSIData32 = 0x08, /* message data register for 32 bit MSI (16 bit) */
- MSIData64 = 0x0C, /* message data register for 64 bit MSI (16 bit) */
-};
-
-int
-pcimsienable(Pcidev *p, uvlong addr, ulong data)
-{
- int off, ok64;
-
- if((off = pcigetmsi(p)) < 0)
- return -1;
- ok64 = (pcicfgr16(p, off + MSICtrl) & (1<<7)) != 0;
- pcicfgw32(p, off + MSIAddr, addr);
- if(ok64) pcicfgw32(p, off + MSIAddr+4, addr >> 32);
- pcicfgw16(p, off + (ok64 ? MSIData64 : MSIData32), data);
- pcicfgw16(p, off + MSICtrl, 1);
- return 0;
-}
-
-int
-pcimsidisable(Pcidev *p)
-{
- int off;
-
- if((off = pcigetmsi(p)) < 0)
- return -1;
- pcicfgw16(p, off + MSICtrl, 0);
- return 0;
-}
-
-enum {
- MSIXCtrl = 0x02,
-};
-
-static int
-pcimsixdisable(Pcidev *p)
-{
- int off;
-
- if((off = pcicap(p, PciCapMSIX)) < 0)
- return -1;
- pcicfgw16(p, off + MSIXCtrl, 0);
- return 0;
-}
-
-static int
-pcigetpmrb(Pcidev *p)
-{
- if(p->pmrb != 0)
- return p->pmrb;
- return p->pmrb = pcicap(p, PciCapPMG);
-}
-
-int
-pcigetpms(Pcidev* p)
-{
- int pmcsr, ptr;
-
- if((ptr = pcigetpmrb(p)) == -1)
- return -1;
-
- /*
- * Power Management Register Block:
- * offset 0: Capability ID
- * 1: next item pointer
- * 2: capabilities
- * 4: control/status
- * 6: bridge support extensions
- * 7: data
- */
- pmcsr = pcicfgr16(p, ptr+4);
-
- return pmcsr & 0x0003;
-}
-
-int
-pcisetpms(Pcidev* p, int state)
-{
- int ostate, pmc, pmcsr, ptr;
-
- if((ptr = pcigetpmrb(p)) == -1)
- return -1;
-
- pmc = pcicfgr16(p, ptr+2);
- pmcsr = pcicfgr16(p, ptr+4);
- ostate = pmcsr & 0x0003;
- pmcsr &= ~0x0003;
-
- switch(state){
- default:
- return -1;
- case 0:
- break;
- case 1:
- if(!(pmc & 0x0200))
- return -1;
- break;
- case 2:
- if(!(pmc & 0x0400))
- return -1;
- break;
- case 3:
- break;
- }
- pmcsr |= state;
- pcicfgw16(p, ptr+4, pmcsr);
-
- return ostate;
-}
-
-void
-pcienable(Pcidev *p)
-{
- uint pcr;
- int i;
-
- if(p == nil)
- return;
-
- pcienable(p->parent);
-
- switch(pcisetpms(p, 0)){
- case 1:
- print("pcienable %T: wakeup from D1\n", p->tbdf);
- break;
- case 2:
- print("pcienable %T: wakeup from D2\n", p->tbdf);
- if(p->bridge != nil)
- delay(100); /* B2: minimum delay 50ms */
- else
- delay(1); /* D2: minimum delay 200µs */
- break;
- case 3:
- print("pcienable %T: wakeup from D3\n", p->tbdf);
- delay(100); /* D3: minimum delay 50ms */
-
- /* restore registers */
- for(i = 0; i < nelem(p->mem); i++){
- if(p->mem[i].size == 0)
- continue;
- pcisetbar(p, PciBAR0+i*4, p->mem[i].bar);
- }
-
- pcicfgw8(p, PciINTL, p->intl);
- pcicfgw8(p, PciLTR, p->ltr);
- pcicfgw8(p, PciCLS, p->cls);
- pcicfgw16(p, PciPCR, p->pcr);
- break;
- }
-
- if(p->ltr == 0 || p->ltr == 0xFF){
- p->ltr = 64;
- pcicfgw8(p,PciLTR, p->ltr);
- }
- if(p->cls == 0 || p->cls == 0xFF){
- p->cls = 64/4;
- pcicfgw8(p, PciCLS, p->cls);
- }
-
- if(p->bridge != nil)
- pcr = IOen|MEMen|MASen;
- else {
- pcr = 0;
- for(i = 0; i < nelem(p->mem); i++){
- if(p->mem[i].size == 0)
- continue;
- if(p->mem[i].bar & 1)
- pcr |= IOen;
- else
- pcr |= MEMen;
- }
- }
-
- if((p->pcr & pcr) != pcr){
- print("pcienable %T: pcr %ux->%ux\n", p->tbdf, p->pcr, p->pcr|pcr);
- p->pcr |= pcr;
- pcicfgw32(p, PciPCR, 0xFFFF0000|p->pcr);
- }
-}
-
-void
-pcidisable(Pcidev *p)
-{
- if(p == nil)
- return;
- pcimsixdisable(p);
- pcimsidisable(p);
- pciclrbme(p);
-}
--- a/os/pc64/fns.h
+++ b/os/pc64/fns.h
@@ -130,7 +130,7 @@
int (*pcicfgrw16)(int, int, int, int);
int (*pcicfgrw32)(int, int, int, int);
int pciscan(int bno, Pcidev **list, Pcidev *parent);
-u32 pcibarsize(Pcidev*, int);
+s32 pcibarsize(Pcidev*, int);
int pcicfgr8(Pcidev*, int);
int pcicfgr16(Pcidev*, int);
int pcicfgr32(Pcidev*, int);
--- a/os/pc64/main.c
+++ b/os/pc64/main.c
@@ -146,8 +146,8 @@
doc("confinit");
confinit();
xinit();
- i8253init();
- /* TODO 9front if(i8237alloc != nil)
+ /* TODO 9front does this for dma
+ if(i8237alloc != nil)
i8237alloc(); */
doc("pcicfginit");
pcicfginit();
--- a/os/pc64/mkfile
+++ b/os/pc64/mkfile
@@ -27,13 +27,10 @@
fpu.$O\
portclock.$O\
tod.$O\
- i8253.$O\
- i8259.$O\
main.$O\
memmap.$O\
memory.$O\
mmu.$O\
- mtrr.$O\
trap.$O\
bootargs.$O\
$CONF.root.$O\
--- a/os/pc64/pc64
+++ b/os/pc64/pc64
@@ -44,6 +44,7 @@
# il
lib
+ aml
fis
interp
keyring
@@ -72,12 +73,13 @@
ethervirtio pci
misc
- archgeneric
-# archacpi mp apic ec hpet
+ pci pcipc
+ archgeneric devkbd i8259 i8253
+ archacpi mp apic squidboy ec hpet
archmp mp apic squidboy
-# mtrr
+ mtrr
+
bios32
- pcipc
cga
uarti8250
--- /dev/null
+++ b/os/port/pci.c
@@ -1,0 +1,1182 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/pci.h"
+
+typedef struct Pcisiz Pcisiz;
+struct Pcisiz
+{
+ Pcidev* dev;
+ int siz;
+ int bar;
+ int typ;
+};
+
+int pcimaxdno;
+
+static Lock pcicfglock;
+static Pcidev *pcilist, **pcitail;
+
+static char* bustypes[] = {
+ "CBUSI",
+ "CBUSII",
+ "EISA",
+ "FUTURE",
+ "INTERN",
+ "ISA",
+ "MBI",
+ "MBII",
+ "MCA",
+ "MPI",
+ "MPSA",
+ "NUBUS",
+ "PCI",
+ "PCMCIA",
+ "TC",
+ "VL",
+ "VME",
+ "XPRESS",
+};
+
+int
+tbdffmt(Fmt* fmt)
+{
+ int type, tbdf;
+
+ switch(fmt->r){
+ default:
+ return fmtstrcpy(fmt, "(tbdffmt)");
+
+ case 'T':
+ tbdf = va_arg(fmt->args, int);
+ if(tbdf == BUSUNKNOWN) {
+ return fmtstrcpy(fmt, "unknown");
+ } else {
+ type = BUSTYPE(tbdf);
+ if(type < nelem(bustypes)) {
+ return fmtprint(fmt, "%s.%d.%d.%d",
+ bustypes[type], BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
+ } else {
+ return fmtprint(fmt, "%d.%d.%d.%d",
+ type, BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
+ }
+ }
+ }
+}
+
+static Pcidev*
+pcidevalloc(void)
+{
+ Pcidev *p;
+
+ p = xalloc(sizeof(*p));
+ if(p == nil)
+ panic("pci: no memory for Pcidev");
+ return p;
+}
+
+void
+pcidevfree(Pcidev *p)
+{
+ Pcidev **l;
+
+ if(p == nil)
+ return;
+
+ while(p->bridge != nil)
+ pcidevfree(p->bridge);
+
+ if(p->parent != nil){
+ for(l = &p->parent->bridge; *l != nil; l = &(*l)->link) {
+ if(*l == p) {
+ *l = p->link;
+ break;
+ }
+ }
+ }
+ for(l = &pcilist; *l != nil; l = &(*l)->list) {
+ if(*l == p) {
+ if((*l = p->list) == nil)
+ pcitail = l;
+ break;
+ }
+ }
+ /* leaked */
+}
+
+int
+pcicfgr8(Pcidev* p, int rno)
+{
+ int data;
+
+ ilock(&pcicfglock);
+ data = pcicfgrw8(p->tbdf, rno, 0, 1);
+ iunlock(&pcicfglock);
+
+ return data;
+}
+void
+pcicfgw8(Pcidev* p, int rno, int data)
+{
+ ilock(&pcicfglock);
+ pcicfgrw8(p->tbdf, rno, data, 0);
+ iunlock(&pcicfglock);
+}
+int
+pcicfgr16(Pcidev* p, int rno)
+{
+ int data;
+
+ ilock(&pcicfglock);
+ data = pcicfgrw16(p->tbdf, rno, 0, 1);
+ iunlock(&pcicfglock);
+
+ return data;
+}
+void
+pcicfgw16(Pcidev* p, int rno, int data)
+{
+ ilock(&pcicfglock);
+ pcicfgrw16(p->tbdf, rno, data, 0);
+ iunlock(&pcicfglock);
+}
+int
+pcicfgr32(Pcidev* p, int rno)
+{
+ int data;
+
+ ilock(&pcicfglock);
+ data = pcicfgrw32(p->tbdf, rno, 0, 1);
+ iunlock(&pcicfglock);
+
+ return data;
+}
+void
+pcicfgw32(Pcidev* p, int rno, int data)
+{
+ ilock(&pcicfglock);
+ pcicfgrw32(p->tbdf, rno, data, 0);
+ iunlock(&pcicfglock);
+}
+
+int
+pcibarsize(Pcidev *p, int rno)
+{
+ int v, size;
+
+ ilock(&pcicfglock);
+ v = pcicfgrw32(p->tbdf, rno, 0, 1);
+ pcicfgrw32(p->tbdf, rno, -1, 0);
+ size = pcicfgrw32(p->tbdf, rno, 0, 1);
+ pcicfgrw32(p->tbdf, rno, v, 0);
+ iunlock(&pcicfglock);
+
+ if(rno == PciEBAR0 || rno == PciEBAR1){
+ size &= ~0x7FF;
+ } else if(v & 1){
+ size = (short)size;
+ size &= ~3;
+ } else {
+ size &= ~0xF;
+ }
+
+ return -size;
+}
+
+void
+pcisetbar(Pcidev *p, int rno, uvlong bar)
+{
+ ilock(&pcicfglock);
+ pcicfgrw32(p->tbdf, rno, bar, 0);
+ if((bar&7) == 4 && rno >= PciBAR0 && rno < PciBAR0+4*(nelem(p->mem)-1))
+ pcicfgrw32(p->tbdf, rno+4, bar>>32, 0);
+ iunlock(&pcicfglock);
+}
+
+void
+pcisetwin(Pcidev *p, uvlong base, uvlong limit)
+{
+ ilock(&pcicfglock);
+ if(base & 1){
+ pcicfgrw16(p->tbdf, PciIBR, (limit & 0xF000)|((base & 0xF000)>>8), 0);
+ pcicfgrw32(p->tbdf, PciIUBR, (limit & 0xFFFF0000)|(base>>16), 0);
+ } else if(base & 8){
+ pcicfgrw32(p->tbdf, PciPMBR, (limit & 0xFFF00000)|((base & 0xFFF00000)>>16), 0);
+ pcicfgrw32(p->tbdf, PciPUBR, base >> 32, 0);
+ pcicfgrw32(p->tbdf, PciPULR, limit >> 32, 0);
+ } else {
+ pcicfgrw32(p->tbdf, PciMBR, (limit & 0xFFF00000)|((base & 0xFFF00000)>>16), 0);
+ }
+ iunlock(&pcicfglock);
+}
+
+static int
+pcisizcmp(void *a, void *b)
+{
+ Pcisiz *aa, *bb;
+
+ aa = a;
+ bb = b;
+ return aa->siz - bb->siz;
+}
+
+static ulong
+pcimask(ulong v)
+{
+ ulong m;
+
+ m = BI2BY*sizeof(v);
+ for(m = 1<<(m-1); m != 0; m >>= 1) {
+ if(m & v)
+ break;
+ }
+
+ m--;
+ if((v & m) == 0)
+ return v;
+
+ v |= m;
+ return v+1;
+}
+
+void
+pcibusmap(Pcidev *root, uvlong *pmema, ulong *pioa, int wrreg)
+{
+ Pcidev *p;
+ int ntb, i, size, rno, hole;
+ uvlong mema, smema;
+ ulong ioa, sioa, v;
+ Pcisiz *table, *tptr, *mtb, *itb;
+
+ ioa = *pioa;
+ mema = *pmema;
+
+ ntb = 0;
+ for(p = root; p != nil; p = p->link)
+ ntb++;
+
+ ntb *= (PciCIS-PciBAR0)/4;
+ table = malloc((2*ntb+1)*sizeof(Pcisiz));
+ if(table == nil)
+ panic("pcibusmap: can't allocate memory");
+ itb = table;
+ mtb = table+ntb;
+
+ /*
+ * Build a table of sizes
+ */
+ for(p = root; p != nil; p = p->link) {
+ if(p->ccrb == 0x06) {
+ /* carbus bridge? */
+ if(p->ccru == 0x07){
+ if(pcicfgr32(p, PciBAR0) & 1)
+ continue;
+ size = pcibarsize(p, PciBAR0);
+ if(size == 0)
+ continue;
+ mtb->dev = p;
+ mtb->bar = 0;
+ mtb->siz = size;
+ mtb->typ = 0;
+ mtb++;
+ continue;
+ }
+
+ /* pci bridge? */
+ if(p->ccru != 0x04 || p->bridge == nil)
+ continue;
+
+ sioa = ioa;
+ smema = mema;
+ pcibusmap(p->bridge, &smema, &sioa, 0);
+
+ hole = pcimask(sioa-ioa);
+ if(hole < (1<<12))
+ hole = 1<<12;
+ itb->dev = p;
+ itb->bar = -1;
+ itb->siz = hole;
+ itb->typ = 0;
+ itb++;
+
+ hole = pcimask(smema-mema);
+ if(hole < (1<<20))
+ hole = 1<<20;
+ mtb->dev = p;
+ mtb->bar = -1;
+ mtb->siz = hole;
+ mtb->typ = 0;
+ mtb++;
+
+ size = pcibarsize(p, PciEBAR1);
+ if(size != 0){
+ mtb->dev = p;
+ mtb->bar = -3;
+ mtb->siz = size;
+ mtb->typ = 0;
+ mtb++;
+ }
+ continue;
+ }
+
+ size = pcibarsize(p, PciEBAR0);
+ if(size != 0){
+ mtb->dev = p;
+ mtb->bar = -2;
+ mtb->siz = size;
+ mtb->typ = 0;
+ mtb++;
+ }
+
+ for(i = 0; i < nelem(p->mem); i++) {
+ rno = PciBAR0 + i*4;
+ v = pcicfgr32(p, rno);
+ size = pcibarsize(p, rno);
+ if(size == 0)
+ continue;
+ if(v & 1) {
+ itb->dev = p;
+ itb->bar = i;
+ itb->siz = size;
+ itb->typ = 1;
+ itb++;
+ } else {
+ mtb->dev = p;
+ mtb->bar = i;
+ mtb->siz = size;
+ mtb->typ = v & 7;
+ if(mtb->typ & 4)
+ i++;
+ mtb++;
+ }
+ }
+ }
+
+ /*
+ * Sort both tables IO smallest first, Memory largest
+ */
+ qsort(table, itb-table, sizeof(Pcisiz), pcisizcmp);
+ tptr = table+ntb;
+ qsort(tptr, mtb-tptr, sizeof(Pcisiz), pcisizcmp);
+
+ /*
+ * Allocate IO address space on this bus
+ */
+ for(tptr = table; tptr < itb; tptr++) {
+ hole = tptr->siz;
+ if(tptr->bar == -1)
+ hole = 1<<12;
+ ioa = (ioa+hole-1) & ~(hole-1);
+ if(wrreg){
+ p = tptr->dev;
+ if(tptr->bar == -1) {
+ p->ioa.bar = ioa;
+ p->ioa.size = tptr->siz;
+ } else {
+ p->mem[tptr->bar].size = tptr->siz;
+ p->mem[tptr->bar].bar = ioa|1;
+ pcisetbar(p, PciBAR0+tptr->bar*4, p->mem[tptr->bar].bar);
+ }
+ }
+ ioa += tptr->siz;
+ }
+
+ /*
+ * Allocate Memory address space on this bus
+ */
+ for(tptr = table+ntb; tptr < mtb; tptr++) {
+ hole = tptr->siz;
+ if(tptr->bar == -1)
+ hole = 1<<20;
+ mema = (mema+hole-1) & ~((uvlong)hole-1);
+ if(wrreg){
+ p = tptr->dev;
+ if(tptr->bar == -1) {
+ p->mema.bar = mema;
+ p->mema.size = tptr->siz;
+ } else if(tptr->bar == -2) {
+ p->rom.bar = mema|1;
+ p->rom.size = tptr->siz;
+ pcisetbar(p, PciEBAR0, p->rom.bar);
+ } else if(tptr->bar == -3) {
+ p->rom.bar = mema|1;
+ p->rom.size = tptr->siz;
+ pcisetbar(p, PciEBAR1, p->rom.bar);
+ } else {
+ p->mem[tptr->bar].size = tptr->siz;
+ p->mem[tptr->bar].bar = mema|tptr->typ;
+ pcisetbar(p, PciBAR0+tptr->bar*4, p->mem[tptr->bar].bar);
+ }
+ }
+ mema += tptr->siz;
+ }
+
+ *pmema = mema;
+ *pioa = ioa;
+ free(table);
+
+ if(wrreg == 0)
+ return;
+
+ /*
+ * Finally set all the bridge addresses & registers
+ */
+ for(p = root; p != nil; p = p->link) {
+ if(p->bridge == nil) {
+ pcienable(p);
+ continue;
+ }
+
+ /* Set I/O and Mem windows */
+ pcisetwin(p, p->ioa.bar|1, p->ioa.bar+p->ioa.size-1);
+ pcisetwin(p, p->mema.bar|0, p->mema.bar+p->mema.size-1);
+
+ /* Disable prefetch */
+ pcisetwin(p, 0xFFF00000|8, 0);
+
+ /* Enable the bridge */
+ pcienable(p);
+
+ sioa = p->ioa.bar;
+ smema = p->mema.bar;
+ pcibusmap(p->bridge, &smema, &sioa, 1);
+ }
+}
+
+static int
+pcivalidwin(Pcidev *p, uvlong base, uvlong limit)
+{
+ Pcidev *bridge = p->parent;
+ char *typ;
+
+ if(base & 1){
+ typ = "io";
+ base &= ~3;
+ if(base > limit)
+ return 0;
+ if(bridge == nil)
+ return 1;
+ if(base >= bridge->ioa.bar && limit < (bridge->ioa.bar + bridge->ioa.size))
+ return 1;
+ } else {
+ typ = "mem";
+ base &= ~0xFULL;
+ if(base > limit)
+ return 0;
+ if(bridge == nil)
+ return 1;
+ if(base >= bridge->mema.bar && limit < (bridge->mema.bar + bridge->mema.size))
+ return 1;
+ if(base >= bridge->prefa.bar && limit < (bridge->prefa.bar + bridge->prefa.size))
+ return 1;
+ }
+ print("%T: %.2uX invalid %s-window: %.8llux-%.8llux\n", p->tbdf, p->ccrb, typ, base, limit);
+ return 0;
+}
+
+static int
+pcivalidbar(Pcidev *p, uvlong bar, int size)
+{
+ if(bar & 1){
+ bar &= ~3;
+ if(bar == 0 || size < 4 || (bar & (size-1)) != 0)
+ return 0;
+ return pcivalidwin(p, bar|1, bar+size-1);
+ } else {
+ bar &= ~0xFULL;
+ if(bar == 0 || size < 16 || (bar & (size-1)) != 0)
+ return 0;
+ return pcivalidwin(p, bar|0, bar+size-1);
+ }
+}
+
+int
+pciscan(int bno, Pcidev** list, Pcidev *parent)
+{
+ Pcidev *p, *head, **tail;
+ int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn;
+
+ maxubn = bno;
+ head = nil;
+ tail = nil;
+ for(dno = 0; dno <= pcimaxdno; dno++){
+ maxfno = 0;
+ for(fno = 0; fno <= maxfno; fno++){
+ /*
+ * For this possible device, form the
+ * bus+device+function triplet needed to address it
+ * and try to read the vendor and device ID.
+ * If successful, allocate a device struct and
+ * start to fill it in with some useful information
+ * from the device's configuration space.
+ */
+ tbdf = MKBUS(BusPCI, bno, dno, fno);
+
+ lock(&pcicfglock);
+ l = pcicfgrw32(tbdf, PciVID, 0, 1);
+ unlock(&pcicfglock);
+
+ if(l == 0xFFFFFFFF || l == 0)
+ continue;
+ p = pcidevalloc();
+ p->tbdf = tbdf;
+ p->vid = l;
+ p->did = l>>16;
+
+ p->pcr = pcicfgr16(p, PciPCR);
+ p->rid = pcicfgr8(p, PciRID);
+ p->ccrp = pcicfgr8(p, PciCCRp);
+ p->ccru = pcicfgr8(p, PciCCRu);
+ p->ccrb = pcicfgr8(p, PciCCRb);
+ p->cls = pcicfgr8(p, PciCLS);
+ p->ltr = pcicfgr8(p, PciLTR);
+ p->intl = pcicfgr8(p, PciINTL);
+
+ /*
+ * If the device is a multi-function device adjust the
+ * loop count so all possible functions are checked.
+ */
+ hdt = pcicfgr8(p, PciHDT);
+ if(hdt & 0x80)
+ maxfno = MaxFNO;
+
+ /*
+ * If appropriate, read the base address registers
+ * and work out the sizes.
+ */
+ switch(p->ccrb) {
+ case 0x00: /* prehistoric */
+ case 0x01: /* mass storage controller */
+ case 0x02: /* network controller */
+ case 0x03: /* display controller */
+ case 0x04: /* multimedia device */
+ case 0x07: /* simple comm. controllers */
+ case 0x08: /* base system peripherals */
+ case 0x09: /* input devices */
+ case 0x0A: /* docking stations */
+ case 0x0B: /* processors */
+ case 0x0C: /* serial bus controllers */
+ case 0x0D: /* wireless controllers */
+ case 0x0E: /* intelligent I/O controllers */
+ case 0x0F: /* sattelite communication controllers */
+ case 0x10: /* encryption/decryption controllers */
+ case 0x11: /* signal processing controllers */
+ if((hdt & 0x7F) != 0)
+ break;
+ rno = PciBAR0;
+ for(i = 0; i < nelem(p->mem); i++) {
+ p->mem[i].bar = (ulong)pcicfgr32(p, rno);
+ p->mem[i].size = pcibarsize(p, rno);
+ if((p->mem[i].bar & 7) == 4 && i < nelem(p->mem)-1){
+ rno += 4;
+ p->mem[i++].bar |= (uvlong)pcicfgr32(p, rno) << 32;
+ p->mem[i].bar = 0;
+ p->mem[i].size = 0;
+ }
+ rno += 4;
+ }
+
+ p->rom.bar = (ulong)pcicfgr32(p, PciEBAR0);
+ p->rom.size = pcibarsize(p, PciEBAR0);
+ break;
+
+ case 0x06: /* bridge device */
+ /* cardbus bridge? */
+ if(p->ccru == 0x07){
+ p->mem[0].bar = (ulong)pcicfgr32(p, PciBAR0);
+ p->mem[0].size = pcibarsize(p, PciBAR0);
+ break;
+ }
+
+ /* pci bridge? */
+ if(p->ccru != 0x04)
+ break;
+
+ p->rom.bar = (ulong)pcicfgr32(p, PciEBAR1);
+ p->rom.size = pcibarsize(p, PciEBAR1);
+ break;
+ case 0x05: /* memory controller */
+ default:
+ break;
+ }
+
+ p->parent = parent;
+ if(head != nil)
+ *tail = p;
+ else
+ head = p;
+ tail = &p->link;
+
+ if(pcilist != nil)
+ *pcitail = p;
+ else
+ pcilist = p;
+ pcitail = &p->list;
+ }
+ }
+
+ *list = head;
+ for(p = head; p != nil; p = p->link){
+ /*
+ * Find PCI-PCI bridges and recursively descend the tree.
+ */
+ switch(p->ccrb) {
+ case 0x06:
+ if(p->ccru == 0x04)
+ break;
+ default:
+ /* check and clear invalid membars for non bridges */
+ for(i = 0; i < nelem(p->mem); i++) {
+ if(p->mem[i].size == 0)
+ continue;
+ if(!pcivalidbar(p, p->mem[i].bar, p->mem[i].size)){
+ if(p->mem[i].bar & 1)
+ p->mem[i].bar &= 3;
+ else
+ p->mem[i].bar &= 0xF;
+ pcisetbar(p, PciBAR0 + i*4, p->mem[i].bar);
+ }
+ }
+ if(p->rom.size) {
+ if((p->rom.bar & 1) == 0
+ || !pcivalidbar(p, p->rom.bar & ~0x7FFULL, p->rom.size)){
+ p->rom.bar = 0;
+ pcisetbar(p, PciEBAR0, p->rom.bar);
+ }
+ }
+ continue;
+ }
+
+ if(p->rom.size) {
+ if((p->rom.bar & 1) == 0
+ || !pcivalidbar(p, p->rom.bar & ~0x7FFULL, p->rom.size)){
+ p->rom.bar = 0;
+ pcisetbar(p, PciEBAR1, p->rom.bar);
+ }
+ }
+
+ /*
+ * If the secondary or subordinate bus number is not
+ * initialised try to do what the PCI BIOS should have
+ * done and fill in the numbers as the tree is descended.
+ * On the way down the subordinate bus number is set to
+ * the maximum as it's not known how many buses are behind
+ * this one; the final value is set on the way back up.
+ */
+ sbn = pcicfgr8(p, PciSBN);
+ ubn = pcicfgr8(p, PciUBN);
+
+ if(sbn == 0 || ubn == 0) {
+ sbn = maxubn+1;
+ /*
+ * Make sure memory, I/O and master enables are
+ * off, set the primary, secondary and subordinate
+ * bus numbers and clear the secondary status before
+ * attempting to scan the secondary bus.
+ *
+ * Initialisation of the bridge should be done here.
+ */
+ p->pcr = 0;
+ pcicfgw32(p, PciPCR, 0xFFFF0000);
+ l = (MaxUBN<<16)|(sbn<<8)|bno;
+ pcicfgw32(p, PciPBN, l);
+ pcicfgw16(p, PciSPSR, 0xFFFF);
+
+ p->ioa.bar = 0;
+ p->ioa.size = 0;
+ p->mema.bar = 0;
+ p->mema.size = 0;
+ p->prefa.bar = 0;
+ p->prefa.size = 0;
+
+ pcisetwin(p, 0xFFFFF000|1, 0);
+ pcisetwin(p, 0xFFF00000|0, 0);
+ pcisetwin(p, 0xFFF00000|8, 0);
+
+ maxubn = pciscan(sbn, &p->bridge, p);
+ l = (maxubn<<16)|(sbn<<8)|bno;
+
+ pcicfgw32(p, PciPBN, l);
+ }
+ else {
+ uvlong base, limit;
+ ulong v;
+
+ v = pcicfgr16(p, PciIBR);
+ limit = (v & 0xF000) | 0x0FFF;
+ base = (v & 0x00F0) << 8;
+ if((v & 0x0F) == 0x01){
+ v = pcicfgr32(p, PciIUBR);
+ limit |= (v & 0xFFFF0000);
+ base |= (v & 0x0000FFFF) << 16;
+ }
+ if(pcivalidwin(p, base|1, limit)){
+ p->ioa.bar = base;
+ p->ioa.size = (limit - base)+1;
+ } else {
+ pcisetwin(p, 0xFFFFF000|1, 0);
+ p->ioa.bar = 0;
+ p->ioa.size = 0;
+ }
+
+ v = pcicfgr32(p, PciMBR);
+ limit = (v & 0xFFF00000) | 0x000FFFFF;
+ base = (v & 0x0000FFF0) << 16;
+ if(pcivalidwin(p, base|0, limit)){
+ p->mema.bar = base;
+ p->mema.size = (limit - base)+1;
+ } else {
+ pcisetwin(p, 0xFFF00000|0, 0);
+ p->mema.bar = 0;
+ p->mema.size = 0;
+ }
+
+ v = pcicfgr32(p, PciPMBR);
+ limit = (v & 0xFFF00000) | 0x000FFFFF;
+ limit |= (uvlong)pcicfgr32(p, PciPULR) << 32;
+ base = (v & 0x0000FFF0) << 16;
+ base |= (uvlong)pcicfgr32(p, PciPUBR) << 32;
+ if(pcivalidwin(p, base|8, limit)){
+ p->prefa.bar = base;
+ p->prefa.size = (limit - base)+1;
+ } else {
+ pcisetwin(p, 0xFFF00000|8, 0);
+ p->prefa.bar = 0;
+ p->prefa.size = 0;
+ }
+
+ if(ubn > maxubn)
+ maxubn = ubn;
+ pciscan(sbn, &p->bridge, p);
+ }
+ }
+
+ return maxubn;
+}
+
+void
+pcibussize(Pcidev *root, uvlong *msize, ulong *iosize)
+{
+ *msize = 0;
+ *iosize = 0;
+ pcibusmap(root, msize, iosize, 0);
+}
+
+Pcidev*
+pcimatch(Pcidev* prev, int vid, int did)
+{
+ if(prev == nil)
+ prev = pcilist;
+ else
+ prev = prev->list;
+
+ while(prev != nil){
+ if((vid == 0 || prev->vid == vid)
+ && (did == 0 || prev->did == did))
+ break;
+ prev = prev->list;
+ }
+ return prev;
+}
+
+Pcidev*
+pcimatchtbdf(int tbdf)
+{
+ Pcidev *pcidev;
+
+ for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) {
+ if(pcidev->tbdf == tbdf)
+ break;
+ }
+ return pcidev;
+}
+
+uchar
+pciipin(Pcidev *pci, uchar pin)
+{
+ if (pci == nil)
+ pci = pcilist;
+
+ while (pci != nil) {
+ uchar intl;
+
+ if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff)
+ return pci->intl;
+
+ if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0)
+ return intl;
+
+ pci = pci->list;
+ }
+ return 0;
+}
+
+static void
+pcilhinv(Pcidev* p)
+{
+ int i;
+ Pcidev *t;
+
+ for(t = p; t != nil; t = t->link) {
+ print("%d %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d ",
+ BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf),
+ t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl);
+ for(i = 0; i < nelem(p->mem); i++) {
+ if(t->mem[i].size == 0)
+ continue;
+ print("%d:%.8llux %d ", i, t->mem[i].bar, t->mem[i].size);
+ }
+ if(t->rom.bar || t->rom.size)
+ print("rom:%.8llux %d ", t->rom.bar, t->rom.size);
+ if(t->ioa.bar || t->ioa.size)
+ print("ioa:%.8llux-%.8llux %d ", t->ioa.bar, t->ioa.bar+t->ioa.size, t->ioa.size);
+ if(t->mema.bar || t->mema.size)
+ print("mema:%.8llux-%.8llux %d ", t->mema.bar, t->mema.bar+t->mema.size, t->mema.size);
+ if(t->prefa.bar || t->prefa.size)
+ print("prefa:%.8llux-%.8llux %llud ", t->prefa.bar, t->prefa.bar+t->prefa.size, t->prefa.size);
+ if(t->bridge)
+ print("->%d", BUSBNO(t->bridge->tbdf));
+ print("\n");
+ }
+ while(p != nil) {
+ if(p->bridge != nil)
+ pcilhinv(p->bridge);
+ p = p->link;
+ }
+}
+
+void
+pcihinv(Pcidev* p)
+{
+ print("bus dev type vid did intl memory\n");
+ pcilhinv(p);
+}
+
+void
+pcireset(void)
+{
+ Pcidev *p;
+
+ for(p = pcilist; p != nil; p = p->list) {
+ /* don't mess with the bridges */
+ if(p->ccrb == 0x06)
+ continue;
+ pcidisable(p);
+ }
+}
+
+void
+pcisetioe(Pcidev* p)
+{
+ p->pcr |= IOen;
+ pcicfgw16(p, PciPCR, p->pcr);
+}
+
+void
+pciclrioe(Pcidev* p)
+{
+ p->pcr &= ~IOen;
+ pcicfgw16(p, PciPCR, p->pcr);
+}
+
+void
+pcisetbme(Pcidev* p)
+{
+ p->pcr |= MASen;
+ pcicfgw16(p, PciPCR, p->pcr);
+}
+
+void
+pciclrbme(Pcidev* p)
+{
+ p->pcr &= ~MASen;
+ pcicfgw16(p, PciPCR, p->pcr);
+}
+
+void
+pcisetmwi(Pcidev* p)
+{
+ p->pcr |= MemWrInv;
+ pcicfgw16(p, PciPCR, p->pcr);
+}
+
+void
+pciclrmwi(Pcidev* p)
+{
+ p->pcr &= ~MemWrInv;
+ pcicfgw16(p, PciPCR, p->pcr);
+}
+
+int
+pcienumcaps(Pcidev *p, int (*fmatch)(Pcidev*, int, int, int), int arg)
+{
+ int i, r, cap, off;
+
+ /* status register bit 4 has capabilities */
+ if((pcicfgr16(p, PciPSR) & 1<<4) == 0)
+ return -1;
+ switch(pcicfgr8(p, PciHDT) & 0x7F){
+ default:
+ return -1;
+ case 0: /* etc */
+ case 1: /* pci to pci bridge */
+ off = 0x34;
+ break;
+ case 2: /* cardbus bridge */
+ off = 0x14;
+ break;
+ }
+ for(i = 48; i--;){
+ off = pcicfgr8(p, off);
+ if(off < 0x40 || (off & 3))
+ break;
+ off &= ~3;
+ cap = pcicfgr8(p, off);
+ if(cap == 0xff)
+ break;
+ r = (*fmatch)(p, cap, off, arg);
+ if(r < 0)
+ break;
+ if(r == 0)
+ return off;
+ off++;
+ }
+ return -1;
+}
+
+static int
+matchcap(Pcidev *, int cap, int, int arg)
+{
+ return cap != arg;
+}
+
+static int
+matchhtcap(Pcidev *p, int cap, int off, int arg)
+{
+ int mask;
+
+ if(cap != PciCapHTC)
+ return 1;
+ if(arg == 0x00 || arg == 0x20)
+ mask = 0xE0;
+ else
+ mask = 0xF8;
+ cap = pcicfgr8(p, off+3);
+ return (cap & mask) != arg;
+}
+
+int
+pcicap(Pcidev *p, int cap)
+{
+ return pcienumcaps(p, matchcap, cap);
+}
+
+int
+pcihtcap(Pcidev *p, int cap)
+{
+ return pcienumcaps(p, matchhtcap, cap);
+}
+
+static int
+pcigetmsi(Pcidev *p)
+{
+ if(p->msi != 0)
+ return p->msi;
+ return p->msi = pcicap(p, PciCapMSI);
+}
+
+enum {
+ MSICtrl = 0x02, /* message control register (16 bit) */
+ MSIAddr = 0x04, /* message address register (64 bit) */
+ MSIData32 = 0x08, /* message data register for 32 bit MSI (16 bit) */
+ MSIData64 = 0x0C, /* message data register for 64 bit MSI (16 bit) */
+};
+
+int
+pcimsienable(Pcidev *p, uvlong addr, ulong data)
+{
+ int off, ok64;
+
+ if((off = pcigetmsi(p)) < 0)
+ return -1;
+ ok64 = (pcicfgr16(p, off + MSICtrl) & (1<<7)) != 0;
+ pcicfgw32(p, off + MSIAddr, addr);
+ if(ok64) pcicfgw32(p, off + MSIAddr+4, addr >> 32);
+ pcicfgw16(p, off + (ok64 ? MSIData64 : MSIData32), data);
+ pcicfgw16(p, off + MSICtrl, 1);
+ return 0;
+}
+
+int
+pcimsidisable(Pcidev *p)
+{
+ int off;
+
+ if((off = pcigetmsi(p)) < 0)
+ return -1;
+ pcicfgw16(p, off + MSICtrl, 0);
+ return 0;
+}
+
+enum {
+ MSIXCtrl = 0x02,
+};
+
+static int
+pcimsixdisable(Pcidev *p)
+{
+ int off;
+
+ if((off = pcicap(p, PciCapMSIX)) < 0)
+ return -1;
+ pcicfgw16(p, off + MSIXCtrl, 0);
+ return 0;
+}
+
+static int
+pcigetpmrb(Pcidev *p)
+{
+ if(p->pmrb != 0)
+ return p->pmrb;
+ return p->pmrb = pcicap(p, PciCapPMG);
+}
+
+int
+pcigetpms(Pcidev* p)
+{
+ int pmcsr, ptr;
+
+ if((ptr = pcigetpmrb(p)) == -1)
+ return -1;
+
+ /*
+ * Power Management Register Block:
+ * offset 0: Capability ID
+ * 1: next item pointer
+ * 2: capabilities
+ * 4: control/status
+ * 6: bridge support extensions
+ * 7: data
+ */
+ pmcsr = pcicfgr16(p, ptr+4);
+
+ return pmcsr & 0x0003;
+}
+
+int
+pcisetpms(Pcidev* p, int state)
+{
+ int ostate, pmc, pmcsr, ptr;
+
+ if((ptr = pcigetpmrb(p)) == -1)
+ return -1;
+
+ pmc = pcicfgr16(p, ptr+2);
+ pmcsr = pcicfgr16(p, ptr+4);
+ ostate = pmcsr & 0x0003;
+ pmcsr &= ~0x0003;
+
+ switch(state){
+ default:
+ return -1;
+ case 0:
+ break;
+ case 1:
+ if(!(pmc & 0x0200))
+ return -1;
+ break;
+ case 2:
+ if(!(pmc & 0x0400))
+ return -1;
+ break;
+ case 3:
+ break;
+ }
+ pmcsr |= state;
+ pcicfgw16(p, ptr+4, pmcsr);
+
+ return ostate;
+}
+
+void
+pcienable(Pcidev *p)
+{
+ uint pcr;
+ int i;
+
+ if(p == nil)
+ return;
+
+ pcienable(p->parent);
+
+ switch(pcisetpms(p, 0)){
+ case 1:
+ print("pcienable %T: wakeup from D1\n", p->tbdf);
+ break;
+ case 2:
+ print("pcienable %T: wakeup from D2\n", p->tbdf);
+ if(p->bridge != nil)
+ delay(100); /* B2: minimum delay 50ms */
+ else
+ delay(1); /* D2: minimum delay 200µs */
+ break;
+ case 3:
+ print("pcienable %T: wakeup from D3\n", p->tbdf);
+ delay(100); /* D3: minimum delay 50ms */
+
+ /* restore registers */
+ for(i = 0; i < nelem(p->mem); i++){
+ if(p->mem[i].size == 0)
+ continue;
+ pcisetbar(p, PciBAR0+i*4, p->mem[i].bar);
+ }
+
+ pcicfgw8(p, PciINTL, p->intl);
+ pcicfgw8(p, PciLTR, p->ltr);
+ pcicfgw8(p, PciCLS, p->cls);
+ pcicfgw16(p, PciPCR, p->pcr);
+ break;
+ }
+
+ if(p->ltr == 0 || p->ltr == 0xFF){
+ p->ltr = 64;
+ pcicfgw8(p,PciLTR, p->ltr);
+ }
+ if(p->cls == 0 || p->cls == 0xFF){
+ p->cls = 64/4;
+ pcicfgw8(p, PciCLS, p->cls);
+ }
+
+ if(p->bridge != nil)
+ pcr = IOen|MEMen|MASen;
+ else {
+ pcr = 0;
+ for(i = 0; i < nelem(p->mem); i++){
+ if(p->mem[i].size == 0)
+ continue;
+ if(p->mem[i].bar & 1)
+ pcr |= IOen;
+ else
+ pcr |= MEMen;
+ }
+ }
+
+ if((p->pcr & pcr) != pcr){
+ print("pcienable %T: pcr %ux->%ux\n", p->tbdf, p->pcr, p->pcr|pcr);
+ p->pcr |= pcr;
+ pcicfgw32(p, PciPCR, 0xFFFF0000|p->pcr);
+ }
+}
+
+void
+pcidisable(Pcidev *p)
+{
+ if(p == nil)
+ return;
+ pcimsixdisable(p);
+ pcimsidisable(p);
+ pciclrbme(p);
+}
--- a/os/port/pci.h
+++ b/os/port/pci.h
@@ -240,7 +240,7 @@
extern Pcidev* pcimatch(Pcidev* prev, int vid, int did);
extern Pcidev* pcimatchtbdf(int tbdf);
-extern u32 pcibarsize(Pcidev *, int rno);
+extern s32 pcibarsize(Pcidev *, int rno);
extern void pcisetbar(Pcidev *, int, uvlong);
extern uchar pciipin(Pcidev *pci, uchar pin);
@@ -254,6 +254,7 @@
extern int pcicap(Pcidev *p, int cap);
extern int pcihtcap(Pcidev *p, int cap);
+extern int pcienumcaps(Pcidev *p, int (*fmatch)(Pcidev*, int, int, int), int arg);
extern int pcimsienable(Pcidev *p, uvlong addr, ulong data);
extern int pcimsidisable(Pcidev *p);