From f81181ea9c8e85ac29360689ac991f5d6a3b503e Mon Sep 17 00:00:00 2001 From: Kevin Alavik Date: Sun, 1 Jun 2025 12:52:41 +0200 Subject: [PATCH] feat/kernel: Added I/O APIC support --- .vscode/settings.json | 4 +- kernel/src/arch/paging.c | 72 ++++++++++++++++++------------------ kernel/src/emk.c | 6 ++- kernel/src/sys/acpi.c | 2 +- kernel/src/sys/acpi/madt.h | 8 ++++ kernel/src/sys/apic/ioapic.c | 64 ++++++++++++++++++++++++++++++++ kernel/src/sys/apic/ioapic.h | 15 ++++++++ kernel/src/sys/apic/lapic.c | 11 ++++-- kernel/src/sys/apic/lapic.h | 2 +- 9 files changed, 141 insertions(+), 43 deletions(-) create mode 100644 kernel/src/sys/apic/ioapic.c create mode 100644 kernel/src/sys/apic/ioapic.h diff --git a/.vscode/settings.json b/.vscode/settings.json index 71349dc..3b75a0f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -25,7 +25,9 @@ "spinlock.h": "c", "fb.h": "c", "acpi.h": "c", - "madt.h": "c" + "madt.h": "c", + "ioapic.h": "c", + "stdatomic.h": "c" }, "editor.formatOnPaste": true, "editor.formatOnSave": true, diff --git a/kernel/src/arch/paging.c b/kernel/src/arch/paging.c index 20c8e62..490d7d7 100644 --- a/kernel/src/arch/paging.c +++ b/kernel/src/arch/paging.c @@ -112,42 +112,6 @@ uint64_t *pmget(void) return (uint64_t *)HIGHER_HALF(cr3); } -/* Map virtual to physical address */ -int vmap(uint64_t *pagemap, uint64_t virt, uint64_t phys, uint64_t flags) -{ - if (!pagemap || (virt & (PAGE_SIZE - 1)) || (phys & (PAGE_SIZE - 1))) - { - return -1; - } - - uint64_t pml4_idx = page_index(virt, PML4_SHIFT); - uint64_t pml3_idx = page_index(virt, PML3_SHIFT); - uint64_t pml2_idx = page_index(virt, PML2_SHIFT); - uint64_t pml1_idx = page_index(virt, PML1_SHIFT); - - uint64_t *pml3 = get_or_alloc_table(pagemap, pml4_idx, flags); - if (!pml3) - { - return -1; - } - - uint64_t *pml2 = get_or_alloc_table(pml3, pml3_idx, flags); - if (!pml2) - { - return -1; - } - - uint64_t *pml1 = get_or_alloc_table(pml2, pml2_idx, flags); - if (!pml1) - { - return -1; - } - - pml1[pml1_idx] = phys | flags; - __asm__ volatile("invlpg (%0)" ::"r"(virt) : "memory"); - return 0; -} - /* Map virtual to physical address (large), only use during paging init */ bool _supports_large_pages() { @@ -187,6 +151,42 @@ int vmap_large(uint64_t *pagemap, uint64_t virt, uint64_t phys, uint64_t flags) return 0; } +/* Map virtual to physical address */ +int vmap(uint64_t *pagemap, uint64_t virt, uint64_t phys, uint64_t flags) +{ + if (!pagemap) + { + return -1; + } + + uint64_t pml4_idx = page_index(virt, PML4_SHIFT); + uint64_t pml3_idx = page_index(virt, PML3_SHIFT); + uint64_t pml2_idx = page_index(virt, PML2_SHIFT); + uint64_t pml1_idx = page_index(virt, PML1_SHIFT); + + uint64_t *pml3 = get_or_alloc_table(pagemap, pml4_idx, flags); + if (!pml3) + { + return -1; + } + + uint64_t *pml2 = get_or_alloc_table(pml3, pml3_idx, flags); + if (!pml2) + { + return -1; + } + + uint64_t *pml1 = get_or_alloc_table(pml2, pml2_idx, flags); + if (!pml1) + { + return -1; + } + + pml1[pml1_idx] = phys | flags; + __asm__ volatile("invlpg (%0)" ::"r"(virt) : "memory"); + return 0; +} + /* Unmap virtual address */ int vunmap(uint64_t *pagemap, uint64_t virt) { diff --git a/kernel/src/emk.c b/kernel/src/emk.c index c5a8ea6..1a95348 100644 --- a/kernel/src/emk.c +++ b/kernel/src/emk.c @@ -21,6 +21,7 @@ #include #include #include +#include __attribute__((used, section(".limine_requests"))) static volatile LIMINE_BASE_REVISION(3); __attribute__((used, section(".limine_requests"))) static volatile struct limine_memmap_request memmap_request = { @@ -189,7 +190,7 @@ void emk_entry(void) outb(0x21, 0xff); outb(0xA1, 0xff); - /* Init APIC (LAPIC for now) */ + /* Setup LAPIC */ lapic_init(); /* Setup SMP */ @@ -201,6 +202,9 @@ void emk_entry(void) mp_response = mp_request.response; smp_init(); + /* Setup IOAPIC */ + ioapic_init(); + /* Finished */ log_early("%s", LOG_SEPARATOR); log_early("Finished initializing EMK v1.0, took ? seconds"); /* Still not usermode, so keep using log_early */ diff --git a/kernel/src/sys/acpi.c b/kernel/src/sys/acpi.c index 425a70d..8cc429f 100644 --- a/kernel/src/sys/acpi.c +++ b/kernel/src/sys/acpi.c @@ -14,7 +14,7 @@ void *acpi_vtable(uint64_t phys) acpi_sdt_header_t *tmp = (acpi_sdt_header_t *)vallocat(kvm_ctx, 1, VALLOC_RW, phys); if (!tmp) { - kpanic(NULL, "Failed to map first page of ACPI table"); + kpanic(NULL, "Failed to map first page of ACPI table, got virt: %p", tmp); return NULL; } diff --git a/kernel/src/sys/acpi/madt.h b/kernel/src/sys/acpi/madt.h index e500785..d7d0fb6 100644 --- a/kernel/src/sys/acpi/madt.h +++ b/kernel/src/sys/acpi/madt.h @@ -95,8 +95,16 @@ typedef struct acpi_madt_lx2apic extern acpi_madt_ioapic_t *madt_ioapic_list[256]; extern acpi_madt_ioapic_src_ovr_t *madt_iso_list[256]; +extern acpi_madt_lapic_t *madt_lapic_list[256]; +extern acpi_madt_lapic_nmi_t *madt_lapic_nmi_list[256]; +extern acpi_madt_ioapic_nmi_src_t *madt_ioapic_nmi_list[256]; +extern acpi_madt_lx2apic_t *madt_lx2apic_list[256]; extern uint32_t madt_ioapic_len; extern uint32_t madt_iso_len; +extern uint32_t madt_lapic_len; +extern uint32_t madt_lapic_nmi_len; +extern uint32_t madt_ioapic_nmi_len; +extern uint32_t madt_lx2apic_len; extern uint64_t lapic_addr; void madt_init(); diff --git a/kernel/src/sys/apic/ioapic.c b/kernel/src/sys/apic/ioapic.c new file mode 100644 index 0000000..1b1bcf7 --- /dev/null +++ b/kernel/src/sys/apic/ioapic.c @@ -0,0 +1,64 @@ +/* EMK 1.0 Copyright (c) 2025 Piraterna */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +atomic_uintptr_t ioapic_base = 0; + +void ioapic_write(uint8_t index, uint32_t value) +{ + volatile uint32_t *ioapic = (volatile uint32_t *)atomic_load(&ioapic_base); + ioapic[IOAPIC_OFF_IOREGSEL / 4] = index; // Write register index to IOREGSEL + ioapic[IOAPIC_OFF_IOWIN / 4] = value; // Write value to IOWIN +} + +uint32_t ioapic_read(uint8_t index) +{ + volatile uint32_t *ioapic = (volatile uint32_t *)atomic_load(&ioapic_base); + ioapic[IOAPIC_OFF_IOREGSEL / 4] = index; // Write register index to IOREGSEL + return ioapic[IOAPIC_OFF_IOWIN / 4]; // Read value from IOWIN +} + +void ioapic_map(int irq, int vec, idt_intr_handler handler) +{ + uint32_t redtble_lo = (0 << 16) | /* Unmask the entry */ + (0 << 11) | /* Dest mode */ + (0 << 8) | /* Delivery mode */ + vec; /* Interrupt vector*/ + ioapic_write(2 * irq, redtble_lo); + uint32_t redtble_hi = (get_cpu_local()->lapic_id << 24); + ioapic_write(2 * irq + 1, redtble_hi); + idt_register_handler(vec, handler); +} + +void ioapic_init() +{ + if (madt_ioapic_len < 1) + { + kpanic(NULL, "No I/O APIC's available"); + } + + /* Set base of I/O APIC */ + uint64_t base = madt_ioapic_list[0]->ioapic_addr; + log_early("I/O APIC phys addr: 0x%lx", base); + uint64_t virt_addr = (uint64_t)HIGHER_HALF(base); + + int ret = vmap(pmget(), virt_addr, base, VMM_PRESENT | VMM_WRITE | VMM_NX); + if (ret != 0) + { + log_early("error: Failed to map I/O APIC base 0x%lx to 0x%lx", base, virt_addr); + kpanic(NULL, "I/O APIC mapping failed"); + } + atomic_store(&ioapic_base, virt_addr); + + // Read IOAPICVER register to get the maximum redirection entry + uint32_t ioapic_ver = ioapic_read(IOAPIC_IDX_IOAPICVER); + uint32_t max_irqs = ((ioapic_ver >> 16) & 0xFF) + 1; + log_early("I/O APIC supports %u IRQs", max_irqs); +} \ No newline at end of file diff --git a/kernel/src/sys/apic/ioapic.h b/kernel/src/sys/apic/ioapic.h new file mode 100644 index 0000000..8132b05 --- /dev/null +++ b/kernel/src/sys/apic/ioapic.h @@ -0,0 +1,15 @@ +/* EMK 1.0 Copyright (c) 2025 Piraterna */ +#ifndef IOAPIC_H +#define IOAPIC_H + +#include + +// I/O APIC Registers +#define IOAPIC_OFF_IOREGSEL 0x0 +#define IOAPIC_OFF_IOWIN 0x10 +#define IOAPIC_IDX_IOAPICVER 0x01 + +void ioapic_init(); +void ioapic_map(int irq, int vec, idt_intr_handler handler); + +#endif // IOAPIC_H \ No newline at end of file diff --git a/kernel/src/sys/apic/lapic.c b/kernel/src/sys/apic/lapic.c index 2392772..588fdb8 100644 --- a/kernel/src/sys/apic/lapic.c +++ b/kernel/src/sys/apic/lapic.c @@ -10,9 +10,9 @@ #define LAPIC_REG_SIZE 4 atomic_uintptr_t lapic_msr = 0; -atomic_uintptr_t lapic_base_atomic = 0; +atomic_uintptr_t lapic_base = 0; -#define LAPIC_BASE ((volatile uint32_t *)atomic_load(&lapic_base_atomic)) +#define LAPIC_BASE ((volatile uint32_t *)atomic_load(&lapic_base)) void lapic_write(uint32_t offset, uint32_t value) { @@ -65,10 +65,15 @@ void lapic_init(void) kpanic(NULL, "LAPIC mapping failed"); } - atomic_store(&lapic_base_atomic, virt_addr); + atomic_store(&lapic_base, virt_addr); atomic_store(&lapic_addr, virt_addr); } +void lapic_eoi(void) +{ + lapic_write(LAPIC_EOI, 0); +} + void lapic_enable(void) { volatile uint32_t *base = LAPIC_BASE; diff --git a/kernel/src/sys/apic/lapic.h b/kernel/src/sys/apic/lapic.h index d0d9a77..c7ccbae 100644 --- a/kernel/src/sys/apic/lapic.h +++ b/kernel/src/sys/apic/lapic.h @@ -33,12 +33,12 @@ // Startup vector address (must be in first 1MB, page-aligned) #define AP_BOOT_ADDRESS 0x8000 // Example: 32KB physical address -extern volatile uint32_t *lapic_base; extern uint64_t lapic_addr; uint32_t lapic_read(uint32_t offset); void lapic_write(uint32_t offset, uint32_t value); void lapic_init(void); void lapic_enable(void); +void lapic_eoi(void); #endif // LAPIC_H \ No newline at end of file