summaryrefslogtreecommitdiff
path: root/kernel/src/acpi/acpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/src/acpi/acpi.c')
-rw-r--r--kernel/src/acpi/acpi.c141
1 files changed, 141 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");
+ }
+}