diff options
author | Freya Murphy <freya@freyacat.org> | 2024-01-27 02:14:19 -0500 |
---|---|---|
committer | Freya Murphy <freya@freyacat.org> | 2024-01-27 02:14:19 -0500 |
commit | 2747693fdb5aa334d8b00a04ed81d2234d710ba9 (patch) | |
tree | 47dad3fd0edac691126e261832a304c3125cd679 /src/arch | |
parent | create bindings.h (diff) | |
download | corn-2747693fdb5aa334d8b00a04ed81d2234d710ba9.tar.gz corn-2747693fdb5aa334d8b00a04ed81d2234d710ba9.tar.bz2 corn-2747693fdb5aa334d8b00a04ed81d2234d710ba9.zip |
acpi + lib
Diffstat (limited to 'src/arch')
-rw-r--r-- | src/arch/amd64/acpi.c | 223 |
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; +} |