summaryrefslogtreecommitdiff
path: root/src/arch/amd64/acpi.c
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2024-01-27 02:14:19 -0500
committerFreya Murphy <freya@freyacat.org>2024-01-27 02:14:19 -0500
commit2747693fdb5aa334d8b00a04ed81d2234d710ba9 (patch)
tree47dad3fd0edac691126e261832a304c3125cd679 /src/arch/amd64/acpi.c
parentcreate bindings.h (diff)
downloadcorn-2747693fdb5aa334d8b00a04ed81d2234d710ba9.tar.gz
corn-2747693fdb5aa334d8b00a04ed81d2234d710ba9.tar.bz2
corn-2747693fdb5aa334d8b00a04ed81d2234d710ba9.zip
acpi + lib
Diffstat (limited to 'src/arch/amd64/acpi.c')
-rw-r--r--src/arch/amd64/acpi.c223
1 files changed, 223 insertions, 0 deletions
diff --git a/src/arch/amd64/acpi.c b/src/arch/amd64/acpi.c
new file mode 100644
index 0000000..42c3e01
--- /dev/null
+++ b/src/arch/amd64/acpi.c
@@ -0,0 +1,223 @@
+#include "arch/amd64/bindings.h"
+#include <acpi.h>
+#include <lib.h>
+
+#include <stdint.h>
+#include <stddef.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;
+}