Parse ACPI MADT table

This commit is contained in:
2025-12-14 01:18:57 +01:00
parent 56f997ba38
commit 9424083576
10 changed files with 461 additions and 8 deletions

View File

@ -58,7 +58,7 @@ int liballoc_free(void *ptr1, int pages) {
sl_lock(&pd->sl);
for (uptr_t page = ptr; page < ptr + PAGE_SIZE * pages; page += PAGE_SIZE) {
uptr_t phys = mm_translate(pd, page, 0);
uptr_t phys = mm_translate_v2p(pd, page, 0);
mm_unmap_page(pd, page, 0);
pmm_free(phys);
}

View File

@ -42,7 +42,7 @@ void mregmap_init(void) {
}
bool_t mregmap_add_region(struct mreg *mreg) {
bool_t ret = true;
bool_t ret = 1;
for (usize_t i = 0; i < MREGMAP_MAX; i++) {
if (!bitmap_test(&mregmap.bm, i)) {
@ -52,7 +52,7 @@ bool_t mregmap_add_region(struct mreg *mreg) {
}
}
ret = false;
ret = 0;
done:
return ret;

View File

@ -0,0 +1 @@
*.o

View File

@ -0,0 +1,231 @@
#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();
}
}

View File

@ -0,0 +1,173 @@
#ifndef _ACPI_ACPI_H
#define _ACPI_ACPI_H
#include <libk/types.h>
#include <libk/compiler.h>
struct acpi_header {
byte_t signature[4];
uint32_t length;
uint8_t revision;
uint8_t checksum;
byte_t oem[6];
byte_t oemtable[8];
uint32_t oem_revision;
uint32_t creator;
uint32_t creator_revision;
} packed;
struct acpi_rsdp {
byte_t signature[8];
uint8_t checksum;
byte_t oem[6];
uint8_t revision;
uint32_t rsdt_addr;
} packed;
struct acpi_rsdt {
struct acpi_header h;
uint32_t ptrs[];
};
/* Address space */
enum {
ACPI_GAS_SYSMEMORY = 0,
ACPI_GAS_SYSIO = 1,
ACPI_GAS_PCI_CONFIG = 2,
ACPI_GAS_MBED_CTL = 3,
ACPI_GAS_SYSMGMT_BUS = 4,
ACPI_GAS_CMOS = 5,
ACPI_GAS_PCI_BAR_TGT = 6,
ACPI_GAS_IPMI = 7,
ACPI_GAS_GPIO = 8,
ACPI_GAS_SERIAL_BUS = 9,
ACPI_GAS_PLAT_COM_CHL = 10,
};
/* Access size */
enum {
ACPI_GAS_UNDEFINED = 0,
ACPI_GAS_BYTE = 1,
ACPI_GAS_WORD = 2,
ACPI_GAS_DWORD = 3,
ACPI_GAS_QWORD = 4,
};
struct acpi_gas {
uint8_t addr_space;
uint8_t bit_width;
uint8_t bit_offset;
uint8_t access_size;
uint64_t addr;
} packed;
struct acpi_fadt {
struct acpi_header h;
uint32_t firmware_ctl;
uint32_t dsdt;
uint8_t reserved;
uint8_t preferred_power_mgmt_profile;
uint16_t sci_interrupt;
uint32_t smi_cmd_port;
uint8_t acpi_enable;
uint8_t acpi_disable;
uint8_t s4bios_req;
uint8_t pstate_ctl;
uint32_t pm1a_evt_blk;
uint32_t pm1b_evt_blk;
uint32_t pm1a_ctl_blk;
uint32_t pm1b_ctl_blk;
uint32_t pm2_ctl_blk;
uint32_t pm_timer_blk;
uint32_t gpe0_blk;
uint32_t gpe1_blk;
uint8_t pm1_evt_length;
uint8_t pm1_ctl_length;
uint8_t pm2_ctl_length;
uint8_t pm_timer_length;
uint8_t gpe0_length;
uint8_t gpe1_length;
uint8_t gpe1_base;
uint8_t cstate_ctl;
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;
uint16_t boot_arch_flags;
uint8_t reserved2;
uint32_t flags;
struct acpi_gas reset_reg;
uint8_t reset_value;
uint8_t reserved3[3];
uint64_t x_firmware_ctl;
uint64_t x_dsdt;
struct acpi_gas x_pm1a_evt_blk;
struct acpi_gas x_pm1b_evt_blk;
struct acpi_gas x_pm1a_ctl_blk;
struct acpi_gas x_pm1b_ctl_blk;
struct acpi_gas x_pm2_ctl_blk;
struct acpi_gas x_pm_timer_blk;
struct acpi_gas x_gpe0_blk;
struct acpi_gas x_gpe1_blk;
} packed;
struct acpi_madt {
struct acpi_header h;
uint32_t lapic_addr;
uint32_t flags;
} packed;
enum {
ACPI_LAPIC = 0,
ACPI_IOAPIC = 1,
ACPI_INTR_OVERRIDE = 2,
};
struct acpi_apic_header {
uint8_t type;
uint8_t length;
} packed;
struct acpi_lapic {
struct acpi_apic_header h;
uint8_t acpi_cpu_id;
uint8_t apic_id;
uint32_t flags;
} packed;
struct acpi_ioapic {
struct acpi_apic_header h;
uint8_t ioapic_id;
uint8_t reserved;
uint32_t ioapic_addr;
uint32_t global_intr_base;
};
struct acpi_intr_override {
struct acpi_apic_header h;
uint8_t bus;
uint8_t source;
uint32_t interrupt;
uint16_t flags;
};
#define ACPI_MAP_START 0xC5000000
void acpi_init(void);
#endif // _ACPI_ACPI_H

View File

@ -0,0 +1,5 @@
dir_acpi := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
c_files += $(dir_acpi)/acpi.c
o_files += $(dir_acpi)/acpi.o

View File

@ -39,6 +39,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sys/pic.h>
#include <sys/pit.h>
#include <sys/isr.h>
#include <acpi/acpi.h>
#include <mm/pmm.h>
#include <mm/bba.h>
#include <mm/liballoc.h>
@ -136,16 +137,18 @@ void bootmain(void *mbootptr) {
#if CFG_I386_PC_DEBUG == CFG_I386_PC_DEBUG_SERIAL
serialdbg_init();
dbgf("dbgf() test...\n");
#endif
dbgf("dbgf() test...\n");
dbgf("-------------------------------------------------------------\n");
dump_multiboot_header();
mregmap_init1();
pmm_init1();
bba_init();
mm_init();
ramdisk_init();
pit_init();
acpi_init();
/* ramdisk_init(); */
/* pit_init(); */
__asm__ volatile("sti");

View File

@ -6,3 +6,4 @@ kernel_cflags += -isystem $(dir_i386_pc)
include $(dir_i386_pc)/boot/make.src
include $(dir_i386_pc)/libk/make.src
include $(dir_i386_pc)/sys/make.src
include $(dir_i386_pc)/acpi/make.src

View File

@ -40,6 +40,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define PD_INDEX(va) (((va) >> 22) & 0x3FF)
#define PT_INDEX(va) (((va) >> 12) & 0x3FF)
#define PD_SHIFT 22
#define PT_SHIFT 12
extern char __init_pd;
@ -137,7 +139,7 @@ done:
sl_unlock(&pd->sl);
}
uptr_t mm_translate(struct page_dir *pd, uptr_t vaddr, uint32_t flags) {
uptr_t mm_translate_v2p(struct page_dir *pd, uptr_t vaddr, uint32_t flags) {
if (flags & PF_LOCK)
sl_lock(&pd->sl);
@ -162,6 +164,42 @@ done:
return paddr;
}
uptr_t mm_translate_p2v(struct page_dir *pd, uptr_t paddr, uint32_t flags) {
uptr_t vaddr;
bool_t found = 0;
if (flags & PF_LOCK)
sl_lock(&pd->sl);
for (uint32_t pd_idx = 0; pd_idx < 1024; pd_idx++) {
if (!(pd->pd[pd_idx] & PF_PRESENT))
continue;
uptr_t pt_phys = pd->pd[pd_idx] & ~(PAGE_SIZE - 1);
volatile uint32_t *pt = (volatile uint32_t *)(pt_phys + VIRT_BASE);
for (uint32_t pt_idx = 0; pt_idx < 1024; pt_idx++) {
if (!(pt[pt_idx] & PF_PRESENT))
continue;
uptr_t pt_addr = pt[pt_idx] & ~(PAGE_SIZE - 1);
if (pt_addr == (paddr & ~(PAGE_SIZE - 1))) {
vaddr = (pd_idx << PD_SHIFT) | (pt_idx << PT_SHIFT) | (paddr & (PAGE_SIZE - 1));
found = 1;
break;
}
}
if (found)
break;
}
if (flags & PF_LOCK)
sl_unlock(&pd->sl);
return found ? vaddr : 0;
}
void mm_load_pd(struct page_dir *pd) {
uptr_t phys = (uptr_t)pd->pd - VIRT_BASE;
__asm__ volatile ("movl %0, %%cr3" :: "r"(phys));

View File

@ -60,7 +60,8 @@ void mm_init(void);
void mm_map_page(struct page_dir *pd, uptr_t vaddr, uptr_t paddr, uint32_t flags);
void mm_unmap_page(struct page_dir *pd, uptr_t vaddr, uint32_t flags);
uptr_t mm_translate(struct page_dir *pd, uptr_t vaddr, uint32_t flags);
uptr_t mm_translate_v2p(struct page_dir *pd, uptr_t vaddr, uint32_t flags);
uptr_t mm_translate_p2v(struct page_dir *pd, uptr_t paddr, uint32_t flags);
void mm_load_pd(struct page_dir *pd);
struct page_dir *mm_get_kernel_pd(void);