232 lines
5.4 KiB
C
232 lines
5.4 KiB
C
#include <libk/types.h>
|
|
#include <libk/compiler.h>
|
|
#include <libk/bitmap.h>
|
|
#include <libk/util.h>
|
|
#include <libk/string.h>
|
|
#include <sys/halt.h>
|
|
#include <sys/mm.h>
|
|
#include <acpi/acpi.h>
|
|
#include <mm/mregmap.h>
|
|
#include <sync/spinlock.h>
|
|
#include <config.h>
|
|
|
|
#define ACPI_RSDP_SCAN_START 0x000E0000
|
|
#define ACPI_RSDP_SCAN_END 0x000FFFFF
|
|
|
|
#define ACPI_PARSER_MATCHES_MAX 32
|
|
|
|
typedef bool_t (*acpi_struct_parser_func_t)(struct acpi_header *h);
|
|
|
|
struct acpi_parser_match {
|
|
char string[4];
|
|
acpi_struct_parser_func_t func;
|
|
};
|
|
|
|
static uptr_t acpi_rsdp;
|
|
static uptr_t acpi_phys_base;
|
|
|
|
/* forward declarations */
|
|
static bool_t acpi_parse_madt(struct acpi_header *h);
|
|
|
|
static struct acpi_parser_match acpi_parser_matches[ACPI_PARSER_MATCHES_MAX] = {
|
|
{ .string = "APIC", .func = &acpi_parse_madt },
|
|
};
|
|
|
|
static bool_t acpi_calc_checksum(struct acpi_header *h) {
|
|
uint8_t sum = 0;
|
|
uint32_t length = h->length;
|
|
|
|
for (uint32_t i = 0; i < length; i++)
|
|
sum += ((uint8_t *)h)[i];
|
|
|
|
return (sum == 0);
|
|
}
|
|
|
|
static void acpi_map_memory(uptr_t ptr, usize_t size) {
|
|
uptr_t aligned_ptr = ALIGN_DOWN(ptr, PAGE_SIZE);
|
|
usize_t aligned_size = ALIGN_UP(size, PAGE_SIZE);
|
|
|
|
struct page_dir *pd = mm_get_kernel_pd();
|
|
|
|
sl_lock(&pd->sl);
|
|
|
|
for (uptr_t page = ACPI_MAP_START, paddr = aligned_ptr;
|
|
page < ACPI_MAP_START + aligned_size;
|
|
page += PAGE_SIZE, paddr += PAGE_SIZE) {
|
|
mm_map_page(pd, page, paddr, PF_PRESENT);
|
|
}
|
|
|
|
sl_unlock(&pd->sl);
|
|
}
|
|
|
|
static bool_t acpi_find_rsdp(void) {
|
|
bool_t ret = 0;
|
|
|
|
uptr_t ptr = ACPI_RSDP_SCAN_START;
|
|
uptr_t end = ACPI_RSDP_SCAN_END;
|
|
|
|
while (ptr < end) {
|
|
if (memcmp((void *)(ptr + VIRT_BASE), "RSD PTR ", 8) == 0) {
|
|
acpi_rsdp = ptr + VIRT_BASE;
|
|
ret = 1;
|
|
break;
|
|
}
|
|
|
|
ptr += 16;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool_t acpi_parse_rsdt(struct acpi_rsdt *rsdt) {
|
|
bool_t ret = 0;
|
|
|
|
dbgf("rsdt=%p\n", rsdt);
|
|
usize_t entries = (rsdt->h.length - sizeof(rsdt->h)) / 4;
|
|
dbgf("ACPI rsdt entries=%zu\n", entries);
|
|
|
|
struct page_dir *pd = mm_get_kernel_pd();
|
|
|
|
sl_lock(&pd->sl);
|
|
|
|
for (usize_t i = 0; i < entries; i++) {
|
|
struct acpi_header *h = (struct acpi_header *)mm_translate_p2v(pd, rsdt->ptrs[i], 0);
|
|
|
|
if (h == NULL)
|
|
continue;
|
|
|
|
bool_t checksum_ok = acpi_calc_checksum(h);
|
|
if (!checksum_ok)
|
|
continue;
|
|
|
|
for (usize_t j = 0; j < LEN(acpi_parser_matches); j++) {
|
|
struct acpi_parser_match *match = &acpi_parser_matches[j];
|
|
|
|
if (memcmp(h->signature, match->string, 4) == 0) {
|
|
dbgf("ACPI parsing %.*s (%p)\n", 4, match->string, h);
|
|
ret = match->func(h);
|
|
dbgf("ACPI %.*s %s\n", 4, match->string, ret ? "OK" : "FAIL");
|
|
if (!ret) {
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
sl_unlock(&pd->sl);
|
|
return ret;
|
|
}
|
|
|
|
static bool_t acpi_parse_rsdp(void) {
|
|
bool_t ret = 1;
|
|
|
|
uint8_t checksum = 0;
|
|
for (usize_t i = 0; i < 20; i++) {
|
|
checksum += ((uint8_t *)acpi_rsdp)[i];
|
|
}
|
|
|
|
if (checksum) {
|
|
dbgf("ACPI rsdp checksum failed: %u\n", (uint32_t)checksum);
|
|
ret = 0;
|
|
goto done;
|
|
}
|
|
|
|
dbgf("ACPI rsdp checksum OK\n");
|
|
|
|
struct acpi_rsdp *rsdp = (struct acpi_rsdp *)acpi_rsdp;
|
|
dbgf("ACPI rsdp OEM=%.*s\n", 6, rsdp->oem);
|
|
|
|
if (!(rsdp->revision == 0 || rsdp->revision == 2)) {
|
|
dbgf("ACPI version %u is not supported\n", rsdp->revision);
|
|
ret = 0;
|
|
goto done;
|
|
}
|
|
|
|
/* we've mapped the rsdt contents into virtual memory, so we're good to go */
|
|
struct acpi_rsdt *rsdt = (struct acpi_rsdt *)ACPI_MAP_START;
|
|
ret = acpi_parse_rsdt(rsdt);
|
|
|
|
done:
|
|
return ret;
|
|
}
|
|
|
|
static bool_t acpi_parse_madt(struct acpi_header *h) {
|
|
struct acpi_madt *madt = (struct acpi_madt *)h;
|
|
dbgf("madt=%p\n", madt);
|
|
|
|
uptr_t ptr = (uptr_t)madt + 0x2C;
|
|
uptr_t end = (uptr_t)madt + madt->h.length;
|
|
|
|
while (ptr < end) {
|
|
struct acpi_apic_header *header = (struct acpi_apic_header *)ptr;
|
|
|
|
if (header->length < sizeof(struct acpi_apic_header))
|
|
break;
|
|
|
|
switch (header->type) {
|
|
case ACPI_LAPIC: {
|
|
struct acpi_lapic *lapic = (struct acpi_lapic *)ptr;
|
|
dbgf("LAPIC: cpu=%u, apic id=%u, flags=%08x\n",
|
|
lapic->acpi_cpu_id, lapic->apic_id, lapic->flags);
|
|
} break;
|
|
case ACPI_IOAPIC: {
|
|
struct acpi_ioapic *ioapic = (struct acpi_ioapic *)ptr;
|
|
dbgf("IOAPIC: id=%u, addr=%08x, GSI base=%u\n",
|
|
ioapic->ioapic_id, ioapic->ioapic_addr, ioapic->global_intr_base);
|
|
} break;
|
|
case ACPI_INTR_OVERRIDE: {
|
|
struct acpi_intr_override *override = (struct acpi_intr_override *)ptr;
|
|
dbgf("INTR OVERRIDE: bus=%u, source=%u, intr=%u, flags=%08x\n",
|
|
override->bus, override->source, override->interrupt, override->flags);
|
|
} break;
|
|
default:
|
|
dbgf("unknown APIC structure\n");
|
|
break;
|
|
}
|
|
|
|
ptr += header->length;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void acpi_init(void) {
|
|
struct mregmap *mregmap = mregmap_get();
|
|
|
|
struct mreg *mreg = NULL;
|
|
|
|
for (usize_t i = 0; i < MREGMAP_MAX; i++) {
|
|
if (!bitmap_test(&mregmap->bm, i))
|
|
continue;
|
|
|
|
mreg = &mregmap->mregs[i];
|
|
|
|
if (mreg->type == MREG_ACPI) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (mreg == NULL) {
|
|
/* We can't go further */
|
|
dbgf("Could not find ACPI memory region!\n");
|
|
halt();
|
|
}
|
|
|
|
dbgf("ACPI memory found at %p (%x)\n", mreg->start, mreg->size);
|
|
|
|
acpi_phys_base = mreg->start;
|
|
acpi_map_memory(mreg->start, mreg->size);
|
|
|
|
if (!acpi_find_rsdp()) {
|
|
dbgf("ACPI rsdp not found\n");
|
|
halt();
|
|
}
|
|
|
|
dbgf("ACPI rsdp found at %p\n", acpi_rsdp - VIRT_BASE);
|
|
|
|
if (!acpi_parse_rsdp()) {
|
|
halt();
|
|
}
|
|
}
|