diff options
Diffstat (limited to 'kernel/src/acpi')
-rw-r--r-- | kernel/src/acpi/acpi.c | 141 | ||||
-rw-r--r-- | kernel/src/acpi/acpi.h | 106 |
2 files changed, 247 insertions, 0 deletions
diff --git a/kernel/src/acpi/acpi.c b/kernel/src/acpi/acpi.c new file mode 100644 index 0000000..ef1fb76 --- /dev/null +++ b/kernel/src/acpi/acpi.c @@ -0,0 +1,141 @@ +#include <panic.h> +#include <string.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <sys.h> + +#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"); + } +} diff --git a/kernel/src/acpi/acpi.h b/kernel/src/acpi/acpi.h new file mode 100644 index 0000000..889497e --- /dev/null +++ b/kernel/src/acpi/acpi.h @@ -0,0 +1,106 @@ +#pragma once + +#include <stdint.h> + +struct RootSystemDescriptionPointer { + char signature[8]; + uint8_t checksum; + char oemid[6]; + uint8_t revision; + uint32_t rsdt_address; +}; + +struct SystemDescriptionTableHeader { + 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; +}; + +struct RootSystemDescriptionTable { + struct SystemDescriptionTableHeader header; + uint32_t sdt_table[]; +}; + +struct GenericAddressStructure { + uint8_t address_space; + uint8_t bit_width; + uint8_t bit_offset; + uint8_t access_size; + uint64_t address; +}; + +struct FixedACPIDescriptionTable { + struct SystemDescriptionTableHeader header; + 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 GenericAddressStructure 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 GenericAddressStructure x_pm1_a_event_block; + struct GenericAddressStructure x_pm1_b_event_block; + struct GenericAddressStructure x_pm1_a_control_block; + struct GenericAddressStructure x_pm1_b_control_block; + struct GenericAddressStructure x_pm2_control_block; + struct GenericAddressStructure x_pm_timer_block; + struct GenericAddressStructure x_gpe0_block; + struct GenericAddressStructure x_gpe1_block; +}; + +void acpi_init(void); +void acpi_poweroff(void); |