From 94240835764ee2d02a3adbba9cc7a9ed1b80b60e Mon Sep 17 00:00:00 2001 From: kamkow1 Date: Sun, 14 Dec 2025 01:18:57 +0100 Subject: [PATCH] Parse ACPI MADT table --- kernel/mm/liballoc.c | 2 +- kernel/mm/mregmap.c | 4 +- kernel/platform/i386_pc/acpi/.gitignore | 1 + kernel/platform/i386_pc/acpi/acpi.c | 231 ++++++++++++++++++++++++ kernel/platform/i386_pc/acpi/acpi.h | 173 ++++++++++++++++++ kernel/platform/i386_pc/acpi/make.src | 5 + kernel/platform/i386_pc/boot/bootmain.c | 9 +- kernel/platform/i386_pc/make.src | 1 + kernel/platform/i386_pc/sys/mm.c | 40 +++- kernel/platform/i386_pc/sys/mm.h | 3 +- 10 files changed, 461 insertions(+), 8 deletions(-) create mode 100644 kernel/platform/i386_pc/acpi/.gitignore create mode 100644 kernel/platform/i386_pc/acpi/acpi.c create mode 100644 kernel/platform/i386_pc/acpi/acpi.h create mode 100644 kernel/platform/i386_pc/acpi/make.src diff --git a/kernel/mm/liballoc.c b/kernel/mm/liballoc.c index 3ac1d7c..f66da9d 100644 --- a/kernel/mm/liballoc.c +++ b/kernel/mm/liballoc.c @@ -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); } diff --git a/kernel/mm/mregmap.c b/kernel/mm/mregmap.c index 87b55f9..c59b858 100644 --- a/kernel/mm/mregmap.c +++ b/kernel/mm/mregmap.c @@ -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; diff --git a/kernel/platform/i386_pc/acpi/.gitignore b/kernel/platform/i386_pc/acpi/.gitignore new file mode 100644 index 0000000..5761abc --- /dev/null +++ b/kernel/platform/i386_pc/acpi/.gitignore @@ -0,0 +1 @@ +*.o diff --git a/kernel/platform/i386_pc/acpi/acpi.c b/kernel/platform/i386_pc/acpi/acpi.c new file mode 100644 index 0000000..eee0dda --- /dev/null +++ b/kernel/platform/i386_pc/acpi/acpi.c @@ -0,0 +1,231 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(); + } +} diff --git a/kernel/platform/i386_pc/acpi/acpi.h b/kernel/platform/i386_pc/acpi/acpi.h new file mode 100644 index 0000000..ba4da78 --- /dev/null +++ b/kernel/platform/i386_pc/acpi/acpi.h @@ -0,0 +1,173 @@ +#ifndef _ACPI_ACPI_H +#define _ACPI_ACPI_H + +#include +#include + +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 diff --git a/kernel/platform/i386_pc/acpi/make.src b/kernel/platform/i386_pc/acpi/make.src new file mode 100644 index 0000000..e9ed804 --- /dev/null +++ b/kernel/platform/i386_pc/acpi/make.src @@ -0,0 +1,5 @@ +dir_acpi := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST))))) + +c_files += $(dir_acpi)/acpi.c + +o_files += $(dir_acpi)/acpi.o diff --git a/kernel/platform/i386_pc/boot/bootmain.c b/kernel/platform/i386_pc/boot/bootmain.c index f7161b4..2603843 100644 --- a/kernel/platform/i386_pc/boot/bootmain.c +++ b/kernel/platform/i386_pc/boot/bootmain.c @@ -39,6 +39,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include #include #include @@ -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"); diff --git a/kernel/platform/i386_pc/make.src b/kernel/platform/i386_pc/make.src index 2b305e3..34c1614 100644 --- a/kernel/platform/i386_pc/make.src +++ b/kernel/platform/i386_pc/make.src @@ -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 diff --git a/kernel/platform/i386_pc/sys/mm.c b/kernel/platform/i386_pc/sys/mm.c index a5c504b..8cfa5c9 100644 --- a/kernel/platform/i386_pc/sys/mm.c +++ b/kernel/platform/i386_pc/sys/mm.c @@ -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)); diff --git a/kernel/platform/i386_pc/sys/mm.h b/kernel/platform/i386_pc/sys/mm.h index 5923371..e7c1ac5 100644 --- a/kernel/platform/i386_pc/sys/mm.h +++ b/kernel/platform/i386_pc/sys/mm.h @@ -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);