#include #include #include #include #include #include #include "acpi.h" #include "boot/tag.h" #include "print.h" static struct RootSystemDescriptionTable *rsdt; static struct FixedACPIDescriptionTable *fadt; static uint16_t SLP_TYPa; static uint16_t SLP_TYPb; static uint16_t SLP_EN; static uint16_t SCI_EN; static bool is_init = false; 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 void *find_fadt(void) { int entries = (rsdt->header.length - sizeof(rsdt->header)) / 4; for (int i = 0; i < entries; i++) { uintptr_t sdt_ptr = rsdt->sdt_table[i]; struct SystemDescriptionTableHeader *h = (void*) sdt_ptr; if (!strncmp(h->signature, "FACP", 4)) return (void *) h; } return NULL; } static int read_s5_addr(void) { uintptr_t ptr = 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 SLP_TYPa = *(s5_addr)<<10; s5_addr++; if (*s5_addr == 0x0A) s5_addr++; // skip byteprefix SLP_TYPb = *(s5_addr)<<10; SLP_EN = 1<<13; SCI_EN = 1; } else { errork("\\_S5 parse error."); return 1; } } else { errork("\\_S5 not present."); return 1; } return 0; } void acpi_init(void) { is_init = false; debugk("Loading ACPI"); struct BootTag *tag; if(!get_boot_tag(ID_RSDP, &tag)) { errork("Could not find RSDP"); return; } debugk("Loading RSDT"); struct RootSystemDescriptionPointer *rsdp = tag->data.rsdp; if (!checksum((uint8_t*) rsdp, sizeof(struct RootSystemDescriptionPointer))) { errork("RSDP checksum failed to validate"); return; } uintptr_t rsdt_ptr = rsdp->rsdt_address; rsdt = (void *) rsdt_ptr; if (!checksum((uint8_t*) &rsdt->header, rsdt->header.length)) { errork("RSDT checksum failed to validate"); return; } debugk("Loading FADT"); fadt = find_fadt(); if (fadt == NULL) { errork("Could not find FADT"); return; } if (!checksum((uint8_t*) &fadt->header, fadt->header.length)) { errork("FADT checksum failed to validate"); return; } debugk("Reading \\_S5 Addr"); if (read_s5_addr()) { return; } outb(fadt->smi_command_port,fadt->acpi_enable); succek("ACPI has been loaded"); is_init = true; } void acpi_poweroff(void) { if (is_init) { outw((unsigned int) fadt->pm1_a_control_block, SLP_TYPb | SLP_EN); panic("failed to shutdown"); } else { errork("Cannot shutdown, ACPI not loaded"); } }