#include #include #include #include #include "bindings.h" /* global state, idk a better way rn */ struct acpi_state state; struct acpi_header { char signature[4]; uint32_t length; uint8_t revision; uint8_t checksum; char oem_id[6]; char oem_table_id[8]; uint32_t oem_revision; uint32_t creator_id; uint32_t creator_revision; }; // eXtended system descriptor pointer struct xsdp { char signature[8]; uint8_t checksum; char oemid[6]; uint8_t revision; uint32_t rsdt_addr; uint32_t length; uint64_t xsdt_addr; uint8_t extendeid_checksum; uint8_t reserved[3]; } __attribute__((packed)); // eXtended system descriptor table struct xsdt { struct acpi_header h; uint64_t sdt_pointers[]; }; // generic address structure struct gas { uint8_t address_space; uint8_t bit_width; uint8_t bit_offset; uint8_t access_size; uint64_t address; }; // fixed acpi description table 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; uint8_t acpi_enable; uint8_t acpi_disable; uint8_t s4bios_req; uint8_t pstate_control; uint32_t pm1_a_event_block; uint32_t pm1_b_event_block; uint32_t pm1_a_control_block; uint32_t pm1_b_control_block; uint32_t pm2_control_block; uint32_t pm_timer_block; uint32_t gpe0_block; uint32_t gpe1_block; uint8_t pm1_event_length; uint8_t pm1_control_length; uint8_t pm2_control_length; uint8_t pm_timer_length; uint8_t gpe0_length; uint8_t gpe1_length; uint8_t gpe1_base; uint8_t cstate_control; uint16_t worst_c2_latency; uint16_t worst_c3_latency; uint16_t flush_size; uint16_t flush_stride; uint8_t duty_offset; uint8_t duty_width; 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; struct gas x_pm1_b_control_block; struct gas x_pm2_control_block; struct gas x_pm_timer_block; struct gas x_gpe0_block; struct gas x_gpe1_block; }; struct acpi_state { struct fadt fadt; uint16_t SLP_TYPa; uint16_t SLP_TYPb; uint16_t SLP_EN; uint16_t SCI_EN; }; 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 acpi_state *state) { uintptr_t ptr = state->fadt.dsdt; char *s5_addr = (void*) (ptr + 36); int dsdt_len = *((int*) (ptr+1)) - 36; 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 ACPI_S5_PARSE_ERROR; } } else { return ACPI_S5_PARSE_ERROR; } return ACPI_SUCCESS; } static void *acpi_find_table(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++) { struct acpi_header *h = (struct acpi_header *) (uintptr_t) xsdt->sdt_pointers[i]; if (!strncmp(h->signature, identifier, ident_len)) return (void *)h; } // TABLE NOT FOUND return NULL; } int acpi_init(void *rootsdp) { struct xsdp *xsdp = (struct xsdp *) rootsdp; if (!checksum((uint8_t *)xsdp, sizeof(struct xsdp))) return ACPI_MALFORMED_TABLE; if (xsdp->revision != 2) return ACPI_OLD_VERSION; struct xsdt *xsdt = (struct xsdt *) (uintptr_t) xsdp->xsdt_addr; if (!checksum((uint8_t *) &xsdt->h, xsdt->h.length)) return ACPI_MALFORMED_TABLE; struct fadt *fadt = acpi_find_table(xsdt, "FACP", 4); if (!fadt) return ACPI_MALFORMED_TABLE; if (!checksum((uint8_t *) &fadt->h, fadt->h.length)) return ACPI_MALFORMED_TABLE; state.fadt = *fadt; int ret = read_s5_addr(&state); if (!ret) return ret; outb(state.fadt.smi_command_port,state.fadt.acpi_enable); return ACPI_SUCCESS; } int acpi_shutdown(void) { outw((unsigned int) state.fadt.pm1_a_control_block, state.SLP_TYPb | state.SLP_EN); return ACPI_FAILURE; }