| /* | 
 |  * This file is part of the UCB release of Plan 9. It is subject to the license | 
 |  * terms in the LICENSE file found in the top-level directory of this | 
 |  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
 |  * part of the UCB release of Plan 9, including this file, may be copied, | 
 |  * modified, propagated, or distributed except according to the terms contained | 
 |  * in the LICENSE file. | 
 |  */ | 
 |  | 
 | #include <vfs.h> | 
 | #include <kfs.h> | 
 | #include <slab.h> | 
 | #include <kmalloc.h> | 
 | #include <kref.h> | 
 | #include <string.h> | 
 | #include <stdio.h> | 
 | #include <assert.h> | 
 | #include <error.h> | 
 | #include <cpio.h> | 
 | #include <pmap.h> | 
 | #include <smp.h> | 
 | #include <ip.h> | 
 | #include <ns.h> | 
 | #include <acpi.h> | 
 | #include <slice.h> | 
 |  | 
 | #include "../timers/hpet.h" | 
 |  | 
 | #ifdef CONFIG_X86 | 
 | #include <arch/pci.h> | 
 | #endif | 
 |  | 
 | /* ----------------------------------------------------------------------------- | 
 |  * Basic ACPI device. | 
 |  * | 
 |  * The qid.Path will be made unique by incrementing lastpath. lastpath starts | 
 |  * at Qroot. | 
 |  * | 
 |  * Qtbl will return a pointer to the Atable, which includes the signature, OEM | 
 |  * data, and so on. | 
 |  * | 
 |  * Raw, at any level, dumps the raw table at that level, which by the ACPI | 
 |  * flattened tree layout will include all descendents. | 
 |  * | 
 |  * Qpretty, at any level, will print the pretty form for that level and all | 
 |  * descendants. | 
 |  */ | 
 | enum { | 
 | 	Qroot = 0, | 
 |  | 
 | 	// The type is the qid.path mod NQtypes. | 
 | 	Qdir = 0, | 
 | 	Qpretty, | 
 | 	Qraw, | 
 | 	Qtbl, | 
 | 	NQtypes, | 
 |  | 
 | 	QIndexShift = 8, | 
 | 	QIndexMask = (1 << QIndexShift) - 1, | 
 | }; | 
 |  | 
 | #define ATABLEBUFSZ	ROUNDUP(sizeof(struct Atable), KMALLOC_ALIGNMENT) | 
 |  | 
 | static uint64_t lastpath; | 
 | static struct slice emptyslice; | 
 | static struct Atable **atableindex; | 
 | struct dev acpidevtab; | 
 |  | 
 | static char *devname(void) | 
 | { | 
 | 	return acpidevtab.name; | 
 | } | 
 |  | 
 | /* | 
 |  * ACPI 4.0 Support. | 
 |  * Still WIP. | 
 |  * | 
 |  * This driver locates tables and parses only a small subset | 
 |  * of tables. All other tables are mapped and kept for the user-level | 
 |  * interpreter. | 
 |  */ | 
 | static struct cmdtab ctls[] = { | 
 | 	{CMregion, "region", 6}, | 
 | 	{CMgpe, "gpe", 3}, | 
 | }; | 
 |  | 
 | static struct Facs *facs;		/* Firmware ACPI control structure */ | 
 | static struct Fadt *fadt;		/* Fixed ACPI description to reach ACPI regs */ | 
 | static struct Atable *root; | 
 | static struct Xsdt *xsdt;		/* XSDT table */ | 
 | static struct Atable *tfirst;	/* loaded DSDT/SSDT/... tables */ | 
 | static struct Atable *tlast;	/* pointer to last table */ | 
 | struct Atable *apics;			/* APIC info */ | 
 | struct Atable *srat;			/* System resource affinity used by physalloc */ | 
 | struct Atable *dmar; | 
 | static struct Slit *slit;		/* Sys locality info table used by scheduler */ | 
 | static struct Atable *mscttbl;		/* Maximum system characteristics table */ | 
 | static struct Reg *reg;			/* region used for I/O */ | 
 | static struct Gpe *gpes;		/* General purpose events */ | 
 | static int ngpes; | 
 |  | 
 | static char *regnames[] = { | 
 | 	"mem", "io", "pcicfg", "embed", | 
 | 	"smb", "cmos", "pcibar", "ipmi", | 
 | }; | 
 |  | 
 | /* | 
 |  * Lists to store RAM that we copy ACPI tables into. When we map a new | 
 |  * ACPI list into the kernel, we copy it into a specifically RAM buffer | 
 |  * (to make sure it's not coming from e.g. slow device memory). We store | 
 |  * pointers to those buffers on these lists. | 
 |  */ | 
 | struct Acpilist { | 
 | 	struct Acpilist *next; | 
 | 	size_t size; | 
 | 	int8_t raw[]; | 
 | }; | 
 |  | 
 | static struct Acpilist *acpilists; | 
 |  | 
 | /* | 
 |  * Produces an Atable at some level in the tree. Note that Atables are | 
 |  * isomorphic to directories in the file system namespace; this code | 
 |  * ensures that invariant. | 
 |  */ | 
 | struct Atable *mkatable(struct Atable *parent, | 
 |                         int type, char *name, uint8_t *raw, | 
 |                         size_t rawsize, size_t addsize) | 
 | { | 
 | 	void *m; | 
 | 	struct Atable *t; | 
 |  | 
 | 	m = kzmalloc(ATABLEBUFSZ + addsize, MEM_WAIT); | 
 | 	if (m == NULL) | 
 | 		panic("no memory for more aml tables"); | 
 | 	t = m; | 
 | 	t->parent = parent; | 
 | 	t->tbl = NULL; | 
 | 	if (addsize != 0) | 
 | 		t->tbl = m + ATABLEBUFSZ; | 
 | 	t->rawsize = rawsize; | 
 | 	t->raw = raw; | 
 | 	strlcpy(t->name, name, sizeof(t->name)); | 
 | 	mkqid(&t->qid,  (lastpath << QIndexShift) + Qdir, 0, QTDIR); | 
 | 	mkqid(&t->rqid, (lastpath << QIndexShift) + Qraw, 0, 0); | 
 | 	mkqid(&t->pqid, (lastpath << QIndexShift) + Qpretty, 0, 0); | 
 | 	mkqid(&t->tqid, (lastpath << QIndexShift) + Qtbl, 0, 0); | 
 | 	lastpath++; | 
 |  | 
 | 	return t; | 
 | } | 
 |  | 
 | struct Atable *finatable(struct Atable *t, struct slice *slice) | 
 | { | 
 | 	size_t n; | 
 | 	struct Atable *tail; | 
 | 	struct dirtab *dirs; | 
 |  | 
 | 	n = slice_len(slice); | 
 | 	t->nchildren = n; | 
 | 	t->children = (struct Atable **)slice_finalize(slice); | 
 | 	dirs = kreallocarray(NULL, n + NQtypes, sizeof(struct dirtab), | 
 | 	                     MEM_WAIT); | 
 | 	assert(dirs != NULL); | 
 | 	dirs[0] = (struct dirtab){ ".",      t->qid,   0, 0555 }; | 
 | 	dirs[1] = (struct dirtab){ "pretty", t->pqid,  0, 0444 }; | 
 | 	dirs[2] = (struct dirtab){ "raw",    t->rqid,  0, 0444 }; | 
 | 	dirs[3] = (struct dirtab){ "table",  t->tqid,  0, 0444 }; | 
 | 	for (size_t i = 0; i < n; i++) { | 
 | 		strlcpy(dirs[i + NQtypes].name, t->children[i]->name, KNAMELEN); | 
 | 		dirs[i + NQtypes].qid = t->children[i]->qid; | 
 | 		dirs[i + NQtypes].length = 0; | 
 | 		dirs[i + NQtypes].perm = DMDIR | 0555; | 
 | 	} | 
 | 	t->cdirs = dirs; | 
 | 	tail = NULL; | 
 | 	while (n-- > 0) { | 
 | 		t->children[n]->next = tail; | 
 | 		tail = t->children[n]; | 
 | 	} | 
 |  | 
 | 	return t; | 
 | } | 
 |  | 
 | struct Atable *finatable_nochildren(struct Atable *t) | 
 | { | 
 | 	return finatable(t, &emptyslice); | 
 | } | 
 |  | 
 | static char *dumpGas(char *start, char *end, char *prefix, struct Gas *g); | 
 | static void dumpxsdt(void); | 
 |  | 
 | static char *acpiregstr(int id) | 
 | { | 
 | 	static char buf[20];		/* BUG */ | 
 |  | 
 | 	if (id >= 0 && id < ARRAY_SIZE(regnames)) | 
 | 		return regnames[id]; | 
 | 	seprintf(buf, buf + sizeof(buf), "spc:%#x", id); | 
 | 	return buf; | 
 | } | 
 |  | 
 | static int acpiregid(char *s) | 
 | { | 
 | 	for (int i = 0; i < ARRAY_SIZE(regnames); i++) | 
 | 		if (strcmp(regnames[i], s) == 0) | 
 | 			return i; | 
 | 	return -1; | 
 | } | 
 |  | 
 | /* | 
 |  * TODO(rminnich): Fix these if we're ever on a different-endian machine. | 
 |  * They are specific to little-endian processors and are not portable. | 
 |  */ | 
 | static uint8_t mget8(uintptr_t p, void *unused) | 
 | { | 
 | 	uint8_t *cp = (uint8_t *) p; | 
 | 	return *cp; | 
 | } | 
 |  | 
 | static void mset8(uintptr_t p, uint8_t v, void *unused) | 
 | { | 
 | 	uint8_t *cp = (uint8_t *) p; | 
 | 	*cp = v; | 
 | } | 
 |  | 
 | static uint16_t mget16(uintptr_t p, void *unused) | 
 | { | 
 | 	uint16_t *cp = (uint16_t *) p; | 
 | 	return *cp; | 
 | } | 
 |  | 
 | static void mset16(uintptr_t p, uint16_t v, void *unused) | 
 | { | 
 | 	uint16_t *cp = (uint16_t *) p; | 
 | 	*cp = v; | 
 | } | 
 |  | 
 | static uint32_t mget32(uintptr_t p, void *unused) | 
 | { | 
 | 	uint32_t *cp = (uint32_t *) p; | 
 | 	return *cp; | 
 | } | 
 |  | 
 | static void mset32(uintptr_t p, uint32_t v, void *unused) | 
 | { | 
 | 	uint32_t *cp = (uint32_t *) p; | 
 | 	*cp = v; | 
 | } | 
 |  | 
 | static uint64_t mget64(uintptr_t p, void *unused) | 
 | { | 
 | 	uint64_t *cp = (uint64_t *) p; | 
 | 	return *cp; | 
 | } | 
 |  | 
 | static void mset64(uintptr_t p, uint64_t v, void *unused) | 
 | { | 
 | 	uint64_t *cp = (uint64_t *) p; | 
 | 	*cp = v; | 
 | } | 
 |  | 
 | static uint8_t ioget8(uintptr_t p, void *unused) | 
 | { | 
 | 	return inb(p); | 
 | } | 
 |  | 
 | static void ioset8(uintptr_t p, uint8_t v, void *unused) | 
 | { | 
 | 	outb(p, v); | 
 | } | 
 |  | 
 | static uint16_t ioget16(uintptr_t p, void *unused) | 
 | { | 
 | 	return inw(p); | 
 | } | 
 |  | 
 | static void ioset16(uintptr_t p, uint16_t v, void *unused) | 
 | { | 
 | 	outw(p, v); | 
 | } | 
 |  | 
 | static uint32_t ioget32(uintptr_t p, void *unused) | 
 | { | 
 | 	return inl(p); | 
 | } | 
 |  | 
 | static void ioset32(uintptr_t p, uint32_t v, void *unused) | 
 | { | 
 | 	outl(p, v); | 
 | } | 
 |  | 
 | /* | 
 |  * TODO(rminnich): these cfgs are hacky. Maybe all the struct Reg should have | 
 |  * struct pci_device or something? | 
 |  */ | 
 | static uint8_t cfgget8(uintptr_t p, void *r) | 
 | { | 
 | 	struct Reg *ro = r; | 
 | 	struct pci_device pcidev; | 
 |  | 
 | 	explode_tbdf(ro->tbdf); | 
 | 	return pcidev_read8(&pcidev, p); | 
 | } | 
 |  | 
 | static void cfgset8(uintptr_t p, uint8_t v, void *r) | 
 | { | 
 | 	struct Reg *ro = r; | 
 | 	struct pci_device pcidev; | 
 |  | 
 | 	explode_tbdf(ro->tbdf); | 
 | 	pcidev_write8(&pcidev, p, v); | 
 | } | 
 |  | 
 | static uint16_t cfgget16(uintptr_t p, void *r) | 
 | { | 
 | 	struct Reg *ro = r; | 
 | 	struct pci_device pcidev; | 
 |  | 
 | 	explode_tbdf(ro->tbdf); | 
 | 	return pcidev_read16(&pcidev, p); | 
 | } | 
 |  | 
 | static void cfgset16(uintptr_t p, uint16_t v, void *r) | 
 | { | 
 | 	struct Reg *ro = r; | 
 | 	struct pci_device pcidev; | 
 |  | 
 | 	explode_tbdf(ro->tbdf); | 
 | 	pcidev_write16(&pcidev, p, v); | 
 | } | 
 |  | 
 | static uint32_t cfgget32(uintptr_t p, void *r) | 
 | { | 
 | 	struct Reg *ro = r; | 
 | 	struct pci_device pcidev; | 
 |  | 
 | 	explode_tbdf(ro->tbdf); | 
 | 	return pcidev_read32(&pcidev, p); | 
 | } | 
 |  | 
 | static void cfgset32(uintptr_t p, uint32_t v, void *r) | 
 | { | 
 | 	struct Reg *ro = r; | 
 | 	struct pci_device pcidev; | 
 |  | 
 | 	explode_tbdf(ro->tbdf); | 
 | 	pcidev_write32(&pcidev, p, v); | 
 | } | 
 |  | 
 | static struct Regio memio = { | 
 | 	NULL, | 
 | 	mget8, mset8, mget16, mset16, | 
 | 	mget32, mset32, mget64, mset64 | 
 | }; | 
 |  | 
 | static struct Regio ioio = { | 
 | 	NULL, | 
 | 	ioget8, ioset8, ioget16, ioset16, | 
 | 	ioget32, ioset32, NULL, NULL | 
 | }; | 
 |  | 
 | static struct Regio cfgio = { | 
 | 	NULL, | 
 | 	cfgget8, cfgset8, cfgget16, cfgset16, | 
 | 	cfgget32, cfgset32, NULL, NULL | 
 | }; | 
 |  | 
 | /* | 
 |  * Copy memory, 1/2/4/8-bytes at a time, to/from a region. | 
 |  */ | 
 | static long | 
 | regcpy(struct Regio *dio, uintptr_t da, struct Regio *sio, | 
 | 	   uintptr_t sa, long len, int align) | 
 | { | 
 | 	int n, i; | 
 |  | 
 | 	printd("regcpy %#p %#p %#p %#p\n", da, sa, len, align); | 
 | 	if ((len % align) != 0) | 
 | 		printd("regcpy: bug: copy not aligned. truncated\n"); | 
 | 	n = len / align; | 
 | 	for (i = 0; i < n; i++) { | 
 | 		switch (align) { | 
 | 			case 1: | 
 | 				printd("cpy8 %#p %#p\n", da, sa); | 
 | 				dio->set8(da, sio->get8(sa, sio->arg), dio->arg); | 
 | 				break; | 
 | 			case 2: | 
 | 				printd("cpy16 %#p %#p\n", da, sa); | 
 | 				dio->set16(da, sio->get16(sa, sio->arg), dio->arg); | 
 | 				break; | 
 | 			case 4: | 
 | 				printd("cpy32 %#p %#p\n", da, sa); | 
 | 				dio->set32(da, sio->get32(sa, sio->arg), dio->arg); | 
 | 				break; | 
 | 			case 8: | 
 | 				printd("cpy64 %#p %#p\n", da, sa); | 
 | 				warn("Not doing set64 for some reason, fix me!"); | 
 | 				//  dio->set64(da, sio->get64(sa, sio->arg), dio->arg); | 
 | 				break; | 
 | 			default: | 
 | 				panic("regcpy: align bug"); | 
 | 		} | 
 | 		da += align; | 
 | 		sa += align; | 
 | 	} | 
 | 	return n * align; | 
 | } | 
 |  | 
 | /* | 
 |  * Perform I/O within region in access units of accsz bytes. | 
 |  * All units in bytes. | 
 |  */ | 
 | static long regio(struct Reg *r, void *p, uint32_t len, uintptr_t off, int iswr) | 
 | { | 
 | 	struct Regio rio; | 
 | 	uintptr_t rp; | 
 |  | 
 | 	printd("reg%s %s %#p %#p %#lx sz=%d\n", | 
 | 		   iswr ? "out" : "in", r->name, p, off, len, r->accsz); | 
 | 	rp = 0; | 
 | 	if (off + len > r->len) { | 
 | 		printd("regio: access outside limits"); | 
 | 		len = r->len - off; | 
 | 	} | 
 | 	if (len <= 0) { | 
 | 		printd("regio: zero len\n"); | 
 | 		return 0; | 
 | 	} | 
 | 	switch (r->spc) { | 
 | 		case Rsysmem: | 
 | 			if (r->p == NULL) | 
 | 				r->p = KADDR_NOCHECK(r->base); | 
 | 			if (r->p == NULL) | 
 | 				error(EFAIL, "regio: vmap/KADDR failed"); | 
 | 			rp = (uintptr_t) r->p + off; | 
 | 			rio = memio; | 
 | 			break; | 
 | 		case Rsysio: | 
 | 			rp = r->base + off; | 
 | 			rio = ioio; | 
 | 			break; | 
 | 		case Rpcicfg: | 
 | 			rp = r->base + off; | 
 | 			rio = cfgio; | 
 | 			rio.arg = r; | 
 | 			break; | 
 | 		case Rpcibar: | 
 | 		case Rembed: | 
 | 		case Rsmbus: | 
 | 		case Rcmos: | 
 | 		case Ripmi: | 
 | 		case Rfixedhw: | 
 | 			printd("regio: reg %s not supported\n", acpiregstr(r->spc)); | 
 | 			error(EFAIL, "region not supported"); | 
 | 	} | 
 | 	if (iswr) | 
 | 		regcpy(&rio, rp, &memio, (uintptr_t) p, len, r->accsz); | 
 | 	else | 
 | 		regcpy(&memio, (uintptr_t) p, &rio, rp, len, r->accsz); | 
 | 	return len; | 
 | } | 
 |  | 
 | /* | 
 |  * Compute and return SDT checksum: '0' is a correct sum. | 
 |  */ | 
 | static uint8_t sdtchecksum(void *addr, int len) | 
 | { | 
 | 	uint8_t *p, sum; | 
 |  | 
 | 	sum = 0; | 
 | 	for (p = addr; len-- > 0; p++) | 
 | 		sum += *p; | 
 |  | 
 | 	return sum; | 
 | } | 
 |  | 
 | static void *sdtmap(uintptr_t pa, size_t *n, int cksum) | 
 | { | 
 | 	struct Sdthdr *sdt; | 
 | 	struct Acpilist *p; | 
 |  | 
 | 	if (!pa) { | 
 | 		printk("sdtmap: NULL pa\n"); | 
 | 		return NULL; | 
 | 	} | 
 | 	sdt = KADDR_NOCHECK(pa); | 
 | 	if (sdt == NULL) { | 
 | 		printk("acpi: vmap: NULL\n"); | 
 | 		return NULL; | 
 | 	} | 
 | 	*n = l32get(sdt->length); | 
 | 	if (!*n) { | 
 | 		printk("sdt has zero length: pa = %p, sig = %.4s\n", pa, sdt->sig); | 
 | 		return NULL; | 
 | 	} | 
 | 	if (cksum != 0 && sdtchecksum(sdt, *n) != 0) { | 
 | 		printk("acpi: SDT: bad checksum. pa = %p, len = %lu\n", pa, *n); | 
 | 		return NULL; | 
 | 	} | 
 | 	p = kzmalloc(sizeof(struct Acpilist) + *n, MEM_WAIT); | 
 | 	if (p == NULL) | 
 | 		panic("sdtmap: memory allocation failed for %lu bytes", *n); | 
 | 	memmove(p->raw, (void *)sdt, *n); | 
 | 	p->size = *n; | 
 | 	p->next = acpilists; | 
 | 	acpilists = p; | 
 |  | 
 | 	return p->raw; | 
 | } | 
 |  | 
 | static int loadfacs(uintptr_t pa) | 
 | { | 
 | 	size_t n; | 
 |  | 
 | 	facs = sdtmap(pa, &n, 0); | 
 | 	if (facs == NULL) | 
 | 		return -1; | 
 | 	if (memcmp(facs->sig, "FACS", 4) != 0) { | 
 | 		facs = NULL; | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	/* no unmap */ | 
 | 	printd("acpi: facs: hwsig: %#p\n", facs->hwsig); | 
 | 	printd("acpi: facs: wakingv: %#p\n", facs->wakingv); | 
 | 	printd("acpi: facs: flags: %#p\n", facs->flags); | 
 | 	printd("acpi: facs: glock: %#p\n", facs->glock); | 
 | 	printd("acpi: facs: xwakingv: %#p\n", facs->xwakingv); | 
 | 	printd("acpi: facs: vers: %#p\n", facs->vers); | 
 | 	printd("acpi: facs: ospmflags: %#p\n", facs->ospmflags); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void loaddsdt(uintptr_t pa) | 
 | { | 
 | 	size_t n; | 
 | 	uint8_t *dsdtp; | 
 |  | 
 | 	dsdtp = sdtmap(pa, &n, 1); | 
 | 	if (dsdtp == NULL) { | 
 | 		printk("acpi: Failed to map dsdtp.\n"); | 
 | 		return; | 
 | 	} | 
 | } | 
 |  | 
 | static void gasget(struct Gas *gas, uint8_t *p) | 
 | { | 
 | 	gas->spc = p[0]; | 
 | 	gas->len = p[1]; | 
 | 	gas->off = p[2]; | 
 | 	gas->accsz = p[3]; | 
 | 	gas->addr = l64get(p + 4); | 
 | } | 
 |  | 
 | static char *dumpfadt(char *start, char *end, struct Fadt *fp) | 
 | { | 
 | 	if (fp == NULL) | 
 | 		return start; | 
 |  | 
 | 	start = seprintf(start, end, "acpi: FADT@%p\n", fp); | 
 | 	start = seprintf(start, end, "acpi: fadt: facs: $%p\n", fp->facs); | 
 | 	start = seprintf(start, end, "acpi: fadt: dsdt: $%p\n", fp->dsdt); | 
 | 	start = seprintf(start, end, "acpi: fadt: pmprofile: $%p\n", fp->pmprofile); | 
 | 	start = seprintf(start, end, "acpi: fadt: sciint: $%p\n", fp->sciint); | 
 | 	start = seprintf(start, end, "acpi: fadt: smicmd: $%p\n", fp->smicmd); | 
 | 	start = | 
 | 		seprintf(start, end, "acpi: fadt: acpienable: $%p\n", fp->acpienable); | 
 | 	start = | 
 | 		seprintf(start, end, "acpi: fadt: acpidisable: $%p\n", fp->acpidisable); | 
 | 	start = seprintf(start, end, "acpi: fadt: s4biosreq: $%p\n", fp->s4biosreq); | 
 | 	start = seprintf(start, end, "acpi: fadt: pstatecnt: $%p\n", fp->pstatecnt); | 
 | 	start = | 
 | 		seprintf(start, end, "acpi: fadt: pm1aevtblk: $%p\n", fp->pm1aevtblk); | 
 | 	start = | 
 | 		seprintf(start, end, "acpi: fadt: pm1bevtblk: $%p\n", fp->pm1bevtblk); | 
 | 	start = | 
 | 		seprintf(start, end, "acpi: fadt: pm1acntblk: $%p\n", fp->pm1acntblk); | 
 | 	start = | 
 | 		seprintf(start, end, "acpi: fadt: pm1bcntblk: $%p\n", fp->pm1bcntblk); | 
 | 	start = seprintf(start, end, "acpi: fadt: pm2cntblk: $%p\n", fp->pm2cntblk); | 
 | 	start = seprintf(start, end, "acpi: fadt: pmtmrblk: $%p\n", fp->pmtmrblk); | 
 | 	start = seprintf(start, end, "acpi: fadt: gpe0blk: $%p\n", fp->gpe0blk); | 
 | 	start = seprintf(start, end, "acpi: fadt: gpe1blk: $%p\n", fp->gpe1blk); | 
 | 	start = seprintf(start, end, "acpi: fadt: pm1evtlen: $%p\n", fp->pm1evtlen); | 
 | 	start = seprintf(start, end, "acpi: fadt: pm1cntlen: $%p\n", fp->pm1cntlen); | 
 | 	start = seprintf(start, end, "acpi: fadt: pm2cntlen: $%p\n", fp->pm2cntlen); | 
 | 	start = seprintf(start, end, "acpi: fadt: pmtmrlen: $%p\n", fp->pmtmrlen); | 
 | 	start = | 
 | 		seprintf(start, end, "acpi: fadt: gpe0blklen: $%p\n", fp->gpe0blklen); | 
 | 	start = | 
 | 		seprintf(start, end, "acpi: fadt: gpe1blklen: $%p\n", fp->gpe1blklen); | 
 | 	start = seprintf(start, end, "acpi: fadt: gp1base: $%p\n", fp->gp1base); | 
 | 	start = seprintf(start, end, "acpi: fadt: cstcnt: $%p\n", fp->cstcnt); | 
 | 	start = seprintf(start, end, "acpi: fadt: plvl2lat: $%p\n", fp->plvl2lat); | 
 | 	start = seprintf(start, end, "acpi: fadt: plvl3lat: $%p\n", fp->plvl3lat); | 
 | 	start = seprintf(start, end, "acpi: fadt: flushsz: $%p\n", fp->flushsz); | 
 | 	start = | 
 | 		seprintf(start, end, "acpi: fadt: flushstride: $%p\n", fp->flushstride); | 
 | 	start = seprintf(start, end, "acpi: fadt: dutyoff: $%p\n", fp->dutyoff); | 
 | 	start = seprintf(start, end, "acpi: fadt: dutywidth: $%p\n", fp->dutywidth); | 
 | 	start = seprintf(start, end, "acpi: fadt: dayalrm: $%p\n", fp->dayalrm); | 
 | 	start = seprintf(start, end, "acpi: fadt: monalrm: $%p\n", fp->monalrm); | 
 | 	start = seprintf(start, end, "acpi: fadt: century: $%p\n", fp->century); | 
 | 	start = | 
 | 		seprintf(start, end, "acpi: fadt: iapcbootarch: $%p\n", | 
 | 				 fp->iapcbootarch); | 
 | 	start = seprintf(start, end, "acpi: fadt: flags: $%p\n", fp->flags); | 
 | 	start = dumpGas(start, end, "acpi: fadt: resetreg: ", &fp->resetreg); | 
 | 	start = seprintf(start, end, "acpi: fadt: resetval: $%p\n", fp->resetval); | 
 | 	start = seprintf(start, end, "acpi: fadt: xfacs: %p\n", fp->xfacs); | 
 | 	start = seprintf(start, end, "acpi: fadt: xdsdt: %p\n", fp->xdsdt); | 
 | 	start = dumpGas(start, end, "acpi: fadt: xpm1aevtblk:", &fp->xpm1aevtblk); | 
 | 	start = dumpGas(start, end, "acpi: fadt: xpm1bevtblk:", &fp->xpm1bevtblk); | 
 | 	start = dumpGas(start, end, "acpi: fadt: xpm1acntblk:", &fp->xpm1acntblk); | 
 | 	start = dumpGas(start, end, "acpi: fadt: xpm1bcntblk:", &fp->xpm1bcntblk); | 
 | 	start = dumpGas(start, end, "acpi: fadt: xpm2cntblk:", &fp->xpm2cntblk); | 
 | 	start = dumpGas(start, end, "acpi: fadt: xpmtmrblk:", &fp->xpmtmrblk); | 
 | 	start = dumpGas(start, end, "acpi: fadt: xgpe0blk:", &fp->xgpe0blk); | 
 | 	start = dumpGas(start, end, "acpi: fadt: xgpe1blk:", &fp->xgpe1blk); | 
 | 	return start; | 
 | } | 
 |  | 
 | static struct Atable *parsefadt(struct Atable *parent, | 
 | 								char *name, uint8_t *p, size_t rawsize) | 
 | { | 
 | 	struct Atable *t; | 
 | 	struct Fadt *fp; | 
 |  | 
 | 	t = mkatable(parent, FADT, name, p, rawsize, sizeof(struct Fadt)); | 
 |  | 
 | 	if (rawsize < 116) { | 
 | 		printk("ACPI: unusually short FADT, aborting!\n"); | 
 | 		return t; | 
 | 	} | 
 | 	/* for now, keep the globals. We'll get rid of them later. */ | 
 | 	fp = t->tbl; | 
 | 	fadt = fp; | 
 | 	fp->facs = l32get(p + 36); | 
 | 	fp->dsdt = l32get(p + 40); | 
 | 	fp->pmprofile = p[45]; | 
 | 	fp->sciint = l16get(p + 46); | 
 | 	fp->smicmd = l32get(p + 48); | 
 | 	fp->acpienable = p[52]; | 
 | 	fp->acpidisable = p[53]; | 
 | 	fp->s4biosreq = p[54]; | 
 | 	fp->pstatecnt = p[55]; | 
 | 	fp->pm1aevtblk = l32get(p + 56); | 
 | 	fp->pm1bevtblk = l32get(p + 60); | 
 | 	fp->pm1acntblk = l32get(p + 64); | 
 | 	fp->pm1bcntblk = l32get(p + 68); | 
 | 	fp->pm2cntblk = l32get(p + 72); | 
 | 	fp->pmtmrblk = l32get(p + 76); | 
 | 	fp->gpe0blk = l32get(p + 80); | 
 | 	fp->gpe1blk = l32get(p + 84); | 
 | 	fp->pm1evtlen = p[88]; | 
 | 	fp->pm1cntlen = p[89]; | 
 | 	fp->pm2cntlen = p[90]; | 
 | 	fp->pmtmrlen = p[91]; | 
 | 	fp->gpe0blklen = p[92]; | 
 | 	fp->gpe1blklen = p[93]; | 
 | 	fp->gp1base = p[94]; | 
 | 	fp->cstcnt = p[95]; | 
 | 	fp->plvl2lat = l16get(p + 96); | 
 | 	fp->plvl3lat = l16get(p + 98); | 
 | 	fp->flushsz = l16get(p + 100); | 
 | 	fp->flushstride = l16get(p + 102); | 
 | 	fp->dutyoff = p[104]; | 
 | 	fp->dutywidth = p[105]; | 
 | 	fp->dayalrm = p[106]; | 
 | 	fp->monalrm = p[107]; | 
 | 	fp->century = p[108]; | 
 | 	fp->iapcbootarch = l16get(p + 109); | 
 | 	fp->flags = l32get(p + 112); | 
 |  | 
 | 	/* | 
 | 	 * qemu gives us a 116 byte fadt, though i haven't seen any HW do that. | 
 | 	 * The right way to do this is to realloc the table and fake it out. | 
 | 	 */ | 
 | 	if (rawsize < 244) | 
 | 		return finatable_nochildren(t); | 
 |  | 
 | 	gasget(&fp->resetreg, p + 116); | 
 | 	fp->resetval = p[128]; | 
 | 	fp->xfacs = l64get(p + 132); | 
 | 	fp->xdsdt = l64get(p + 140); | 
 | 	gasget(&fp->xpm1aevtblk, p + 148); | 
 | 	gasget(&fp->xpm1bevtblk, p + 160); | 
 | 	gasget(&fp->xpm1acntblk, p + 172); | 
 | 	gasget(&fp->xpm1bcntblk, p + 184); | 
 | 	gasget(&fp->xpm2cntblk, p + 196); | 
 | 	gasget(&fp->xpmtmrblk, p + 208); | 
 | 	gasget(&fp->xgpe0blk, p + 220); | 
 | 	gasget(&fp->xgpe1blk, p + 232); | 
 |  | 
 | 	if (fp->xfacs != 0) | 
 | 		loadfacs(fp->xfacs); | 
 | 	else | 
 | 		loadfacs(fp->facs); | 
 |  | 
 | 	if (fp->xdsdt == (uint64_t)fp->dsdt)	/* acpica */ | 
 | 		loaddsdt(fp->xdsdt); | 
 | 	else | 
 | 		loaddsdt(fp->dsdt); | 
 |  | 
 | 	return finatable_nochildren(t); | 
 | } | 
 |  | 
 | static char *dumpmsct(char *start, char *end, struct Atable *table) | 
 | { | 
 | 	struct Msct *msct; | 
 |  | 
 | 	if (!table) | 
 | 		return start; | 
 |  | 
 | 	msct = table->tbl; | 
 | 	if (!msct) | 
 | 		return start; | 
 |  | 
 | 	start = seprintf(start, end, "acpi: msct: %d doms %d clkdoms %#p maxpa\n", | 
 | 					 msct->ndoms, msct->nclkdoms, msct->maxpa); | 
 | 	for (int i = 0; i < table->nchildren; i++) { | 
 | 		struct Atable *domtbl = table->children[i]->tbl; | 
 | 		struct Mdom *st = domtbl->tbl; | 
 |  | 
 | 		start = seprintf(start, end, "\t[%d:%d] %d maxproc %#p maxmmem\n", | 
 | 						 st->start, st->end, st->maxproc, st->maxmem); | 
 | 	} | 
 | 	start = seprintf(start, end, "\n"); | 
 |  | 
 | 	return start; | 
 | } | 
 |  | 
 | /* | 
 |  * XXX: should perhaps update our idea of available memory. | 
 |  * Else we should remove this code. | 
 |  */ | 
 | static struct Atable *parsemsct(struct Atable *parent, | 
 |                                 char *name, uint8_t *raw, size_t rawsize) | 
 | { | 
 | 	struct Atable *t; | 
 | 	uint8_t *r, *re; | 
 | 	struct Msct *msct; | 
 | 	struct Mdom **stl, *st; | 
 | 	size_t off, nmdom; | 
 | 	int i; | 
 |  | 
 | 	re = raw + rawsize; | 
 | 	off = l32get(raw + 36); | 
 | 	nmdom = 0; | 
 | 	for (r = raw + off, re = raw + rawsize; r < re; r += 22) | 
 | 		nmdom++; | 
 | 	t = mkatable(parent, MSCT, name, raw, rawsize, | 
 | 	             sizeof(struct Msct) + nmdom * sizeof(struct Mdom)); | 
 | 	msct = t->tbl; | 
 | 	msct->ndoms = l32get(raw + 40) + 1; | 
 | 	msct->nclkdoms = l32get(raw + 44) + 1; | 
 | 	msct->maxpa = l64get(raw + 48); | 
 | 	msct->nmdom = nmdom; | 
 | 	msct->dom = NULL; | 
 | 	if (nmdom != 0) | 
 | 		msct->dom = (void *)msct + sizeof(struct Msct); | 
 | 	for (i = 0, r = raw; i < nmdom; i++, r += 22) { | 
 | 		msct->dom[i].start = l32get(r + 2); | 
 | 		msct->dom[i].end = l32get(r + 6); | 
 | 		msct->dom[i].maxproc = l32get(r + 10); | 
 | 		msct->dom[i].maxmem = l64get(r + 14); | 
 | 	} | 
 | 	mscttbl = finatable_nochildren(t); | 
 |  | 
 | 	return mscttbl; | 
 | } | 
 |  | 
 | /* TODO(rminnich): only handles on IOMMU for now. */ | 
 | static char *dumpdmar(char *start, char *end, struct Atable *dmar) | 
 | { | 
 | 	struct Dmar *dt; | 
 |  | 
 | 	if (dmar == NULL) | 
 | 		return start; | 
 |  | 
 | 	dt = dmar->tbl; | 
 | 	start = seprintf(start, end, "acpi: DMAR addr %p:\n", dt); | 
 | 	start = seprintf(start, end, "\tdmar: intr_remap %d haw %d\n", | 
 | 	                 dt->intr_remap, dt->haw); | 
 | 	for (int i = 0; i < dmar->nchildren; i++) { | 
 | 		struct Atable *at = dmar->children[i]; | 
 | 		struct Drhd *drhd = at->tbl; | 
 |  | 
 | 		start = seprintf(start, end, "\tDRHD: "); | 
 | 		start = seprintf(start, end, "%s 0x%02x 0x%016x\n", | 
 | 		                 drhd->all & 1 ? "INCLUDE_PCI_ALL" : "Scoped", | 
 | 		                 drhd->segment, drhd->rba); | 
 | 	} | 
 |  | 
 | 	return start; | 
 | } | 
 |  | 
 | static char *dumpsrat(char *start, char *end, struct Atable *table) | 
 | { | 
 | 	if (table == NULL) | 
 | 		return seprintf(start, end, "NO SRAT\n"); | 
 | 	start = seprintf(start, end, "acpi: SRAT@%p:\n", table->tbl); | 
 | 	for (; table != NULL; table = table->next) { | 
 | 		struct Srat *st = table->tbl; | 
 |  | 
 | 		if (st == NULL) | 
 | 			continue; | 
 | 		switch (st->type) { | 
 | 			case SRlapic: | 
 | 				start = | 
 | 					seprintf(start, end, | 
 | 							 "\tlapic: dom %d apic %d sapic %d clk %d\n", | 
 | 							 st->lapic.dom, st->lapic.apic, st->lapic.sapic, | 
 | 							 st->lapic.clkdom); | 
 | 				break; | 
 | 			case SRmem: | 
 | 				start = seprintf(start, end, "\tmem: dom %d %#p %#p %c%c\n", | 
 | 								 st->mem.dom, st->mem.addr, st->mem.len, | 
 | 								 st->mem.hplug ? 'h' : '-', | 
 | 								 st->mem.nvram ? 'n' : '-'); | 
 | 				break; | 
 | 			case SRlx2apic: | 
 | 				start = | 
 | 					seprintf(start, end, "\tlx2apic: dom %d apic %d clk %d\n", | 
 | 							 st->lx2apic.dom, st->lx2apic.apic, | 
 | 							 st->lx2apic.clkdom); | 
 | 				break; | 
 | 			default: | 
 | 				start = seprintf(start, end, "\t<unknown srat entry>\n"); | 
 | 		} | 
 | 	} | 
 | 	start = seprintf(start, end, "\n"); | 
 | 	return start; | 
 | } | 
 |  | 
 | static struct Atable *parsesrat(struct Atable *parent, | 
 |                                 char *name, uint8_t *p, size_t rawsize) | 
 | { | 
 |  | 
 | 	struct Atable *t, *tt, *tail; | 
 | 	uint8_t *pe; | 
 | 	int stlen, flags; | 
 | 	struct slice slice; | 
 | 	char buf[16]; | 
 | 	int i; | 
 | 	struct Srat *st; | 
 |  | 
 | 	/* TODO: Parse the second SRAT */ | 
 | 	if (srat != NULL) { | 
 | 		warn("Multiple SRATs detected and ignored!"); | 
 | 		return NULL; | 
 | 	} | 
 |  | 
 | 	t = mkatable(parent, SRAT, name, p, rawsize, 0); | 
 | 	slice_init(&slice); | 
 | 	pe = p + rawsize; | 
 | 	for (p += 48, i = 0; p < pe; p += stlen, i++) { | 
 | 		snprintf(buf, sizeof(buf), "%d", i); | 
 | 		stlen = p[1]; | 
 | 		tt = mkatable(t, SRAT, buf, p, stlen, sizeof(struct Srat)); | 
 | 		st = tt->tbl; | 
 | 		st->type = p[0]; | 
 | 		switch (st->type) { | 
 | 			case SRlapic: | 
 | 				st->lapic.dom = p[2] | p[9] << 24 | p[10] << 16 | p[11] << 8; | 
 | 				st->lapic.apic = p[3]; | 
 | 				st->lapic.sapic = p[8]; | 
 | 				st->lapic.clkdom = l32get(p + 12); | 
 | 				if (l32get(p + 4) == 0) { | 
 | 					kfree(tt); | 
 | 					tt = NULL; | 
 | 				} | 
 | 				break; | 
 | 			case SRmem: | 
 | 				st->mem.dom = l32get(p + 2); | 
 | 				st->mem.addr = l64get(p + 8); | 
 | 				st->mem.len = l64get(p + 16); | 
 | 				flags = l32get(p + 28); | 
 | 				if ((flags & 1) == 0) {	/* not enabled */ | 
 | 					kfree(tt); | 
 | 					tt = NULL; | 
 | 				} else { | 
 | 					st->mem.hplug = flags & 2; | 
 | 					st->mem.nvram = flags & 4; | 
 | 				} | 
 | 				break; | 
 | 			case SRlx2apic: | 
 | 				st->lx2apic.dom = l32get(p + 4); | 
 | 				st->lx2apic.apic = l32get(p + 8); | 
 | 				st->lx2apic.clkdom = l32get(p + 16); | 
 | 				if (l32get(p + 12) == 0) { | 
 | 					kfree(tt); | 
 | 					tt = NULL; | 
 | 				} | 
 | 				break; | 
 | 			default: | 
 | 				printd("unknown SRAT structure\n"); | 
 | 				kfree(tt); | 
 | 				tt = NULL; | 
 | 				break; | 
 | 		} | 
 | 		if (tt != NULL) { | 
 | 			finatable_nochildren(tt); | 
 | 			slice_append(&slice, tt); | 
 | 		} | 
 | 	} | 
 | 	srat = finatable(t, &slice); | 
 |  | 
 | 	return srat; | 
 | } | 
 |  | 
 | static char *dumpslit(char *start, char *end, struct Slit *sl) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	if (sl == NULL) | 
 | 		return start; | 
 | 	start = seprintf(start, end, "acpi slit:\n"); | 
 | 	for (i = 0; i < sl->rowlen * sl->rowlen; i++) { | 
 | 		start = seprintf(start, end, | 
 | 						 "slit: %ux\n", | 
 | 						 sl->e[i / sl->rowlen][i % sl->rowlen].dist); | 
 | 	} | 
 | 	start = seprintf(start, end, "\n"); | 
 | 	return start; | 
 | } | 
 |  | 
 | static int cmpslitent(void *v1, void *v2) | 
 | { | 
 | 	struct SlEntry *se1, *se2; | 
 |  | 
 | 	se1 = v1; | 
 | 	se2 = v2; | 
 | 	return se1->dist - se2->dist; | 
 | } | 
 |  | 
 | static struct Atable *parseslit(struct Atable *parent, | 
 |                                 char *name, uint8_t *raw, size_t rawsize) | 
 | { | 
 | 	struct Atable *t; | 
 | 	uint8_t *r, *re; | 
 | 	int i, j, k; | 
 | 	struct SlEntry *se; | 
 | 	size_t addsize, rowlen; | 
 | 	void *p; | 
 |  | 
 | 	addsize = sizeof(*slit); | 
 | 	rowlen = l64get(raw + 36); | 
 | 	addsize += rowlen * sizeof(struct SlEntry *); | 
 | 	addsize += sizeof(struct SlEntry) * rowlen * rowlen; | 
 |  | 
 | 	t = mkatable(parent, SLIT, name, raw, rawsize, addsize); | 
 | 	slit = t->tbl; | 
 | 	slit->rowlen = rowlen; | 
 | 	p = (void *)slit + sizeof(*slit); | 
 | 	slit->e = p; | 
 | 	p += rowlen * sizeof(struct SlEntry *); | 
 | 	for (i = 0; i < rowlen; i++) { | 
 | 		slit->e[i] = p; | 
 | 		p += sizeof(struct SlEntry) * rowlen; | 
 | 	} | 
 | 	for (i = 0, r = raw + 44, re = raw + rawsize; r < re; r++, i++) { | 
 | 		int j = i / rowlen; | 
 | 		int k = i % rowlen; | 
 |  | 
 | 		se = &slit->e[j][k]; | 
 | 		se->dom = k; | 
 | 		se->dist = *r; | 
 | 	} | 
 |  | 
 | #if 0 | 
 | 	/* TODO: might need to sort this shit */ | 
 | 	for (i = 0; i < slit->rowlen; i++) | 
 | 		qsort(slit->e[i], slit->rowlen, sizeof(slit->e[0][0]), cmpslitent); | 
 | #endif | 
 |  | 
 | 	return finatable_nochildren(t); | 
 | } | 
 |  | 
 | int pickcore(int mycolor, int index) | 
 | { | 
 | 	int color; | 
 | 	int ncorepercol; | 
 |  | 
 | 	if (slit == NULL) | 
 | 		return 0; | 
 | 	ncorepercol = num_cores / slit->rowlen; | 
 | 	color = slit->e[mycolor][index / ncorepercol].dom; | 
 | 	return color * ncorepercol + index % ncorepercol; | 
 | } | 
 |  | 
 | static char *polarity[4] = { | 
 | 	"polarity/trigger like in ISA", | 
 | 	"active high", | 
 | 	"BOGUS POLARITY", | 
 | 	"active low" | 
 | }; | 
 |  | 
 | static char *trigger[] = { | 
 | 	"BOGUS TRIGGER", | 
 | 	"edge", | 
 | 	"BOGUS TRIGGER", | 
 | 	"level" | 
 | }; | 
 |  | 
 | static char *printiflags(char *start, char *end, int flags) | 
 | { | 
 |  | 
 | 	return seprintf(start, end, "[%s,%s]", | 
 | 					polarity[flags & AFpmask], trigger[(flags & AFtmask) >> 2]); | 
 | } | 
 |  | 
 | static char *dumpmadt(char *start, char *end, struct Atable *apics) | 
 | { | 
 | 	struct Madt *mt; | 
 |  | 
 | 	if (apics == NULL) | 
 | 		return start; | 
 |  | 
 | 	mt = apics->tbl; | 
 | 	if (mt == NULL) | 
 | 		return seprintf(start, end, "acpi: no MADT"); | 
 | 	start = seprintf(start, end, "acpi: MADT@%p: lapic paddr %p pcat %d:\n", | 
 | 	                 mt, mt->lapicpa, mt->pcat); | 
 | 	for (int i = 0; i < apics->nchildren; i++) { | 
 | 		struct Atable *apic = apics->children[i]; | 
 | 		struct Apicst *st = apic->tbl; | 
 |  | 
 | 		switch (st->type) { | 
 | 			case ASlapic: | 
 | 				start = | 
 | 					seprintf(start, end, "\tlapic pid %d id %d\n", | 
 | 							 st->lapic.pid, st->lapic.id); | 
 | 				break; | 
 | 			case ASioapic: | 
 | 			case ASiosapic: | 
 | 				start = | 
 | 					seprintf(start, end, | 
 | 							 "\tioapic id %d addr %p ibase %d\n", | 
 | 							 st->ioapic.id, st->ioapic.addr, st->ioapic.ibase); | 
 | 				break; | 
 | 			case ASintovr: | 
 | 				start = | 
 | 					seprintf(start, end, "\tintovr irq %d intr %d flags $%p", | 
 | 							 st->intovr.irq, st->intovr.intr, st->intovr.flags); | 
 | 				start = printiflags(start, end, st->intovr.flags); | 
 | 				start = seprintf(start, end, "\n"); | 
 | 				break; | 
 | 			case ASnmi: | 
 | 				start = seprintf(start, end, "\tnmi intr %d flags $%p\n", | 
 | 								 st->nmi.intr, st->nmi.flags); | 
 | 				break; | 
 | 			case ASlnmi: | 
 | 				start = | 
 | 					seprintf(start, end, "\tlnmi pid %d lint %d flags $%p\n", | 
 | 							 st->lnmi.pid, st->lnmi.lint, st->lnmi.flags); | 
 | 				break; | 
 | 			case ASlsapic: | 
 | 				start = | 
 | 					seprintf(start, end, | 
 | 							 "\tlsapic pid %d id %d eid %d puid %d puids %s\n", | 
 | 							 st->lsapic.pid, st->lsapic.id, st->lsapic.eid, | 
 | 							 st->lsapic.puid, st->lsapic.puids); | 
 | 				break; | 
 | 			case ASintsrc: | 
 | 				start = | 
 | 					seprintf(start, end, | 
 | 							 "\tintr type %d pid %d peid %d iosv %d intr %d %#x\n", | 
 | 							 st->type, st->intsrc.pid, st->intsrc.peid, | 
 | 							 st->intsrc.iosv, st->intsrc.intr, | 
 | 							 st->intsrc.flags); | 
 | 				start = printiflags(start, end, st->intsrc.flags); | 
 | 				start = seprintf(start, end, "\n"); | 
 | 				break; | 
 | 			case ASlx2apic: | 
 | 				start = | 
 | 					seprintf(start, end, "\tlx2apic puid %d id %d\n", | 
 | 							 st->lx2apic.puid, st->lx2apic.id); | 
 | 				break; | 
 | 			case ASlx2nmi: | 
 | 				start = | 
 | 					seprintf(start, end, "\tlx2nmi puid %d intr %d flags $%p\n", | 
 | 							 st->lx2nmi.puid, st->lx2nmi.intr, | 
 | 							 st->lx2nmi.flags); | 
 | 				break; | 
 | 			default: | 
 | 				start = seprintf(start, end, "\t<unknown madt entry>\n"); | 
 | 		} | 
 | 	} | 
 | 	start = seprintf(start, end, "\n"); | 
 | 	return start; | 
 | } | 
 |  | 
 | static struct Atable *parsemadt(struct Atable *parent, | 
 |                                 char *name, uint8_t *p, size_t size) | 
 | { | 
 | 	struct Atable *t, *tt, *tail; | 
 | 	uint8_t *pe; | 
 | 	struct Madt *mt; | 
 | 	struct Apicst *st, *l; | 
 | 	int id; | 
 | 	size_t stlen; | 
 | 	char buf[16]; | 
 | 	int i; | 
 | 	struct slice slice; | 
 |  | 
 | 	slice_init(&slice); | 
 | 	t = mkatable(parent, MADT, name, p, size, sizeof(struct Madt)); | 
 | 	mt = t->tbl; | 
 | 	mt->lapicpa = l32get(p + 36); | 
 | 	mt->pcat = l32get(p + 40); | 
 | 	pe = p + size; | 
 | 	for (p += 44, i = 0; p < pe; p += stlen, i++) { | 
 | 		snprintf(buf, sizeof(buf), "%d", i); | 
 | 		stlen = p[1]; | 
 | 		tt = mkatable(t, APIC, buf, p, stlen, sizeof(struct Apicst)); | 
 | 		st = tt->tbl; | 
 | 		st->type = p[0]; | 
 | 		switch (st->type) { | 
 | 			case ASlapic: | 
 | 				st->lapic.pid = p[2]; | 
 | 				st->lapic.id = p[3]; | 
 | 				if (l32get(p + 4) == 0) { | 
 | 					kfree(tt); | 
 | 					tt = NULL; | 
 | 				} | 
 | 				break; | 
 | 			case ASioapic: | 
 | 				st->ioapic.id = id = p[2]; | 
 | 				st->ioapic.addr = l32get(p + 4); | 
 | 				st->ioapic.ibase = l32get(p + 8); | 
 | 				/* ioapic overrides any ioapic entry for the same id */ | 
 | 				for (int i = 0; i < slice_len(&slice); i++) { | 
 | 					l = ((struct Atable *)slice_get(&slice, i))->tbl; | 
 | 					if (l->type == ASiosapic && l->iosapic.id == id) { | 
 | 						st->ioapic = l->iosapic; | 
 | 						/* we leave it linked; could be removed */ | 
 | 						break; | 
 | 					} | 
 | 				} | 
 | 				break; | 
 | 			case ASintovr: | 
 | 				st->intovr.irq = p[3]; | 
 | 				st->intovr.intr = l32get(p + 4); | 
 | 				st->intovr.flags = l16get(p + 8); | 
 | 				break; | 
 | 			case ASnmi: | 
 | 				st->nmi.flags = l16get(p + 2); | 
 | 				st->nmi.intr = l32get(p + 4); | 
 | 				break; | 
 | 			case ASlnmi: | 
 | 				st->lnmi.pid = p[2]; | 
 | 				st->lnmi.flags = l16get(p + 3); | 
 | 				st->lnmi.lint = p[5]; | 
 | 				break; | 
 | 			case ASladdr: | 
 | 				/* This is for 64 bits, perhaps we should not | 
 | 				 * honor it on 32 bits. | 
 | 				 */ | 
 | 				mt->lapicpa = l64get(p + 8); | 
 | 				break; | 
 | 			case ASiosapic: | 
 | 				id = st->iosapic.id = p[2]; | 
 | 				st->iosapic.ibase = l32get(p + 4); | 
 | 				st->iosapic.addr = l64get(p + 8); | 
 | 				/* iosapic overrides any ioapic entry for the same id */ | 
 | 				for (int i = 0; i < slice_len(&slice); i++) { | 
 | 					l = ((struct Atable*)slice_get(&slice, i))->tbl; | 
 | 					if (l->type == ASioapic && l->ioapic.id == id) { | 
 | 						l->ioapic = st->iosapic; | 
 | 						kfree(tt); | 
 | 						tt = NULL; | 
 | 						break; | 
 | 					} | 
 | 				} | 
 | 				break; | 
 | 			case ASlsapic: | 
 | 				st->lsapic.pid = p[2]; | 
 | 				st->lsapic.id = p[3]; | 
 | 				st->lsapic.eid = p[4]; | 
 | 				st->lsapic.puid = l32get(p + 12); | 
 | 				if (l32get(p + 8) == 0) { | 
 | 					kfree(tt); | 
 | 					tt = NULL; | 
 | 				} else | 
 | 					kstrdup(&st->lsapic.puids, (char *)p + 16); | 
 | 				break; | 
 | 			case ASintsrc: | 
 | 				st->intsrc.flags = l16get(p + 2); | 
 | 				st->type = p[4]; | 
 | 				st->intsrc.pid = p[5]; | 
 | 				st->intsrc.peid = p[6]; | 
 | 				st->intsrc.iosv = p[7]; | 
 | 				st->intsrc.intr = l32get(p + 8); | 
 | 				st->intsrc.any = l32get(p + 12); | 
 | 				break; | 
 | 			case ASlx2apic: | 
 | 				st->lx2apic.id = l32get(p + 4); | 
 | 				st->lx2apic.puid = l32get(p + 12); | 
 | 				if (l32get(p + 8) == 0) { | 
 | 					kfree(tt); | 
 | 					tt = NULL; | 
 | 				} | 
 | 				break; | 
 | 			case ASlx2nmi: | 
 | 				st->lx2nmi.flags = l16get(p + 2); | 
 | 				st->lx2nmi.puid = l32get(p + 4); | 
 | 				st->lx2nmi.intr = p[8]; | 
 | 				break; | 
 | 			default: | 
 | 				printd("unknown APIC structure\n"); | 
 | 				kfree(tt); | 
 | 				tt = NULL; | 
 | 		} | 
 | 		if (tt != NULL) { | 
 | 			finatable_nochildren(tt); | 
 | 			slice_append(&slice, tt); | 
 | 		} | 
 | 	} | 
 | 	apics = finatable(t, &slice); | 
 |  | 
 | 	return apics; | 
 | } | 
 |  | 
 | static struct Atable *parsedmar(struct Atable *parent, | 
 |                                 char *name, uint8_t *raw, size_t rawsize) | 
 | { | 
 | 	struct Atable *t, *tt; | 
 | 	int i; | 
 | 	int baselen = MIN(rawsize, 38); | 
 | 	int nentry, nscope, npath, off, dslen, dhlen, len, type, flags; | 
 | 	void *pathp; | 
 | 	char buf[16]; | 
 | 	struct slice drhds; | 
 | 	struct Drhd *drhd; | 
 | 	struct Dmar *dt; | 
 |  | 
 | 	/* count the entries */ | 
 | 	for (nentry = 0, off = 48; off < rawsize; nentry++) { | 
 | 		dslen = l16get(raw + off + 2); | 
 | 		printk("acpi DMAR: entry %d is addr %p (0x%x/0x%x)\n", | 
 | 		       nentry, raw + off, l16get(raw + off), dslen); | 
 | 		off = off + dslen; | 
 | 	} | 
 | 	printk("DMAR: %d entries\n", nentry); | 
 |  | 
 | 	t = mkatable(parent, DMAR, name, raw, rawsize, sizeof(*dmar)); | 
 | 	dt = t->tbl; | 
 | 	/* The table can be only partly filled. */ | 
 | 	if (baselen >= 38 && raw[37] & 1) | 
 | 		dt->intr_remap = 1; | 
 | 	if (baselen >= 37) | 
 | 		dt->haw = raw[36] + 1; | 
 |  | 
 | 	/* Now we walk all the DMAR entries. */ | 
 | 	slice_init(&drhds); | 
 | 	for (off = 48, i = 0; i < nentry; i++, off += dslen) { | 
 | 		snprintf(buf, sizeof(buf), "%d", i); | 
 | 		dslen = l16get(raw + off + 2); | 
 | 		type = l16get(raw + off); | 
 | 		// TODO(dcross): Introduce sensible symbolic constants | 
 | 		// for DMAR entry types. For right now, type 0 => DRHD. | 
 | 		// We skip everything else. | 
 | 		if (type != 0) | 
 | 			continue; | 
 | 		npath = 0; | 
 | 		nscope = 0; | 
 | 		for (int o = off + 16; o < (off + dslen); o += dhlen) { | 
 | 			nscope++; | 
 | 			dhlen = *(raw + o + 1);	// Single byte length. | 
 | 			npath += ((dhlen - 6) / 2); | 
 | 		} | 
 | 		tt = mkatable(t, DRHD, buf, raw + off, dslen, | 
 | 		              sizeof(struct Drhd) + 2 * npath + | 
 | 		              nscope * sizeof(struct DevScope)); | 
 | 		flags = *(raw + off + 4); | 
 | 		drhd = tt->tbl; | 
 | 		drhd->all = flags & 1; | 
 | 		drhd->segment = l16get(raw + off + 6); | 
 | 		drhd->rba = l64get(raw + off + 8); | 
 | 		drhd->nscope = nscope; | 
 | 		drhd->scopes = (void *)drhd + sizeof(struct Drhd); | 
 | 		pathp = (void *)drhd + | 
 | 		    sizeof(struct Drhd) + nscope * sizeof(struct DevScope); | 
 | 		for (int i = 0, o = off + 16; i < nscope; i++) { | 
 | 			struct DevScope *ds = &drhd->scopes[i]; | 
 |  | 
 | 			dhlen = *(raw + o + 1); | 
 | 			ds->enumeration_id = *(raw + o + 4); | 
 | 			ds->start_bus_number = *(raw + o + 5); | 
 | 			ds->npath = (dhlen - 6) / 2; | 
 | 			ds->paths = pathp; | 
 | 			for (int j = 0; j < ds->npath; j++) | 
 | 				ds->paths[j] = l16get(raw + o + 6 + 2*j); | 
 | 			pathp += 2*ds->npath; | 
 | 			o += dhlen; | 
 | 		} | 
 | 		/* | 
 | 		 * NOTE: if all is set, there should be no scopes of type | 
 | 		 * This being ACPI, where vendors randomly copy tables | 
 | 		 * from one system to another, and creating breakage, | 
 | 		 * anything is possible. But we'll warn them. | 
 | 		 */ | 
 | 		finatable_nochildren(tt); | 
 | 		slice_append(&drhds, tt); | 
 | 	} | 
 | 	dmar = finatable(t, &drhds); | 
 |  | 
 | 	return dmar; | 
 | } | 
 |  | 
 | /* | 
 |  * Map the table and keep it there. | 
 |  */ | 
 | static struct Atable *parsessdt(struct Atable *parent, | 
 |                                 char *name, uint8_t *raw, size_t size) | 
 | { | 
 | 	struct Atable *t; | 
 | 	struct Sdthdr *h; | 
 |  | 
 | 	/* | 
 | 	 * We found it and it is too small. | 
 | 	 * Simply return with no side effect. | 
 | 	 */ | 
 | 	if (size < Sdthdrsz) | 
 | 		return NULL; | 
 | 	t = mkatable(parent, SSDT, name, raw, size, 0); | 
 | 	h = (struct Sdthdr *)raw; | 
 | 	memmove(t->name, h->sig, sizeof(h->sig)); | 
 | 	t->name[sizeof(h->sig)] = '\0'; | 
 |  | 
 | 	return finatable_nochildren(t); | 
 | } | 
 |  | 
 | static char *dumptable(char *start, char *end, char *sig, uint8_t *p, int l) | 
 | { | 
 | 	int n, i; | 
 |  | 
 | 	if (2 > 1) { | 
 | 		start = seprintf(start, end, "%s @ %#p\n", sig, p); | 
 | 		if (2 > 2) | 
 | 			n = l; | 
 | 		else | 
 | 			n = 256; | 
 | 		for (i = 0; i < n; i++) { | 
 | 			if ((i % 16) == 0) | 
 | 				start = seprintf(start, end, "%x: ", i); | 
 | 			start = seprintf(start, end, " %2.2ux", p[i]); | 
 | 			if ((i % 16) == 15) | 
 | 				start = seprintf(start, end, "\n"); | 
 | 		} | 
 | 		start = seprintf(start, end, "\n"); | 
 | 		start = seprintf(start, end, "\n"); | 
 | 	} | 
 | 	return start; | 
 | } | 
 |  | 
 | static char *seprinttable(char *s, char *e, struct Atable *t) | 
 | { | 
 | 	uint8_t *p; | 
 | 	int i, n; | 
 |  | 
 | 	p = (uint8_t *)t->tbl;	/* include header */ | 
 | 	n = t->rawsize; | 
 | 	s = seprintf(s, e, "%s @ %#p\n", t->name, p); | 
 | 	for (i = 0; i < n; i++) { | 
 | 		if ((i % 16) == 0) | 
 | 			s = seprintf(s, e, "%x: ", i); | 
 | 		s = seprintf(s, e, " %2.2ux", p[i]); | 
 | 		if ((i % 16) == 15) | 
 | 			s = seprintf(s, e, "\n"); | 
 | 	} | 
 | 	return seprintf(s, e, "\n\n"); | 
 | } | 
 |  | 
 | static void *rsdsearch(char *signature) | 
 | { | 
 | 	uintptr_t p; | 
 | 	uint8_t *bda; | 
 | 	void *rsd; | 
 |  | 
 | 	/* | 
 | 	 * Search for the data structure signature: | 
 | 	 * 1) in the BIOS ROM between 0xE0000 and 0xFFFFF. | 
 | 	 */ | 
 | 	return sigscan(KADDR(0xE0000), 0x20000, signature); | 
 | } | 
 |  | 
 | /* | 
 |  * Note: some of this comment is from the unfinished user interpreter. | 
 |  * | 
 |  * The DSDT is always given to the user interpreter. | 
 |  * Tables listed here are also loaded from the XSDT: | 
 |  * MSCT, MADT, and FADT are processed by us, because they are | 
 |  * required to do early initialization before we have user processes. | 
 |  * Other tables are given to the user level interpreter for | 
 |  * execution. | 
 |  * | 
 |  * These historically returned a value to tell acpi whether or not it was okay | 
 |  * to unmap the table.  (return 0 means there was no table, meaning it was okay | 
 |  * to unmap).  We just use the kernbase mapping, so it's irrelevant. | 
 |  * | 
 |  * N.B. The intel source code defines the constants for ACPI in a | 
 |  * non-endian-independent manner. Rather than bring in the huge wad o' code | 
 |  * that represents, we just the names. | 
 |  */ | 
 | struct Parser { | 
 | 	char *sig; | 
 | 	struct Atable *(*parse)(struct Atable *parent, | 
 | 	                        char *name, uint8_t *raw, size_t rawsize); | 
 | }; | 
 |  | 
 |  | 
 | static struct Parser ptable[] = { | 
 | 	{"FACP", parsefadt}, | 
 | 	{"APIC", parsemadt}, | 
 | 	{"DMAR", parsedmar}, | 
 | 	{"SRAT", parsesrat}, | 
 | 	{"SLIT", parseslit}, | 
 | 	{"MSCT", parsemsct}, | 
 | 	{"SSDT", parsessdt}, | 
 | 	{"HPET", parsehpet}, | 
 | }; | 
 |  | 
 | /* | 
 |  * process xsdt table and load tables with sig, or all if NULL. | 
 |  * (XXX: should be able to search for sig, oemid, oemtblid) | 
 |  */ | 
 | static void parsexsdt(struct Atable *root) | 
 | { | 
 | 	ERRSTACK(1); | 
 | 	struct Sdthdr *sdt; | 
 | 	struct Atable *table; | 
 | 	struct slice slice; | 
 | 	size_t l, end; | 
 | 	uintptr_t dhpa; | 
 | 	struct Atable *n; | 
 | 	uint8_t *tbl; | 
 |  | 
 | 	slice_init(&slice); | 
 | 	if (waserror()) { | 
 | 		slice_destroy(&slice); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	tbl = xsdt->p + sizeof(struct Sdthdr); | 
 | 	end = xsdt->len - sizeof(struct Sdthdr); | 
 | 	for (int i = 0; i < end; i += xsdt->asize) { | 
 | 		dhpa = (xsdt->asize == 8) ? l64get(tbl + i) : l32get(tbl + i); | 
 | 		sdt = sdtmap(dhpa, &l, 1); | 
 | 		if (sdt == NULL) | 
 | 			continue; | 
 | 		printd("acpi: %s addr %#p\n", tsig, sdt); | 
 | 		for (int j = 0; j < ARRAY_SIZE(ptable); j++) { | 
 | 			if (memcmp(sdt->sig, ptable[j].sig, sizeof(sdt->sig)) == 0) { | 
 | 				table = ptable[j].parse(root, ptable[j].sig, (void *)sdt, l); | 
 | 				if (table != NULL) | 
 | 					slice_append(&slice, table); | 
 | 				break; | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	finatable(root, &slice); | 
 | } | 
 |  | 
 | void makeindex(struct Atable *root) | 
 | { | 
 | 	uint64_t index; | 
 |  | 
 | 	if (root == NULL) | 
 | 		return; | 
 | 	index = root->qid.path >> QIndexShift; | 
 | 	atableindex[index] = root; | 
 | 	for (int k = 0; k < root->nchildren; k++) | 
 | 		makeindex(root->children[k]); | 
 | } | 
 |  | 
 | static void parsersdptr(void) | 
 | { | 
 | 	struct Rsdp *rsd; | 
 | 	int asize, cksum; | 
 | 	uintptr_t sdtpa; | 
 |  | 
 | 	static_assert(sizeof(struct Sdthdr) == 36); | 
 |  | 
 | 	/* Find the root pointer. */ | 
 | 	rsd = rsdsearch("RSD PTR "); | 
 | 	if (rsd == NULL) { | 
 | 		printk("NO RSDP\n"); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * Initialize the root of ACPI parse tree. | 
 | 	 */ | 
 | 	lastpath = Qroot; | 
 | 	root = mkatable(NULL, XSDT, devname(), NULL, 0, sizeof(struct Xsdt)); | 
 | 	root->parent = root; | 
 |  | 
 | 	printd("/* RSDP */ struct Rsdp = {%08c, %x, %06c, %x, %p, %d, %p, %x}\n", | 
 | 		   rsd->signature, rsd->rchecksum, rsd->oemid, rsd->revision, | 
 | 		   *(uint32_t *)rsd->raddr, *(uint32_t *)rsd->length, | 
 | 		   *(uint32_t *)rsd->xaddr, rsd->xchecksum); | 
 |  | 
 | 	printd("acpi: RSD PTR@ %#p, physaddr $%p length %ud %#llux rev %d\n", | 
 | 		   rsd, l32get(rsd->raddr), l32get(rsd->length), | 
 | 		   l64get(rsd->xaddr), rsd->revision); | 
 |  | 
 | 	if (rsd->revision >= 2) { | 
 | 		cksum = sdtchecksum(rsd, 36); | 
 | 		if (cksum != 0) { | 
 | 			printk("acpi: bad RSD checksum %d, 64 bit parser aborted\n", cksum); | 
 | 			return; | 
 | 		} | 
 | 		sdtpa = l64get(rsd->xaddr); | 
 | 		asize = 8; | 
 | 	} else { | 
 | 		cksum = sdtchecksum(rsd, 20); | 
 | 		if (cksum != 0) { | 
 | 			printk("acpi: bad RSD checksum %d, 32 bit parser aborted\n", cksum); | 
 | 			return; | 
 | 		} | 
 | 		sdtpa = l32get(rsd->raddr); | 
 | 		asize = 4; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * process the RSDT or XSDT table. | 
 | 	 */ | 
 | 	xsdt = root->tbl; | 
 | 	xsdt->p = sdtmap(sdtpa, &xsdt->len, 1); | 
 | 	if (xsdt->p == NULL) { | 
 | 		printk("acpi: sdtmap failed\n"); | 
 | 		return; | 
 | 	} | 
 | 	if ((xsdt->p[0] != 'R' && xsdt->p[0] != 'X') | 
 | 		|| memcmp(xsdt->p + 1, "SDT", 3) != 0) { | 
 | 		printd("acpi: xsdt sig: %c%c%c%c\n", | 
 | 		       xsdt->p[0], xsdt->p[1], xsdt->p[2], xsdt->p[3]); | 
 | 		xsdt = NULL; | 
 | 		return; | 
 | 	} | 
 | 	xsdt->asize = asize; | 
 | 	printd("acpi: XSDT %#p\n", xsdt); | 
 | 	parsexsdt(root); | 
 | 	atableindex = kreallocarray(NULL, lastpath, sizeof(struct Atable *), | 
 | 	                            MEM_WAIT); | 
 | 	assert(atableindex != NULL); | 
 | 	makeindex(root); | 
 | } | 
 |  | 
 | /* | 
 |  * The invariant that each level in the tree has an associated | 
 |  * Atable implies that each chan can be mapped to an Atable. | 
 |  * The assertions here enforce that invariant. | 
 |  */ | 
 | static struct Atable *genatable(struct chan *c) | 
 | { | 
 | 	struct Atable *a; | 
 | 	uint64_t ai; | 
 |  | 
 | 	ai = c->qid.path >> QIndexShift; | 
 | 	assert(ai < lastpath); | 
 | 	a = atableindex[ai]; | 
 | 	assert(a != NULL); | 
 |  | 
 | 	return a; | 
 | } | 
 |  | 
 | static int acpigen(struct chan *c, char *name, struct dirtab *tab, int ntab, | 
 | 				   int i, struct dir *dp) | 
 | { | 
 | 	struct Atable *a = genatable(c); | 
 |  | 
 | 	if (i == DEVDOTDOT) { | 
 | 		assert((c->qid.path & QIndexMask) == Qdir); | 
 | 		devdir(c, a->parent->qid, a->parent->name, 0, eve, DMDIR|0555, dp); | 
 | 		return 1; | 
 | 	} | 
 | 	return devgen(c, name, a->cdirs, a->nchildren + NQtypes, i, dp); | 
 | } | 
 |  | 
 | /* | 
 |  * Print the contents of the XSDT. | 
 |  */ | 
 | static void dumpxsdt(void) | 
 | { | 
 | 	printk("xsdt: len = %lu, asize = %lu, p = %p\n", | 
 | 	       xsdt->len, xsdt->asize, xsdt->p); | 
 | } | 
 |  | 
 | static char *dumpGas(char *start, char *end, char *prefix, struct Gas *g) | 
 | { | 
 | 	start = seprintf(start, end, "%s", prefix); | 
 |  | 
 | 	switch (g->spc) { | 
 | 		case Rsysmem: | 
 | 		case Rsysio: | 
 | 		case Rembed: | 
 | 		case Rsmbus: | 
 | 		case Rcmos: | 
 | 		case Rpcibar: | 
 | 		case Ripmi: | 
 | 			start = seprintf(start, end, "[%s ", regnames[g->spc]); | 
 | 			break; | 
 | 		case Rpcicfg: | 
 | 			start = seprintf(start, end, "[pci "); | 
 | 			start = | 
 | 				seprintf(start, end, "dev %#p ", | 
 | 						 (uint32_t)(g->addr >> 32) & 0xFFFF); | 
 | 			start = | 
 | 				seprintf(start, end, "fn %#p ", | 
 | 						 (uint32_t)(g->addr & 0xFFFF0000) >> 16); | 
 | 			start = | 
 | 				seprintf(start, end, "adr %#p ", (uint32_t)(g->addr & 0xFFFF)); | 
 | 			break; | 
 | 		case Rfixedhw: | 
 | 			start = seprintf(start, end, "[hw "); | 
 | 			break; | 
 | 		default: | 
 | 			start = seprintf(start, end, "[spc=%#p ", g->spc); | 
 | 	} | 
 | 	start = seprintf(start, end, "off %d len %d addr %#p sz%d]", | 
 | 					 g->off, g->len, g->addr, g->accsz); | 
 | 	start = seprintf(start, end, "\n"); | 
 | 	return start; | 
 | } | 
 |  | 
 | static unsigned int getbanked(uintptr_t ra, uintptr_t rb, int sz) | 
 | { | 
 | 	unsigned int r; | 
 |  | 
 | 	r = 0; | 
 | 	switch (sz) { | 
 | 		case 1: | 
 | 			if (ra != 0) | 
 | 				r |= inb(ra); | 
 | 			if (rb != 0) | 
 | 				r |= inb(rb); | 
 | 			break; | 
 | 		case 2: | 
 | 			if (ra != 0) | 
 | 				r |= inw(ra); | 
 | 			if (rb != 0) | 
 | 				r |= inw(rb); | 
 | 			break; | 
 | 		case 4: | 
 | 			if (ra != 0) | 
 | 				r |= inl(ra); | 
 | 			if (rb != 0) | 
 | 				r |= inl(rb); | 
 | 			break; | 
 | 		default: | 
 | 			printd("getbanked: wrong size\n"); | 
 | 	} | 
 | 	return r; | 
 | } | 
 |  | 
 | static unsigned int setbanked(uintptr_t ra, uintptr_t rb, int sz, int v) | 
 | { | 
 | 	unsigned int r; | 
 |  | 
 | 	r = -1; | 
 | 	switch (sz) { | 
 | 		case 1: | 
 | 			if (ra != 0) | 
 | 				outb(ra, v); | 
 | 			if (rb != 0) | 
 | 				outb(rb, v); | 
 | 			break; | 
 | 		case 2: | 
 | 			if (ra != 0) | 
 | 				outw(ra, v); | 
 | 			if (rb != 0) | 
 | 				outw(rb, v); | 
 | 			break; | 
 | 		case 4: | 
 | 			if (ra != 0) | 
 | 				outl(ra, v); | 
 | 			if (rb != 0) | 
 | 				outl(rb, v); | 
 | 			break; | 
 | 		default: | 
 | 			printd("setbanked: wrong size\n"); | 
 | 	} | 
 | 	return r; | 
 | } | 
 |  | 
 | static unsigned int getpm1ctl(void) | 
 | { | 
 | 	assert(fadt != NULL); | 
 | 	return getbanked(fadt->pm1acntblk, fadt->pm1bcntblk, fadt->pm1cntlen); | 
 | } | 
 |  | 
 | static void setpm1sts(unsigned int v) | 
 | { | 
 | 	assert(fadt != NULL); | 
 | 	setbanked(fadt->pm1aevtblk, fadt->pm1bevtblk, fadt->pm1evtlen / 2, v); | 
 | } | 
 |  | 
 | static unsigned int getpm1sts(void) | 
 | { | 
 | 	assert(fadt != NULL); | 
 | 	return getbanked(fadt->pm1aevtblk, fadt->pm1bevtblk, fadt->pm1evtlen / 2); | 
 | } | 
 |  | 
 | static unsigned int getpm1en(void) | 
 | { | 
 | 	int sz; | 
 |  | 
 | 	assert(fadt != NULL); | 
 | 	sz = fadt->pm1evtlen / 2; | 
 | 	return getbanked(fadt->pm1aevtblk + sz, fadt->pm1bevtblk + sz, sz); | 
 | } | 
 |  | 
 | static int getgpeen(int n) | 
 | { | 
 | 	return inb(gpes[n].enio) & 1 << gpes[n].enbit; | 
 | } | 
 |  | 
 | static void setgpeen(int n, unsigned int v) | 
 | { | 
 | 	int old; | 
 |  | 
 | 	old = inb(gpes[n].enio); | 
 | 	if (v) | 
 | 		outb(gpes[n].enio, old | 1 << gpes[n].enbit); | 
 | 	else | 
 | 		outb(gpes[n].enio, old & ~(1 << gpes[n].enbit)); | 
 | } | 
 |  | 
 | static void clrgpests(int n) | 
 | { | 
 | 	outb(gpes[n].stsio, 1 << gpes[n].stsbit); | 
 | } | 
 |  | 
 | static unsigned int getgpests(int n) | 
 | { | 
 | 	return inb(gpes[n].stsio) & 1 << gpes[n].stsbit; | 
 | } | 
 |  | 
 | #if 0 | 
 | static void acpiintr(Ureg *, void *) | 
 | { | 
 | 	int i; | 
 | 	unsigned int sts, en; | 
 |  | 
 | 	printd("acpi: intr\n"); | 
 |  | 
 | 	for (i = 0; i < ngpes; i++) | 
 | 		if (getgpests(i)) { | 
 | 			printd("gpe %d on\n", i); | 
 | 			en = getgpeen(i); | 
 | 			setgpeen(i, 0); | 
 | 			clrgpests(i); | 
 | 			if (en != 0) | 
 | 				printd("acpiitr: calling gpe %d\n", i); | 
 | 			//  queue gpe for calling gpe->ho in the | 
 | 			//  aml process. | 
 | 			//  enable it again when it returns. | 
 | 		} | 
 | 	sts = getpm1sts(); | 
 | 	en = getpm1en(); | 
 | 	printd("acpiitr: pm1sts %#p pm1en %#p\n", sts, en); | 
 | 	if (sts & en) | 
 | 		printd("have enabled events\n"); | 
 | 	if (sts & 1) | 
 | 		printd("power button\n"); | 
 | 	// XXX serve other interrupts here. | 
 | 	setpm1sts(sts); | 
 | } | 
 | #endif | 
 |  | 
 | static void initgpes(void) | 
 | { | 
 | 	int i, n0, n1; | 
 |  | 
 | 	assert(fadt != NULL); | 
 | 	n0 = fadt->gpe0blklen / 2; | 
 | 	n1 = fadt->gpe1blklen / 2; | 
 | 	ngpes = n0 + n1; | 
 | 	gpes = kzmalloc(sizeof(struct Gpe) * ngpes, 1); | 
 | 	for (i = 0; i < n0; i++) { | 
 | 		gpes[i].nb = i; | 
 | 		gpes[i].stsbit = i & 7; | 
 | 		gpes[i].stsio = fadt->gpe0blk + (i >> 3); | 
 | 		gpes[i].enbit = (n0 + i) & 7; | 
 | 		gpes[i].enio = fadt->gpe0blk + ((n0 + i) >> 3); | 
 | 	} | 
 | 	for (i = 0; i + n0 < ngpes; i++) { | 
 | 		gpes[i + n0].nb = fadt->gp1base + i; | 
 | 		gpes[i + n0].stsbit = i & 7; | 
 | 		gpes[i + n0].stsio = fadt->gpe1blk + (i >> 3); | 
 | 		gpes[i + n0].enbit = (n1 + i) & 7; | 
 | 		gpes[i + n0].enio = fadt->gpe1blk + ((n1 + i) >> 3); | 
 | 	} | 
 | 	for (i = 0; i < ngpes; i++) { | 
 | 		setgpeen(i, 0); | 
 | 		clrgpests(i); | 
 | 	} | 
 | } | 
 |  | 
 | static void acpiioalloc(unsigned int addr, int len) | 
 | { | 
 | 	if (addr != 0) | 
 | 		printd("Just TAKING port %016lx to %016lx\n", addr, addr + len); | 
 | } | 
 |  | 
 | static void acpiinitonce(void) | 
 | { | 
 | 	parsersdptr(); | 
 | 	if (root != NULL) | 
 | 		printk("ACPI initialized\n"); | 
 | } | 
 |  | 
 | int acpiinit(void) | 
 | { | 
 | 	run_once(acpiinitonce()); | 
 | 	return (root == NULL) ? -1 : 0; | 
 | } | 
 |  | 
 | static struct chan *acpiattach(char *spec) | 
 | { | 
 | 	int i; | 
 | 	struct chan *c; | 
 | 	/* | 
 | 	 * This was written for the stock kernel. | 
 | 	 * This code must use 64 registers to be acpi ready in nix. | 
 | 	 */ | 
 | 	if (acpiinit() < 0) | 
 | 		error(ENOSYS, "no acpi"); | 
 |  | 
 | 	/* | 
 | 	 * should use fadt->xpm* and fadt->xgpe* registers for 64 bits. | 
 | 	 * We are not ready in this kernel for that. | 
 | 	 */ | 
 | 	assert(fadt != NULL); | 
 | 	acpiioalloc(fadt->smicmd, 1); | 
 | 	acpiioalloc(fadt->pm1aevtblk, fadt->pm1evtlen); | 
 | 	acpiioalloc(fadt->pm1bevtblk, fadt->pm1evtlen); | 
 | 	acpiioalloc(fadt->pm1acntblk, fadt->pm1cntlen); | 
 | 	acpiioalloc(fadt->pm1bcntblk, fadt->pm1cntlen); | 
 | 	acpiioalloc(fadt->pm2cntblk, fadt->pm2cntlen); | 
 | 	acpiioalloc(fadt->pmtmrblk, fadt->pmtmrlen); | 
 | 	acpiioalloc(fadt->gpe0blk, fadt->gpe0blklen); | 
 | 	acpiioalloc(fadt->gpe1blk, fadt->gpe1blklen); | 
 |  | 
 | 	initgpes(); | 
 | #ifdef RON_SAYS_CONFIG_WE_ARE_NOT_WORTHY | 
 | 	/* this is frightening. SMI: just say no. Although we will almost | 
 | 	 * certainly find that we have no choice. | 
 | 	 * | 
 | 	 * This starts ACPI, which may require we handle | 
 | 	 * power mgmt events ourselves. Use with care. | 
 | 	 */ | 
 | 	outb(fadt->smicmd, fadt->acpienable); | 
 | 	for (i = 0; i < 10; i++) | 
 | 		if (getpm1ctl() & Pm1SciEn) | 
 | 			break; | 
 | 	if (i == 10) | 
 | 		error(EFAIL, "acpi: failed to enable\n"); | 
 | 	if (fadt->sciint != 0) | 
 | 		intrenable(fadt->sciint, acpiintr, 0, BUSUNKNOWN, "acpi"); | 
 | #endif | 
 | 	c = devattach(devname(), spec); | 
 |  | 
 | 	return c; | 
 | } | 
 |  | 
 | static struct walkqid *acpiwalk(struct chan *c, struct chan *nc, char **name, | 
 | 								int nname) | 
 | { | 
 | 	/* | 
 | 	 * Note that devwalk hard-codes a test against the location of 'devgen', | 
 | 	 * so we pretty much have to not pass it here. | 
 | 	 */ | 
 | 	return devwalk(c, nc, name, nname, NULL, 0, acpigen); | 
 | } | 
 |  | 
 | static int acpistat(struct chan *c, uint8_t *dp, int n) | 
 | { | 
 | 	struct Atable *a = genatable(c); | 
 |  | 
 | 	if (c->qid.type == QTDIR) | 
 | 		a = a->parent; | 
 | 	assert(a != NULL); | 
 |  | 
 | 	/* TODO(dcross): make acpigen work here. */ | 
 | 	return devstat(c, dp, n, a->cdirs, a->nchildren + NQtypes, devgen); | 
 | } | 
 |  | 
 | static struct chan *acpiopen(struct chan *c, int omode) | 
 | { | 
 | 	return devopen(c, omode, NULL, 0, acpigen); | 
 | } | 
 |  | 
 | static void acpiclose(struct chan *unused) | 
 | { | 
 | } | 
 |  | 
 | static char *ttext; | 
 | static int tlen; | 
 |  | 
 | // Get the table from the qid. | 
 | // Read that one table using the pointers. | 
 | static long acpiread(struct chan *c, void *a, long n, int64_t off) | 
 | { | 
 | 	long q; | 
 | 	struct Atable *t; | 
 | 	char *ns, *s, *e, *ntext; | 
 |  | 
 | 	if (ttext == NULL) { | 
 | 		tlen = 32768; | 
 | 		ttext = kzmalloc(tlen, 0); | 
 | 	} | 
 | 	if (ttext == NULL) | 
 | 		error(ENOMEM, "acpiread: no memory"); | 
 | 	q = c->qid.path & QIndexMask; | 
 | 	switch (q) { | 
 | 	case Qdir: | 
 | 		return devdirread(c, a, n, NULL, 0, acpigen); | 
 | 	case Qraw: | 
 | 		return readmem(off, a, n, ttext, tlen); | 
 | 	case Qtbl: | 
 | 		s = ttext; | 
 | 		e = ttext + tlen; | 
 | 		strlcpy(s, "no tables\n", tlen); | 
 | 		for (t = tfirst; t != NULL; t = t->next) { | 
 | 			ns = seprinttable(s, e, t); | 
 | 			while (ns == e - 1) { | 
 | 				ntext = krealloc(ttext, tlen * 2, 0); | 
 | 				if (ntext == NULL) | 
 | 					panic("acpi: no memory\n"); | 
 | 				s = ntext + (ttext - s); | 
 | 				ttext = ntext; | 
 | 				tlen *= 2; | 
 | 				e = ttext + tlen; | 
 | 				ns = seprinttable(s, e, t); | 
 | 			} | 
 | 			s = ns; | 
 | 		} | 
 | 		return readstr(off, a, n, ttext); | 
 | 	case Qpretty: | 
 | 		s = ttext; | 
 | 		e = ttext + tlen; | 
 | 		s = dumpfadt(s, e, fadt); | 
 | 		s = dumpmadt(s, e, apics); | 
 | 		s = dumpslit(s, e, slit); | 
 | 		s = dumpsrat(s, e, srat); | 
 | 		s = dumpdmar(s, e, dmar); | 
 | 		dumpmsct(s, e, mscttbl); | 
 | 		return readstr(off, a, n, ttext); | 
 | 	default: | 
 | 		error(EINVAL, "acpiread: bad path %d\n", q); | 
 | 	} | 
 | 	error(EPERM, ERROR_FIXME); | 
 |  | 
 | 	return -1; | 
 | } | 
 |  | 
 | static long acpiwrite(struct chan *c, void *a, long n, int64_t off) | 
 | { | 
 | 	error(EFAIL, "acpiwrite: not until we can figure out what it's for"); | 
 | #if 0 | 
 | 	ERRSTACK(2); | 
 | 	struct cmdtab *ct; | 
 | 	struct cmdbuf *cb; | 
 | 	struct Reg *r; | 
 | 	unsigned int rno, fun, dev, bus, i; | 
 |  | 
 | 	if (c->qid.path == Qio) { | 
 | 		if (reg == NULL) | 
 | 			error(EFAIL, "region not configured"); | 
 | 		return regio(reg, a, n, off, 1); | 
 | 	} | 
 | 	if (c->qid.path != Qctl) | 
 | 		error(EPERM, ERROR_FIXME); | 
 |  | 
 | 	cb = parsecmd(a, n); | 
 | 	if (waserror()) { | 
 | 		kfree(cb); | 
 | 		nexterror(); | 
 | 	} | 
 | 	ct = lookupcmd(cb, ctls, ARRAY_SIZE(ctls)); | 
 | 	switch (ct->index) { | 
 | 		case CMregion: | 
 | 			/* TODO: this block is racy on reg (global) */ | 
 | 			r = reg; | 
 | 			if (r == NULL) { | 
 | 				r = kzmalloc(sizeof(struct Reg), 0); | 
 | 				r->name = NULL; | 
 | 			} | 
 | 			kstrdup(&r->name, cb->f[1]); | 
 | 			r->spc = acpiregid(cb->f[2]); | 
 | 			if (r->spc < 0) { | 
 | 				kfree(r); | 
 | 				reg = NULL; | 
 | 				error(EFAIL, "bad region type"); | 
 | 			} | 
 | 			if (r->spc == Rpcicfg || r->spc == Rpcibar) { | 
 | 				rno = r->base >> Rpciregshift & Rpciregmask; | 
 | 				fun = r->base >> Rpcifunshift & Rpcifunmask; | 
 | 				dev = r->base >> Rpcidevshift & Rpcidevmask; | 
 | 				bus = r->base >> Rpcibusshift & Rpcibusmask; | 
 | #ifdef CONFIG_X86 | 
 | 				r->tbdf = MKBUS(BusPCI, bus, dev, fun); | 
 | #else | 
 | 				r->tbdf = 0 | 
 | #endif | 
 | 				r->base = rno;	/* register ~ our base addr */ | 
 | 			} | 
 | 			r->base = strtoul(cb->f[3], NULL, 0); | 
 | 			r->len = strtoul(cb->f[4], NULL, 0); | 
 | 			r->accsz = strtoul(cb->f[5], NULL, 0); | 
 | 			if (r->accsz < 1 || r->accsz > 4) { | 
 | 				kfree(r); | 
 | 				reg = NULL; | 
 | 				error(EFAIL, "bad region access size"); | 
 | 			} | 
 | 			reg = r; | 
 | 			printd("region %s %s %p %p sz%d", | 
 | 				   r->name, acpiregstr(r->spc), r->base, r->len, r->accsz); | 
 | 			break; | 
 | 		case CMgpe: | 
 | 			i = strtoul(cb->f[1], NULL, 0); | 
 | 			if (i >= ngpes) | 
 | 				error(ERANGE, "gpe out of range"); | 
 | 			kstrdup(&gpes[i].obj, cb->f[2]); | 
 | 			setgpeen(i, 1); | 
 | 			break; | 
 | 		default: | 
 | 			panic("acpi: unknown ctl"); | 
 | 	} | 
 | 	poperror(); | 
 | 	kfree(cb); | 
 | 	return n; | 
 | #endif | 
 | } | 
 |  | 
 | struct { | 
 | 	char *(*pretty)(struct Atable *atbl, char *start, char *end, void *arg); | 
 | } acpisw[NACPITBLS] = { | 
 | }; | 
 |  | 
 | static char *pretty(struct Atable *atbl, char *start, char *end, void *arg) | 
 | { | 
 | 	int type; | 
 |  | 
 | 	type = atbl->type; | 
 | 	if (type < 0 || NACPITBLS < type) | 
 | 		return start; | 
 | 	if (acpisw[type].pretty == NULL) | 
 | 		return seprintf(start, end, "\"\"\n"); | 
 | 	return acpisw[type].pretty(atbl, start, end, arg); | 
 | } | 
 |  | 
 | static char *raw(struct Atable *atbl, char *start, char *end, void *unused_arg) | 
 | { | 
 | 	size_t len = MIN(end - start, atbl->rawsize); | 
 |  | 
 | 	memmove(start, atbl->raw, len); | 
 |  | 
 | 	return start + len; | 
 | } | 
 |  | 
 | struct dev acpidevtab __devtab = { | 
 | 	.name = "acpi", | 
 |  | 
 | 	.reset = devreset, | 
 | 	.init = devinit, | 
 | 	.shutdown = devshutdown, | 
 | 	.attach = acpiattach, | 
 | 	.walk = acpiwalk, | 
 | 	.stat = acpistat, | 
 | 	.open = acpiopen, | 
 | 	.create = devcreate, | 
 | 	.close = acpiclose, | 
 | 	.read = acpiread, | 
 | 	.bread = devbread, | 
 | 	.write = acpiwrite, | 
 | 	.bwrite = devbwrite, | 
 | 	.remove = devremove, | 
 | 	.wstat = devwstat, | 
 | }; |