|  | /* | 
|  | * 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. | 
|  | */ | 
|  |  | 
|  | /* ----------------------------------------------------------------------------- | 
|  | * ACPI is a table of tables. The tables define a hierarchy. | 
|  | * | 
|  | * From the hardware's perspective: | 
|  | * Each table that we care about has a header, and the header has a | 
|  | * length that includes the the length of all its subtables. So, even | 
|  | * if you can't completely parse a table, you can find the next table. | 
|  | * | 
|  | * The process of parsing is to find the RSDP, and then for each subtable | 
|  | * see what type it is and parse it. The process is recursive except for | 
|  | * a few issues: The RSDP signature and header differs from the header of | 
|  | * its subtables; their headers differ from the signatures of the tables | 
|  | * they contain. As you walk down the tree, you need different parsers. | 
|  | * | 
|  | * The parser is recursive descent. Each parsing function takes a pointer | 
|  | * to the parent of the node it is parsing and will attach itself to the parent | 
|  | * via that pointer. Parsing functions are responsible for building the data | 
|  | * structures that represent their node and recursive invocations of the parser | 
|  | * for subtables. | 
|  | * | 
|  | * So, in this case, it's something like this: | 
|  | * | 
|  | * RSDP is the root. It has a standard header and size. You map that | 
|  | * memory.  You find the first header, get its type and size, and | 
|  | * parse as much of it as you can. Parsing will involve either a | 
|  | * function or case statement for each element type. DMARs are complex | 
|  | * and need functions; APICs are simple and we can get by with case | 
|  | * statements. | 
|  | * | 
|  | * Each node in the tree is represented as a 'struct Atable'. This has a | 
|  | * pointer to the actual node data, a type tag, a name, pointers to this | 
|  | * node's children (if any) and a parent pointer. It also has a QID so that | 
|  | * the entire structure can be exposed as a filesystem. The Atable doesn't | 
|  | * contain any table data per se; it's metadata. The table pointer contains | 
|  | * the table data as well as a pointer back to it's corresponding Atable. | 
|  | * | 
|  | * In the end we present a directory tree for #apic that looks, in this example: | 
|  | * #acpi/DMAR/DRHD/0/{pretty,raw} | 
|  | * | 
|  | * 'cat pretty' will return JSON-encoded data described the element. | 
|  | * 'cat raw' gets you the raw bytes. | 
|  | */ | 
|  |  | 
|  | #pragma once | 
|  |  | 
|  | #include <ns.h> | 
|  | #include <slice.h> | 
|  | #include <arch/intel-iommu.h> | 
|  |  | 
|  | enum { | 
|  |  | 
|  | Sdthdrsz = 36,		/* size of SDT header */ | 
|  |  | 
|  | /* ACPI regions. Gas ids */ | 
|  | Rsysmem = 0, | 
|  | Rsysio, | 
|  | Rpcicfg, | 
|  | Rembed, | 
|  | Rsmbus, | 
|  | Rcmos, | 
|  | Rpcibar, | 
|  | Ripmi, | 
|  | Rfixedhw = 0x7f, | 
|  |  | 
|  | /* ACPI PM1 control */ | 
|  | Pm1SciEn = 0x1,		/* Generate SCI and not SMI */ | 
|  |  | 
|  | /* ACPI tbdf as encoded in acpi region base addresses */ | 
|  | Rpciregshift = 0, | 
|  | Rpciregmask = 0xFFFF, | 
|  | Rpcifunshift = 16, | 
|  | Rpcifunmask = 0xFFFF, | 
|  | Rpcidevshift = 32, | 
|  | Rpcidevmask = 0xFFFF, | 
|  | Rpcibusshift = 48, | 
|  | Rpcibusmask = 0xFFFF, | 
|  |  | 
|  | /* Apic structure types */ | 
|  | ASlapic = 0,		/* processor local apic */ | 
|  | ASioapic,		/* I/O apic */ | 
|  | ASintovr,		/* Interrupt source override */ | 
|  | ASnmi,			/* NMI source */ | 
|  | ASlnmi,			/* local apic nmi */ | 
|  | ASladdr,		/* local apic address override */ | 
|  | ASiosapic,		/* I/O sapic */ | 
|  | ASlsapic,		/* local sapic */ | 
|  | ASintsrc,		/* platform interrupt sources */ | 
|  | ASlx2apic,		/* local x2 apic */ | 
|  | ASlx2nmi,		/* local x2 apic NMI */ | 
|  |  | 
|  | /* Apic flags */ | 
|  | AFbus = 0,		/* polarity/trigger like in ISA */ | 
|  | AFhigh = 1,		/* active high */ | 
|  | AFlow = 3,		/* active low */ | 
|  | AFpmask = 3,		/* polarity bits */ | 
|  | AFedge = 1 << 2,	/* edge triggered */ | 
|  | AFlevel = 3 << 2,	/* level triggered */ | 
|  | AFtmask = 3 << 2,	/* trigger bits */ | 
|  |  | 
|  | /* Table types. */ | 
|  | RSDP = 0, | 
|  | SDTH, | 
|  | RSDT, | 
|  | FADT, | 
|  | FACS, | 
|  | DSDT, | 
|  | SSDT, | 
|  | MADT, | 
|  | SBST, | 
|  | XSDT, | 
|  | ECDT, | 
|  | SLIT, | 
|  | SRAT, | 
|  | CPEP, | 
|  | MSCT, | 
|  | RASF, | 
|  | MPST, | 
|  | PMTT, | 
|  | BGRT, | 
|  | FPDT, | 
|  | GTDT, | 
|  | HPET, | 
|  | APIC, | 
|  | DMAR, | 
|  | MCFG, | 
|  | /* DMAR types */ | 
|  | DRHD, | 
|  | RMRR, | 
|  | ATSR, | 
|  | RHSA, | 
|  | ANDD, | 
|  | NACPITBLS,		/* Number of ACPI tables */ | 
|  |  | 
|  | /* SRAT types */ | 
|  | SRlapic = 0,		/* Local apic/sapic affinity */ | 
|  | SRmem,			/* Memory affinity */ | 
|  | SRlx2apic,		/* x2 apic affinity */ | 
|  |  | 
|  | /* Atable constants */ | 
|  | SIGSZ		= 4+1,	/* Size of the signature (including NUL) */ | 
|  | OEMIDSZ		= 6+1,	/* Size of the OEM ID (including NUL) */ | 
|  | OEMTBLIDSZ	= 8+1,	/* Size of the OEM Table ID (including NUL) */ | 
|  |  | 
|  | /* Arg for _PIC */ | 
|  | Ppic = 0,		/* PIC interrupt model */ | 
|  | Papic,			/* APIC interrupt model */ | 
|  | Psapic,			/* SAPIC interrupt model */ | 
|  |  | 
|  | CMregion = 0,		/* regio name spc base len accsz */ | 
|  | CMgpe,			/* gpe name id */ | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * ACPI table (sw) | 
|  | * | 
|  | * This Atable struct corresponds to an interpretation of the standard header | 
|  | * for all table types we support. It has a pointer to the converted data, i.e. | 
|  | * the structs created by functions like acpimadt and so on. Note: althouh the | 
|  | * various things in this are a superset of many ACPI table names (DRHD, DRHD | 
|  | * scopes, etc). The raw data follows this header. | 
|  | * | 
|  | * Child entries in the table are kept in an array of pointers. Each entry has | 
|  | * a pointer to it's logically "next" sibling, thus forming a linked list. But | 
|  | * these lists are purely for convenience and all point to nodes within the | 
|  | * same array. | 
|  | */ | 
|  | struct Atable { | 
|  | struct qid qid;		/* QID corresponding to this table. */ | 
|  | struct qid rqid;	/* This table's 'raw' QID. */ | 
|  | struct qid pqid;	/* This table's 'pretty' QID. */ | 
|  | struct qid tqid;	/* This table's 'table' QID. */ | 
|  | int type;		/* This table's type */ | 
|  | void *tbl;		/* pointer to the converted table, e.g. madt. */ | 
|  | char name[16];		/* name of this table */ | 
|  |  | 
|  | struct Atable *parent;	/* Parent pointer */ | 
|  | struct Atable **children; /* children of this node (an array). */ | 
|  | struct dirtab *cdirs;	/* child directory entries of this node. */ | 
|  | size_t nchildren;	/* count of this node's children */ | 
|  | struct Atable *next;	/* Pointer to the next sibling. */ | 
|  |  | 
|  | size_t rawsize;		/* Total size of raw table */ | 
|  | uint8_t *raw;		/* Raw data. */ | 
|  | }; | 
|  |  | 
|  | struct Gpe { | 
|  | uintptr_t stsio;	/* port used for status */ | 
|  | int stsbit;		/* bit number */ | 
|  | uintptr_t enio;		/* port used for enable */ | 
|  | int enbit;		/* bit number */ | 
|  | int nb;			/* event number */ | 
|  | char *obj;		/* handler object  */ | 
|  | int id;			/* id as supplied by user */ | 
|  | }; | 
|  |  | 
|  | struct Regio { | 
|  | void *arg; | 
|  | uint8_t (*get8)(uintptr_t, void *); | 
|  | void (*set8)(uintptr_t, uint8_t unused_int, void *); | 
|  | uint16_t (*get16)(uintptr_t, void *); | 
|  | void (*set16)(uintptr_t, uint16_t unused_int, void *); | 
|  | uint32_t (*get32)(uintptr_t, void *); | 
|  | void (*set32)(uintptr_t, uint32_t, void *); | 
|  | uint64_t (*get64)(uintptr_t, void *); | 
|  | void (*set64)(uintptr_t, uint64_t unused_int, void *); | 
|  | }; | 
|  |  | 
|  | struct Reg { | 
|  | char *name; | 
|  | int spc;		/* io space */ | 
|  | uint64_t base;		/* address, physical */ | 
|  | uint8_t *p;		/* address, kmapped */ | 
|  | uint64_t len; | 
|  | int tbdf; | 
|  | int accsz;		/* access size */ | 
|  | }; | 
|  |  | 
|  | /* Generic address structure. | 
|  | */ | 
|  | struct Gas { | 
|  | uint8_t spc;		/* address space id */ | 
|  | uint8_t len;		/* register size in bits */ | 
|  | uint8_t off;		/* bit offset */ | 
|  | uint8_t accsz;		/* 1: byte; 2: word; 3: dword; 4: qword */ | 
|  | uint64_t addr;		/* address (or acpi encoded tbdf + reg) */ | 
|  | }; | 
|  |  | 
|  | /* Root system description table pointer. | 
|  | * Used to locate the root system description table RSDT | 
|  | * (or the extended system description table from version 2) XSDT. | 
|  | * The XDST contains (after the DST header) a list of pointers to tables: | 
|  | *	- FADT	fixed acpi description table. | 
|  | *		It points to the DSDT, AML code making the acpi namespace. | 
|  | *	- SSDTs	tables with AML code to add to the acpi namespace. | 
|  | *	- pointers to other tables for apics, etc. | 
|  | */ | 
|  | struct Rsdp { | 
|  | uint8_t signature[8];	/* "RSD PTR " */ | 
|  | uint8_t rchecksum; | 
|  | uint8_t oemid[6]; | 
|  | uint8_t revision; | 
|  | uint8_t raddr[4];	/* RSDT */ | 
|  | uint8_t length[4]; | 
|  | uint8_t xaddr[8];	/* XSDT */ | 
|  | uint8_t xchecksum;	/* XSDT */ | 
|  | uint8_t _reserved[3];	/* reserved */ | 
|  | }; | 
|  |  | 
|  | /* Header for ACPI description tables | 
|  | */ | 
|  | struct Sdthdr { | 
|  | uint8_t sig[4];		/* "FACP" or whatever */ | 
|  | uint8_t length[4]; | 
|  | uint8_t rev; | 
|  | uint8_t csum; | 
|  | uint8_t oemid[6]; | 
|  | uint8_t oemtblid[8]; | 
|  | uint8_t oemrev[4]; | 
|  | uint8_t creatorid[4]; | 
|  | uint8_t creatorrev[4]; | 
|  | }; | 
|  |  | 
|  | /* Firmware control structure | 
|  | */ | 
|  | struct Facs { | 
|  | uint8_t sig[4]; | 
|  | uint8_t len[4]; | 
|  | uint32_t hwsig; | 
|  | uint32_t wakingv; | 
|  | uint32_t glock; | 
|  | uint32_t flags; | 
|  | uint64_t xwakingv; | 
|  | uint8_t vers; | 
|  | uint32_t ospmflags; | 
|  | }; | 
|  |  | 
|  | /* Maximum System Characteristics table | 
|  | */ | 
|  | struct Msct { | 
|  | int ndoms;		/* number of domains */ | 
|  | int nclkdoms;		/* number of clock domains */ | 
|  | uint64_t maxpa;		/* max physical address */ | 
|  | size_t nmdom;		/* number of discovered domains */ | 
|  | struct Mdom *dom;	/* array of domains */ | 
|  | }; | 
|  |  | 
|  | struct Mdom { | 
|  | int start;		/* start dom id */ | 
|  | int end;		/* end dom id */ | 
|  | int maxproc;		/* max processor capacity */ | 
|  | uint64_t maxmem;	/* max memory capacity */ | 
|  | }; | 
|  |  | 
|  | /* Multiple APIC description table | 
|  | * Interrupts are virtualized by ACPI and each APIC has | 
|  | * a `virtual interrupt base' where its interrupts start. | 
|  | * Addresses are processor-relative physical addresses. | 
|  | * Only enabled devices are linked, others are filtered out. | 
|  | */ | 
|  | struct Madt { | 
|  | uint64_t lapicpa;	/* local APIC addr */ | 
|  | int pcat;		/* the machine has PC/AT 8259s */ | 
|  | }; | 
|  |  | 
|  | struct Apicst { | 
|  | int type; | 
|  | union { | 
|  | struct { | 
|  | int pid;		/* processor id */ | 
|  | int id;			/* apic no */ | 
|  | } lapic; | 
|  | struct { | 
|  | int id;			/* io apic id */ | 
|  | uint32_t ibase;		/* interrupt base addr. */ | 
|  | uint64_t addr;		/* base address */ | 
|  | } ioapic, iosapic; | 
|  | struct { | 
|  | int irq;		/* bus intr. source (ISA only) */ | 
|  | int intr;		/* system interrupt */ | 
|  | int flags;		/* apic flags */ | 
|  | } intovr; | 
|  | struct { | 
|  | int intr;		/* system interrupt */ | 
|  | int flags;		/* apic flags */ | 
|  | } nmi; | 
|  | struct { | 
|  | int pid;		/* processor id */ | 
|  | int flags;		/* lapic flags */ | 
|  | int lint;		/* lapic LINTn for nmi */ | 
|  | } lnmi; | 
|  | struct { | 
|  | int pid;		/* processor id */ | 
|  | int id;			/* apic id */ | 
|  | int eid;		/* apic eid */ | 
|  | int puid;		/* processor uid */ | 
|  | char *puids;		/* same thing */ | 
|  | } lsapic; | 
|  | struct { | 
|  | int pid;		/* processor id */ | 
|  | int peid;		/* processor eid */ | 
|  | int iosv;		/* io sapic vector */ | 
|  | int intr;		/* global sys intr. */ | 
|  | int type;		/* intr type */ | 
|  | int flags;		/* apic flags */ | 
|  | int any;		/* err sts at any proc */ | 
|  | } intsrc; | 
|  | struct { | 
|  | int id;			/* x2 apic id */ | 
|  | int puid;		/* processor uid */ | 
|  | } lx2apic; | 
|  | struct { | 
|  | int puid; | 
|  | int flags; | 
|  | int intr; | 
|  | } lx2nmi; | 
|  | }; | 
|  | }; | 
|  |  | 
|  | /* System resource affinity table | 
|  | */ | 
|  | struct Srat { | 
|  | int type; | 
|  | union { | 
|  | struct { | 
|  | int dom;		/* proximity domain */ | 
|  | int apic;		/* apic id */ | 
|  | int sapic;		/* sapic id */ | 
|  | int clkdom;		/* clock domain */ | 
|  | } lapic; | 
|  | struct { | 
|  | int dom;		/* proximity domain */ | 
|  | uint64_t addr;		/* base address */ | 
|  | uint64_t len; | 
|  | int hplug;		/* hot pluggable */ | 
|  | int nvram;		/* non volatile */ | 
|  | } mem; | 
|  | struct { | 
|  | int dom;		/* proximity domain */ | 
|  | int apic;		/* x2 apic id */ | 
|  | int clkdom;		/* clock domain */ | 
|  | } lx2apic; | 
|  | }; | 
|  | }; | 
|  |  | 
|  | /* System locality information table | 
|  | */ | 
|  | struct Slit { | 
|  | uint64_t rowlen; | 
|  | struct SlEntry **e; | 
|  | }; | 
|  |  | 
|  | struct SlEntry { | 
|  | int dom;			/* proximity domain */ | 
|  | unsigned int dist;		/* distance to proximity domain */ | 
|  | }; | 
|  |  | 
|  | /* Fixed ACPI description table. | 
|  | * Describes implementation and hardware registers. | 
|  | * PM* blocks are low level functions. | 
|  | * GPE* blocks refer to general purpose events. | 
|  | * P_* blocks are for processor features. | 
|  | * Has address for the DSDT. | 
|  | */ | 
|  | struct Fadt { | 
|  | uint32_t facs; | 
|  | uint32_t dsdt; | 
|  | /* 1 reserved */ | 
|  | uint8_t pmprofile; | 
|  | uint16_t sciint; | 
|  | uint32_t smicmd; | 
|  | uint8_t acpienable; | 
|  | uint8_t acpidisable; | 
|  | uint8_t s4biosreq; | 
|  | uint8_t pstatecnt; | 
|  | uint32_t pm1aevtblk; | 
|  | uint32_t pm1bevtblk; | 
|  | uint32_t pm1acntblk; | 
|  | uint32_t pm1bcntblk; | 
|  | uint32_t pm2cntblk; | 
|  | uint32_t pmtmrblk; | 
|  | uint32_t gpe0blk; | 
|  | uint32_t gpe1blk; | 
|  | uint8_t pm1evtlen; | 
|  | uint8_t pm1cntlen; | 
|  | uint8_t pm2cntlen; | 
|  | uint8_t pmtmrlen; | 
|  | uint8_t gpe0blklen; | 
|  | uint8_t gpe1blklen; | 
|  | uint8_t gp1base; | 
|  | uint8_t cstcnt; | 
|  | uint16_t plvl2lat; | 
|  | uint16_t plvl3lat; | 
|  | uint16_t flushsz; | 
|  | uint16_t flushstride; | 
|  | uint8_t dutyoff; | 
|  | uint8_t dutywidth; | 
|  | uint8_t dayalrm; | 
|  | uint8_t monalrm; | 
|  | uint8_t century; | 
|  | uint16_t iapcbootarch; | 
|  | /* 1 reserved */ | 
|  | uint32_t flags; | 
|  | struct Gas resetreg; | 
|  | uint8_t resetval; | 
|  | /* 3 reserved */ | 
|  | uint64_t xfacs; | 
|  | uint64_t xdsdt; | 
|  | struct Gas xpm1aevtblk; | 
|  | struct Gas xpm1bevtblk; | 
|  | struct Gas xpm1acntblk; | 
|  | struct Gas xpm1bcntblk; | 
|  | struct Gas xpm2cntblk; | 
|  | struct Gas xpmtmrblk; | 
|  | struct Gas xgpe0blk; | 
|  | struct Gas xgpe1blk; | 
|  | }; | 
|  |  | 
|  | /* XSDT/RSDT. 4/8 byte addresses starting at p. | 
|  | */ | 
|  | struct Xsdt { | 
|  | size_t len; | 
|  | size_t asize; | 
|  | uint8_t *p; | 
|  | }; | 
|  |  | 
|  | /* DMAR. | 
|  | */ | 
|  | /* | 
|  | * Device scope. | 
|  | */ | 
|  | struct DevScope { | 
|  | int enumeration_id; | 
|  | int start_bus_number; | 
|  | int npath; | 
|  | int *paths; | 
|  | }; | 
|  | /* | 
|  | * The device scope is basic tbdf as uint32_t. There is a special value | 
|  | * that means "everything" and if we see that we set "all" in the Drhd. | 
|  | */ | 
|  | struct Drhd { | 
|  | int flags; | 
|  | int segment; | 
|  | uintptr_t rba; | 
|  | uintptr_t all;	// This drhd scope is for everything. | 
|  | size_t nscope; | 
|  | struct DevScope *scopes; | 
|  | struct iommu iommu; /* not part of the ACPI table */ | 
|  | }; | 
|  |  | 
|  | struct Dmar { | 
|  | int haw; | 
|  | /* | 
|  | * If your firmware disables x2apic mode, you should not be here. | 
|  | * We ignore that bit. | 
|  | */ | 
|  | int intr_remap; | 
|  | }; | 
|  |  | 
|  | struct acpi_mcfg { | 
|  | uint8_t sig[4]; | 
|  | uint8_t length[4]; | 
|  | uint8_t rev; | 
|  | uint8_t csum; | 
|  | uint8_t oemid[6]; | 
|  | uint8_t oemtblid[8]; | 
|  | uint8_t oemrev[4]; | 
|  | uint8_t creatorid[4]; | 
|  | uint8_t creatorrev[4]; | 
|  | uint8_t _pad[8]; | 
|  | uint8_t entries[0]; | 
|  | }; | 
|  |  | 
|  | struct acpi_mcfg_entry { | 
|  | physaddr_t addr; | 
|  | uint16_t segment; | 
|  | uint8_t start_bus; | 
|  | uint8_t end_bus; | 
|  | }; | 
|  |  | 
|  | struct acpi_mcfg_data { | 
|  | size_t nr_entries; | 
|  | struct acpi_mcfg_entry entries[]; | 
|  | }; | 
|  |  | 
|  | int acpiinit(void); | 
|  | struct Atable *mkatable(struct Atable *parent, | 
|  | int type, char *name, uint8_t *raw, | 
|  | size_t rawsize, size_t addsize); | 
|  | struct Atable *finatable(struct Atable *t, struct slice *slice); | 
|  | struct Atable *finatable_nochildren(struct Atable *t); | 
|  |  | 
|  | int get_early_num_cores(void); | 
|  | physaddr_t acpi_pci_get_mmio_cfg_addr(int segment, int bus, int dev, int func); | 
|  |  | 
|  | extern struct Atable *apics; | 
|  | extern struct Atable *dmar; | 
|  | extern struct Atable *srat; |