code: 9ferno

Download patch

ref: 8d8022281735dcf8a6c0c2c52568180e1d4ba17c
parent: d468a21a8275e1af3c23a46d50b1531415e65e33
author: 9ferno <gophone2015@gmail.com>
date: Wed Sep 15 04:23:09 EDT 2021

9front acpi

diff: cannot open b/libaml//null: file does not exist: 'b/libaml//null'
--- /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);