diff options
Diffstat (limited to 'src/arch/amd64/acpi.c')
-rw-r--r-- | src/arch/amd64/acpi.c | 120 |
1 files changed, 94 insertions, 26 deletions
diff --git a/src/arch/amd64/acpi.c b/src/arch/amd64/acpi.c index eb150fe..61083f4 100644 --- a/src/arch/amd64/acpi.c +++ b/src/arch/amd64/acpi.c @@ -5,6 +5,7 @@ #include <stddef.h> #include "bindings.h" +#include "serial.h" /* global state, idk a better way rn */ struct acpi_state state; @@ -21,7 +22,18 @@ struct acpi_header { uint32_t creator_revision; }; +// root system descriptor pointer +// ACPI 1.0 +struct rsdp { + char signature[8]; + uint8_t checksum; + char oemid[6]; + uint8_t revision; + uint32_t rsdt_addr; +} __attribute__((packed)); + // eXtended system descriptor pointer +// ACPI 2.0 struct xsdp { char signature[8]; uint8_t checksum; @@ -35,13 +47,21 @@ struct xsdp { uint8_t reserved[3]; } __attribute__((packed)); +// root system descriptor table +// ACPI 1.0 +struct rsdt { + struct acpi_header h; + uint64_t sdt_pointers[]; +}; // eXtended system descriptor table +// ACPI 2.0 struct xsdt { struct acpi_header h; uint64_t sdt_pointers[]; }; + // generic address structure struct gas { uint8_t address_space; @@ -56,10 +76,10 @@ struct fadt { struct acpi_header h; uint32_t firmware_ctrl; uint32_t dsdt; - + // field used in ACPI 1.0; no longer in use, for compatibility only uint8_t reserved; - + uint8_t preferred_power_management_profile; uint16_t sci_interrupt; uint32_t smi_command_port; @@ -92,23 +112,23 @@ struct fadt { uint8_t day_alarm; uint8_t month_alarm; uint8_t century; - + // reserved in ACPI 1.0; used since ACPI 2.0+ uint16_t boot_architecture_flags; - + uint8_t reserved_2; uint32_t flags; - + // 12 byte structure; see below for details struct gas reset_reg; - + uint8_t reset_value; uint8_t reserved_3[3]; - + // 64bit pointers - Available on ACPI 2.0+ uint64_t x_firmware_control; uint64_t x_dsdt; - + struct gas x_pm1_a_event_block; struct gas x_pm1_b_event_block; struct gas x_pm1_a_control_block; @@ -135,9 +155,11 @@ static bool checksum(uint8_t *data, size_t len) { } static int read_s5_addr(struct acpi_state *state) { + serial_out_str("a"); uintptr_t ptr = state->fadt.dsdt; char *s5_addr = (void*) (ptr + 36); - + serial_out_str("a"); + int dsdt_len = *((int*) (ptr+1)) - 36; while (0 < dsdt_len--) { if ( memcmp(s5_addr, "_S5_", 4) == 0) @@ -164,17 +186,29 @@ static int read_s5_addr(struct acpi_state *state) { state->SCI_EN = 1; } else { - return ACPI_S5_PARSE_ERROR; + return -1; } } else { - return ACPI_S5_PARSE_ERROR; + return -1; } - return ACPI_SUCCESS; + return -1; } +static void *acpi_find_table_rsdt(struct rsdt *rsdt, const char *identifier, int ident_len) { + int entries = (rsdt->h.length - sizeof(rsdt->h)) / 8; -static void *acpi_find_table(struct xsdt *xsdt, const char *identifier, int ident_len) { + for (int i = 0; i < entries; i++) { + struct acpi_header *h = (struct acpi_header *) (uintptr_t) rsdt->sdt_pointers[i]; + if (!strncmp(h->signature, identifier, ident_len)) + return (void *)h; + } + + // TABLE NOT FOUND + return NULL; +} + +static void *acpi_find_table_xsdt(struct xsdt *xsdt, const char *identifier, int ident_len) { int entries = (xsdt->h.length - sizeof(xsdt->h)) / 8; for (int i = 0; i < entries; i++) { @@ -187,38 +221,72 @@ static void *acpi_find_table(struct xsdt *xsdt, const char *identifier, int iden return NULL; } -int acpi_init(void *rootsdp) { - struct xsdp *xsdp = (struct xsdp *) rootsdp; +int acpi_init_rsdt(struct rsdt *rsdt) { + if (!checksum((uint8_t *) &rsdt->h, rsdt->h.length)) + return -1; + + struct fadt *fadt = acpi_find_table_rsdt(rsdt, "FACP", 4); + if (!fadt) + return -1; + + if (!checksum((uint8_t *) &fadt->h, fadt->h.length)) + return -1; - if (!checksum((uint8_t *)xsdp, sizeof(struct xsdp))) - return ACPI_MALFORMED_TABLE; + state.fadt = *fadt; - if (xsdp->revision != 2) - return ACPI_OLD_VERSION; + return -1; +} - struct xsdt *xsdt = (struct xsdt *) (uintptr_t) xsdp->xsdt_addr; +int acpi_init_xsdt(struct xsdt *xsdt) { if (!checksum((uint8_t *) &xsdt->h, xsdt->h.length)) - return ACPI_MALFORMED_TABLE; + return -1; - struct fadt *fadt = acpi_find_table(xsdt, "FACP", 4); + struct fadt *fadt = acpi_find_table_xsdt(xsdt, "FACP", 4); if (!fadt) - return ACPI_MALFORMED_TABLE; + return -1; if (!checksum((uint8_t *) &fadt->h, fadt->h.length)) - return ACPI_MALFORMED_TABLE; + return -1; state.fadt = *fadt; + return 0; +} + +int acpi_init(void *rootsdp) { + struct rsdp *rsdp = (struct rsdp *) rootsdp; + + if (!checksum((uint8_t *)rsdp, sizeof(struct xsdp))) + return -1; + + int res; + + if (rsdp->revision == 0) { + res = acpi_init_rsdt( + (struct rsdt *) (uintptr_t) rsdp->rsdt_addr + ); + } else if (rsdp->revision == 2) { + struct xsdp *xsdp = (struct xsdp *) rsdp; + res = acpi_init_xsdt( + (struct xsdt *) (uintptr_t) xsdp->xsdt_addr + ); + } else { + return -1; + } + + if (res) + return res; + int ret = read_s5_addr(&state); if (!ret) return ret; outb(state.fadt.smi_command_port,state.fadt.acpi_enable); - return ACPI_SUCCESS; + return 0; } int acpi_shutdown(void) { outw((unsigned int) state.fadt.pm1_a_control_block, state.SLP_TYPb | state.SLP_EN); - return ACPI_FAILURE; + return -1; } |