blob: b02c026bc542d1940c7eac3503caa70f9e951829 [file] [log] [blame]
/*
* 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>
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,
/* 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 Dmar {
int haw;
/*
* If your firmware disables x2apic mode, you should not be here.
* We ignore that bit.
*/
int intr_remap;
};
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);
extern struct Atable *apics;
extern struct Atable *dmar;
extern struct Atable *srat;