diff options
Diffstat (limited to 'src/acpi.c')
-rw-r--r-- | src/acpi.c | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/src/acpi.c b/src/acpi.c new file mode 100644 index 0000000..c6a0cce --- /dev/null +++ b/src/acpi.c @@ -0,0 +1,162 @@ +#include <acpi.h> +#include <lib.h> +#include <memory.h> +#include <stdint.h> +#include <stddef.h> +#include <string.h> +#include <panic.h> + +#define ACPI_INTERNAL +#include <sys/acpi.h> + +/* global state, idk a better way rn */ +static struct acpi_state state; + +static bool checksum(uint8_t *data, size_t len) { + unsigned char sum = 0; + for (size_t i = 0; i < len; i++) + sum += data[i]; + return sum == 0; +} + +static int read_s5_addr(struct dsdt *dsdt) { + char *s5_addr = dsdt->s5_addr; + int dsdt_len = dsdt->h.length - sizeof(struct acpi_header); + + while (0 < dsdt_len--) { + if (memcmp(s5_addr, "_S5_", 4) == 0) + break; + s5_addr++; + } + + if (dsdt_len > 0) { + // check for valid AML structure + if ( ( *(s5_addr-1) == 0x08 || ( *(s5_addr-2) == 0x08 && *(s5_addr-1) == '\\') ) && *(s5_addr+4) == 0x12 ) { + s5_addr += 5; + s5_addr += ((*s5_addr &0xC0)>>6) +2; // calculate PkgLength size + + if (*s5_addr == 0x0A) + s5_addr++; // skip byteprefix + state.SLP_TYPa = *(s5_addr)<<10; + s5_addr++; + + if (*s5_addr == 0x0A) + s5_addr++; // skip byteprefix + state.SLP_TYPb = *(s5_addr)<<10; + + state.SLP_EN = 1<<13; + state.SCI_EN = 1; + + } else { + return -1; + } + } else { + return -1; + } + + return -1; +} + +static void acpi_load_table(uint64_t addr); + +static void acpi_load_rsdt_tables(struct rsdt *rsdt) { + int entries = (rsdt->h.length - sizeof(rsdt->h)) / 4; + for (int i = 0; i < entries; i++) { + uint32_t addr = rsdt->sdt_pointers[i]; + acpi_load_table(addr); + } +} + +static void acpi_load_xsdt_tables(struct xsdt *xsdt) { + int entries = (xsdt->h.length - sizeof(xsdt->h)) / 8; + for (int i = 0; i < entries; i++) { + uint64_t addr = xsdt->sdt_pointers[i]; + acpi_load_table(addr); + } +} + +#define SIG_RSDT 0x54445352 +#define SIG_XSDT 0x54445358 +#define SIG_FACP 0x50434146 +#define SIG_DSDT 0x54445344 +#define SIG_APIC 0x43495041 +#define SIG_HEPT 0x54455048 +#define SIG_WAET 0x54454157 + +static void acpi_handle_table(struct acpi_header *header) { + switch (header->signature) { + case SIG_RSDT: + state.sdt.rsdt = (struct rsdt *) header; + acpi_load_rsdt_tables(state.sdt.rsdt); + break; + case SIG_XSDT: + state.sdt.xsdt = (struct xsdt *) header; + acpi_load_xsdt_tables(state.sdt.xsdt); + break; + case SIG_FACP: + state.fadt = (struct fadt *) header; + acpi_load_table(state.fadt->dsdt); + break; + case SIG_DSDT: + state.dsdt = (struct dsdt *) header; + read_s5_addr(state.dsdt); + break; + case SIG_APIC: + state.apic = (struct apic *) header; + break; + case SIG_HEPT: + state.hept = (struct hept *) header; + break; + case SIG_WAET: + state.waet = (struct waet *) header; + break; + default: + break; + } +} + +static void acpi_load_table(uint64_t addr) { + struct acpi_header *temp, *mapped; + uint32_t length; + temp = (struct acpi_header * ) (uintptr_t) addr; + mapped = mmap(temp, sizeof(struct acpi_header)); + length = mapped->length; + unmap(mapped); + mapped = mmap(temp, length); + if (!checksum((uint8_t *) mapped, mapped->length)) { + unmap(mapped); + return; + } + kprintf("%.*s: %#016lx\n", 4, (char*)&mapped->signature, (size_t)temp); + acpi_handle_table(mapped); +} + +int acpi_init(void *rootsdp) { + memset(&state, 0, sizeof(struct acpi_state)); + struct rsdp *rsdp = (struct rsdp *) rootsdp; + if (!checksum((uint8_t *)rsdp, sizeof(struct rsdp))) { + return -1; + } + if (memcmp(rsdp->signature, "RSD PTR ", 8) != 0) { + panic("invalid acpi rsdp signature: %.*s\n", 8, rsdp->signature); + } + if (rsdp->revision == 0) { + state.version = 0; + kprintf("ACPI 1.0\n"); + acpi_load_table(rsdp->rsdt_addr); + } else if (rsdp->revision == 2) { + state.version = 2; + struct xsdp *xsdp = (struct xsdp *) rsdp; + kprintf("ACPI 2.0\n"); + acpi_load_table(xsdp->xsdt_addr); + } else { + panic("invalid acpi rev: %d\n", rsdp->revision); + } + kprintf("\n"); + acpi_sys_enable(&state); + return 0; +} + +int acpi_shutdown(void) { + return acpi_sys_shutdown(&state); +} |