aboutsummaryrefslogtreecommitdiff
path: root/kernel/main/acpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/main/acpi.c')
-rw-r--r--kernel/main/acpi.c161
1 files changed, 161 insertions, 0 deletions
diff --git a/kernel/main/acpi.c b/kernel/main/acpi.c
new file mode 100644
index 0000000..cb0f221
--- /dev/null
+++ b/kernel/main/acpi.c
@@ -0,0 +1,161 @@
+#include "main/acpi.h"
+
+#include "boot/config.h"
+#include "mm/page.h"
+#include "types.h"
+#include "util/debug.h"
+#include "util/string.h"
+
+#define XSDT_SIGNATURE (*(uint32_t *)"XSDT")
+#define RSDT_SIGNATURE (*(uint32_t *)"RSDT")
+#define FACP_SIGNATURE (*(uint32_t *)"FACP")
+#define DSDT_SIGNATURE (*(uint32_t *)"DSDT")
+
+#define RSDP_ALIGN 16
+
+#define EBDA_MIN_PADDR 0x80000
+#define EBDA_MAX_PADDR 0xa0000
+#define EBDA_PTR_LOC_PADDR 0x040e
+
+#define EBDA_MIN (PHYS_OFFSET + EBDA_MIN_PADDR)
+#define EBDA_MAX (PHYS_OFFSET + EBDA_MAX_PADDR)
+#define EBDA_PTR_LOC (PHYS_OFFSET + EBDA_PTR_LOC_PADDR)
+
+static const uint8_t rsdp_sig[8] = {'R', 'S', 'D', ' ', 'P', 'T', 'R', ' '};
+
+typedef struct rsdp
+{
+ uint8_t rp_sign[8];
+ uint8_t rp_checksum;
+ uint8_t rp_oemid[6];
+ uint8_t rp_rev;
+ uint32_t rp_addr;
+} packed rsdp_t;
+
+typedef struct rsdp_20
+{
+ rsdp_t rsdp;
+ uint32_t length;
+ uint64_t xsdt_addr;
+ uint8_t ext_checksum;
+ uint8_t reserved[3];
+} packed rsdp_20_t;
+
+typedef struct rsd_table
+{
+ acpi_header_t rt_header;
+ uint64_t rt_other[];
+} packed rsd_table_t;
+
+static uint8_t __acpi_checksum(const uint8_t *buf, long size)
+{
+ uint8_t sum = 0;
+ for (long i = 0; i < size; i++)
+ sum += buf[i];
+ return sum;
+}
+
+static rsdp_20_t *__rsdp_search_range(uintptr_t start, uintptr_t end)
+{
+ uintptr_t rsdp_candidate = start;
+ while (rsdp_candidate <= end - sizeof(struct rsdp))
+ {
+ if (memcmp((void *)rsdp_candidate, rsdp_sig, sizeof(rsdp_sig)) == 0 &&
+ __acpi_checksum((uint8_t *)rsdp_candidate, sizeof(rsdp_20_t)) ==
+ 0)
+ {
+ return (rsdp_20_t *)rsdp_candidate;
+ }
+ rsdp_candidate += RSDP_ALIGN;
+ }
+ return NULL;
+}
+
+static void *__rsdp_search()
+{
+ // detect the location of the EBDA from the BIOS data section
+ uintptr_t ebda =
+ ((uintptr_t) * (uint16_t *)EBDA_PTR_LOC << 4) + PHYS_OFFSET;
+ rsdp_20_t *rsdp = 0;
+ if (ebda >= EBDA_MIN && ebda <= EBDA_MAX && ebda % RSDP_ALIGN == 0)
+ {
+ // check only if it's valid
+ rsdp = __rsdp_search_range(ebda, EBDA_MAX);
+ }
+ if (!rsdp)
+ {
+ // darmanio: unsure where these magic constants came from...
+ rsdp =
+ __rsdp_search_range(PHYS_OFFSET + 0xe0000, PHYS_OFFSET + 0x100000);
+ }
+ return rsdp;
+}
+
+static rsdp_20_t *rsd_ptr = NULL;
+static rsd_table_t *rsd_table = NULL;
+
+static rsd_table_t *_acpi_load_table(uintptr_t paddr)
+{
+ page_mark_reserved(PAGE_ALIGN_DOWN(paddr));
+ return (rsd_table_t *)(PHYS_OFFSET + paddr);
+}
+
+void acpi_init()
+{
+ if (rsd_ptr == NULL)
+ {
+ rsd_ptr = __rsdp_search();
+ KASSERT(rsd_ptr && "Could not find the ACPI Root Descriptor Table.");
+
+ rsd_table = _acpi_load_table(rsd_ptr->xsdt_addr);
+ KASSERT(XSDT_SIGNATURE == rsd_table->rt_header.ah_sign);
+ if (__acpi_checksum((void *)rsd_table, rsd_table->rt_header.ah_size))
+ {
+ panic("Weenix only supports ACPI 2.0 or higher");
+ }
+
+ dbgq(DBG_CORE, "--- ACPI INIT ---\n");
+ dbgq(DBG_CORE, "rsdp addr: %p\n", rsd_ptr);
+ dbgq(DBG_CORE, "rsdt addr: %p\n", rsd_table);
+ dbgq(DBG_CORE, "rev: %i\n", (int)rsd_ptr->rsdp.rp_rev);
+
+ rsd_ptr->rsdp.rp_oemid[5] = 0;
+ dbgq(DBG_CORE, "oem: %s\n", (char *)rsd_ptr->rsdp.rp_oemid);
+
+ // search for all tables listed in the RSDT and checksum them
+ dbgq(DBG_CORE, "ents:\t");
+ size_t headers =
+ (rsd_table->rt_header.ah_size - sizeof(rsd_table->rt_header)) /
+ sizeof(rsd_table->rt_other[0]);
+
+ for (size_t i = 0; i < headers; ++i)
+ {
+ acpi_header_t *header =
+ &_acpi_load_table(rsd_table->rt_other[i])->rt_header;
+ rsd_table->rt_other[i] = (uintptr_t)header;
+
+ dbgq(DBG_CORE, "%.4s ", (char *)&header->ah_sign);
+ KASSERT(0 == __acpi_checksum((void *)header, header->ah_size));
+ }
+ dbgq(DBG_CORE, "\n");
+ }
+}
+
+void *acpi_table(uint32_t signature, int index)
+{
+ KASSERT(index >= 0);
+
+ size_t headers =
+ (rsd_table->rt_header.ah_size - sizeof(rsd_table->rt_header)) /
+ sizeof(rsd_table->rt_other[0]);
+
+ for (size_t i = 0; i < headers; ++i)
+ {
+ acpi_header_t *header = (acpi_header_t *)rsd_table->rt_other[i];
+ if (header->ah_sign == signature && 0 == index--)
+ {
+ return header;
+ }
+ }
+ return NULL;
+}