From 2747693fdb5aa334d8b00a04ed81d2234d710ba9 Mon Sep 17 00:00:00 2001 From: Freya Murphy Date: Sat, 27 Jan 2024 02:14:19 -0500 Subject: [PATCH] acpi + lib --- include/acpi.h | 23 +++++ include/lib.h | 12 +++ src/arch/amd64/acpi.c | 223 ++++++++++++++++++++++++++++++++++++++++++ src/lib.c | 14 +++ 4 files changed, 272 insertions(+) create mode 100644 include/acpi.h create mode 100644 include/lib.h create mode 100644 src/arch/amd64/acpi.c create mode 100644 src/lib.c diff --git a/include/acpi.h b/include/acpi.h new file mode 100644 index 0000000..637ccd2 --- /dev/null +++ b/include/acpi.h @@ -0,0 +1,23 @@ + +enum acpi_status { + ACPI_SUCCESS = 0, + ACPI_FAILURE = -1, + ACPI_MALFORMED_TABLE = -2, + ACPI_OLD_VERSION = -4, + ACPI_S5_PARSE_ERROR = -5, +}; + +/** + * Loads the ACPI tables + * https://en.wikipedia.org/wiki/ACPI + * @param rsdp - pointer to the Root System Description Pointer + * usually passed from the bootlater + * @returns ACPI_SUCCESS on success + */ +int acpi_init(void *rsdp); + +/** + * Shutdowns down the system + * @returns ACPI_FAILURE on failure + */ +int acpi_shutdown(void); diff --git a/include/lib.h b/include/lib.h new file mode 100644 index 0000000..4271aa5 --- /dev/null +++ b/include/lib.h @@ -0,0 +1,12 @@ + +/** + * The strcmp() function compares the two strings s1 and s2. The locale is not taken into account + * (for a locale-aware comparison, see strcoll(3)). The comparison is done using unsigned characters. + */ +int strncmp(const char *s1, const char *s2, unsigned long n); + +/** + * The memcmp() function compares the first n bytes (each interpreted as unsigned char) of the memory + * areas s1 and s2. + */ +int memcmp(const void *s1, const void *s2, unsigned long n); 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 +#include + +#include +#include + +/* 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; +} diff --git a/src/lib.c b/src/lib.c new file mode 100644 index 0000000..d072f1a --- /dev/null +++ b/src/lib.c @@ -0,0 +1,14 @@ +#include + +int strncmp(const char *lhs, const char *rhs, unsigned long n) { + const unsigned char *l=(void *)lhs, *r=(void *)rhs; + if (!n--) return 0; + for (; *l && *r && n && *l == *r ; l++, r++, n--); + return *l - *r; +} + +int memcmp(const void *vl, const void *vr, unsigned long n) { + const unsigned char *l = vl, *r = vr; + for (; n && *l == *r; n--, l++, r++); + return n ? *l-*r : 0; +}