Files
mop3/kernel/platform/i386_pc/acpi/acpi.c
2025-12-14 01:18:57 +01:00

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();
}
}