summaryrefslogtreecommitdiff
path: root/kernel/src/acpi
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/src/acpi')
-rw-r--r--kernel/src/acpi/acpi.c141
-rw-r--r--kernel/src/acpi/acpi.h106
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);