diff --git a/.vscode/settings.json b/.vscode/settings.json index 3b75a0f..5c58225 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -27,7 +27,8 @@ "acpi.h": "c", "madt.h": "c", "ioapic.h": "c", - "stdatomic.h": "c" + "stdatomic.h": "c", + "pit.h": "c" }, "editor.formatOnPaste": true, "editor.formatOnSave": true, diff --git a/GNUmakefile b/GNUmakefile index ab9a0cd..8786cdc 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -15,7 +15,7 @@ all: $(IMAGE_NAME).iso .PHONY: run run: $(IMAGE_NAME).iso ovmf/ovmf-code-x86_64.fd @qemu-system-x86_64 \ - -M q35 -serial stdio \ + -M q35 -monitor stdio -serial file:com1.log \ -drive if=pflash,unit=0,format=raw,file=ovmf/ovmf-code-x86_64.fd,readonly=on \ -cdrom $(IMAGE_NAME).iso \ $(QEMUFLAGS) diff --git a/kernel/src/arch/smp.c b/kernel/src/arch/smp.c index f121d07..3001279 100644 --- a/kernel/src/arch/smp.c +++ b/kernel/src/arch/smp.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #define MAX_CPUS 256 #define MSR_GS_BASE 0xC0000101 @@ -53,6 +55,8 @@ void smp_entry(struct limine_mp_info *smp_info) set_cpu_local(cpu); /* Setup core */ + gdt_init(); + idt_init(); pmset(kernel_pagemap); lapic_enable(); diff --git a/kernel/src/dev/pit.c b/kernel/src/dev/pit.c new file mode 100644 index 0000000..da3833e --- /dev/null +++ b/kernel/src/dev/pit.c @@ -0,0 +1,32 @@ +/* EMK 1.0 Copyright (c) 2025 Piraterna */ +#include +#include +#include +#include +#include + +#define PIT_VECTOR 32 + +void (*pit_callback)(struct register_ctx *ctx) = NULL; + +void pit_handler(struct register_ctx *frame) +{ + if (pit_callback) + pit_callback(frame); + lapic_eoi(); +} + +void pit_init(idt_intr_handler handler) +{ + if (handler) + pit_callback = handler; + + outb(0x43, 0x36); + + uint16_t divisor = 5966; + outb(0x40, divisor & 0xFF); + outb(0x40, (divisor >> 8) & 0xFF); + + ioapic_map(0, PIT_VECTOR, pit_handler, 0); + ioapic_unmask(0); +} \ No newline at end of file diff --git a/kernel/src/dev/pit.h b/kernel/src/dev/pit.h new file mode 100644 index 0000000..f2e1279 --- /dev/null +++ b/kernel/src/dev/pit.h @@ -0,0 +1,9 @@ +/* EMK 1.0 Copyright (c) 2025 Piraterna */ +#ifndef PIT_H +#define PIT_H + +#include + +void pit_init(idt_intr_handler handler); + +#endif // PIT_H \ No newline at end of file diff --git a/kernel/src/emk.c b/kernel/src/emk.c index 1a95348..6d9e99d 100644 --- a/kernel/src/emk.c +++ b/kernel/src/emk.c @@ -22,6 +22,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 = { @@ -66,6 +67,11 @@ struct limine_mp_response *mp_response = NULL; struct flanterm_context *ft_ctx = NULL; #endif // FLANTERM_SUPPORT +void tick(struct register_ctx *) +{ + log_early("tick"); +} + void emk_entry(void) { __asm__ volatile("movq %%rsp, %0" : "=r"(kstack_top)); @@ -187,11 +193,8 @@ void emk_entry(void) madt_init(); // Also init MADT, to prepare for APIC /* Disable legacy PIC to prepare for APIC */ - outb(0x21, 0xff); - outb(0xA1, 0xff); - - /* Setup LAPIC */ - lapic_init(); + outb(0x21, 0xFF); + outb(0xA1, 0xFF); /* Setup SMP */ if (!mp_request.response) @@ -200,14 +203,17 @@ void emk_entry(void) } mp_response = mp_request.response; + lapic_init(); smp_init(); /* Setup IOAPIC */ ioapic_init(); + /* Setup timer */ + pit_init(tick); + /* Finished */ log_early("%s", LOG_SEPARATOR); log_early("Finished initializing EMK v1.0, took ? seconds"); /* Still not usermode, so keep using log_early */ - hlt(); } \ No newline at end of file diff --git a/kernel/src/sys/apic/ioapic.c b/kernel/src/sys/apic/ioapic.c index 1b1bcf7..056de2f 100644 --- a/kernel/src/sys/apic/ioapic.c +++ b/kernel/src/sys/apic/ioapic.c @@ -5,60 +5,159 @@ #include #include #include -#include #include #include +#include +#include -atomic_uintptr_t ioapic_base = 0; +static atomic_uintptr_t ioapic_base = 0; + +// Helper function to translate IRQ to GSI based on MADT ISO entries +static uint32_t irq_to_gsi(uint32_t irq) +{ + for (uint32_t i = 0; i < madt_iso_len; i++) + { + struct acpi_madt_ioapic_src_ovr *iso = madt_iso_list[i]; + if (iso->irq_source == irq) + { + return iso->gsi; + } + } + // No override; assume IRQ == GSI + return irq; +} 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 + if (!ioapic) + { + log_early("error: IOAPIC not initialized"); + kpanic(NULL, "IOAPIC write before init"); + } + ioapic[IOAPIC_OFF_IOREGSEL / 4] = index; + ioapic[IOAPIC_OFF_IOWIN / 4] = value; } 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 + if (!ioapic) + { + log_early("error: IOAPIC not initialized"); + return 0; + } + ioapic[IOAPIC_OFF_IOREGSEL / 4] = index; + return ioapic[IOAPIC_OFF_IOWIN / 4]; } -void ioapic_map(int irq, int vec, idt_intr_handler handler) +void ioapic_map(int irq, int vec, idt_intr_handler handler, uint8_t dest_mode) { - 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 gsi = irq_to_gsi(irq); + uint32_t max_irqs = ((ioapic_read(IOAPIC_IDX_IOAPICVER) >> 16) & 0xFF) + 1; + if (gsi >= max_irqs) + { + log_early("error: Invalid GSI %u for IRQ %d (max %u)", gsi, irq, max_irqs); + kpanic(NULL, "Invalid GSI for IOAPIC"); + } + if (vec < 32 || vec > 255 || vec == LAPIC_SPURIOUS_VECTOR) + { + log_early("error: Invalid vector 0x%x for IRQ %d (GSI %u)", vec, irq, gsi); + kpanic(NULL, "Invalid vector"); + } + + uint32_t redtble_lo = (1 << 16) | // Masked by default + (dest_mode << 11) | // 0: Physical, 1: Logical + (0 << 8) | // Fixed delivery + vec; uint32_t redtble_hi = (get_cpu_local()->lapic_id << 24); - ioapic_write(2 * irq + 1, redtble_hi); - idt_register_handler(vec, handler); + ioapic_write(0x10 + 2 * gsi, redtble_lo); + ioapic_write(0x10 + 2 * gsi + 1, redtble_hi); + + if (handler) + { + idt_register_handler(vec, handler); + } + log_early("Mapped IRQ %d (GSI %u) to vector 0x%x on CPU %u", irq, gsi, vec, get_cpu_local()->lapic_id); } -void ioapic_init() +void ioapic_unmask(int irq) +{ + uint32_t gsi = irq_to_gsi(irq); + uint32_t max_irqs = ((ioapic_read(IOAPIC_IDX_IOAPICVER) >> 16) & 0xFF) + 1; + if (gsi >= max_irqs) + { + log_early("error: Invalid GSI %u for IRQ %d (max %u)", gsi, irq, max_irqs); + return; + } + uint32_t redtble_lo = ioapic_read(0x10 + 2 * gsi); + redtble_lo &= ~(1 << 16); + ioapic_write(0x10 + 2 * gsi, redtble_lo); + log_early("Unmasked IRQ %d (GSI %u)", irq, gsi); +} + +void ioapic_init(void) { if (madt_ioapic_len < 1) { - kpanic(NULL, "No I/O APIC's available"); + log_early("error: No IOAPIC entries in MADT"); + kpanic(NULL, "No IOAPIC 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); + uint64_t phys_addr = madt_ioapic_list[0]->ioapic_addr; + if (phys_addr & 0xFFF) + { + log_early("error: IOAPIC base 0x%lx not page-aligned", phys_addr); + kpanic(NULL, "Invalid IOAPIC alignment"); + } + uint64_t virt_addr = (uint64_t)HIGHER_HALF(phys_addr); + int ret = vmap(pmget(), virt_addr, phys_addr, 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"); + log_early("error: Failed to map IOAPIC 0x%lx to 0x%lx (%d)", phys_addr, virt_addr, ret); + kpanic(NULL, "IOAPIC 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); + + // Initialize all GSIs, respecting ISO overrides + for (uint32_t gsi = 0; gsi < max_irqs; gsi++) + { + uint32_t vec = 32 + gsi; + if (vec == LAPIC_SPURIOUS_VECTOR) + { + vec++; + } + // Check if this GSI is overridden + int is_overridden = 0; + uint32_t irq = gsi; // Default: GSI == IRQ + for (uint32_t i = 0; i < madt_iso_len; i++) + { + if (madt_iso_list[i]->gsi == gsi) + { + is_overridden = 1; + irq = madt_iso_list[i]->irq_source; + break; + } + } + uint32_t redtble_lo = (1 << 16) | // Masked by default + (0 << 11) | // Physical mode + (0 << 8) | // Fixed delivery + vec; + uint32_t redtble_hi = (get_cpu_local()->lapic_id << 24); + ioapic_write(0x10 + 2 * gsi, redtble_lo); + ioapic_write(0x10 + 2 * gsi + 1, redtble_hi); + if (is_overridden) + { + log_early("Initialized IRQ %u (GSI %u) to vector 0x%x", irq, gsi, vec); + } + else + { + log_early("Initialized GSI %u to vector 0x%x", gsi, vec); + } + } + + log_early("IOAPIC init at 0x%lx (virt 0x%lx) with %u IRQs", phys_addr, virt_addr, max_irqs); } \ No newline at end of file diff --git a/kernel/src/sys/apic/ioapic.h b/kernel/src/sys/apic/ioapic.h index 8132b05..46257f2 100644 --- a/kernel/src/sys/apic/ioapic.h +++ b/kernel/src/sys/apic/ioapic.h @@ -4,12 +4,15 @@ #include -// I/O APIC Registers #define IOAPIC_OFF_IOREGSEL 0x0 #define IOAPIC_OFF_IOWIN 0x10 + +#define IOAPIC_IDX_IOAPICID 0x00 #define IOAPIC_IDX_IOAPICVER 0x01 +#define IOAPIC_IDX_RED_TBL 0x10 void ioapic_init(); -void ioapic_map(int irq, int vec, idt_intr_handler handler); +void ioapic_map(int irq, int vec, idt_intr_handler handler, uint8_t dest_mode); +void ioapic_unmask(int irq); #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 588fdb8..2b8803c 100644 --- a/kernel/src/sys/apic/lapic.c +++ b/kernel/src/sys/apic/lapic.c @@ -9,8 +9,8 @@ #define LAPIC_REG_ALIGN 16 #define LAPIC_REG_SIZE 4 -atomic_uintptr_t lapic_msr = 0; -atomic_uintptr_t lapic_base = 0; +static atomic_uintptr_t lapic_msr = 0; +static atomic_uintptr_t lapic_base = 0; #define LAPIC_BASE ((volatile uint32_t *)atomic_load(&lapic_base)) @@ -19,16 +19,15 @@ void lapic_write(uint32_t offset, uint32_t value) volatile uint32_t *base = LAPIC_BASE; if (!base) { - log_early("error: LAPIC not initialized!"); - kpanic(NULL, "LAPIC write attempted before initialization"); + log_early("error: LAPIC not initialized"); + kpanic(NULL, "LAPIC write before init"); } if (offset % LAPIC_REG_ALIGN != 0) { log_early("error: Misaligned LAPIC offset 0x%x", offset); - kpanic(NULL, "Invalid LAPIC register offset"); + kpanic(NULL, "Invalid LAPIC offset"); } - volatile uint32_t *reg = base + (offset / LAPIC_REG_SIZE); - *reg = value; + base[offset / LAPIC_REG_SIZE] = value; } uint32_t lapic_read(uint32_t offset) @@ -36,37 +35,51 @@ uint32_t lapic_read(uint32_t offset) volatile uint32_t *base = LAPIC_BASE; if (!base) { - log_early("error: LAPIC not initialized!"); + log_early("error: LAPIC not initialized"); return 0; } if (offset % LAPIC_REG_ALIGN != 0) { log_early("error: Misaligned LAPIC offset 0x%x", offset); - kpanic(NULL, "Invalid LAPIC register offset"); + kpanic(NULL, "Invalid LAPIC offset"); } - volatile uint32_t *reg = base + (offset / LAPIC_REG_SIZE); - return *reg; + return base[offset / LAPIC_REG_SIZE]; } void lapic_init(void) { uint64_t msr = rdmsr(LAPIC_BASE_MSR); - msr |= (1 << 11); // Set global LAPIC enable bit + if (!(msr & (1ULL << 11))) + { + log_early("LAPIC disabled in MSR, enabling"); + } + msr |= (1ULL << 11); wrmsr(LAPIC_BASE_MSR, msr); atomic_store(&lapic_msr, msr); uint64_t phys_addr = msr & ~0xFFFULL; + if (phys_addr & 0xFFF) + { + log_early("error: LAPIC base 0x%lx not page-aligned", phys_addr); + kpanic(NULL, "Invalid LAPIC alignment"); + } uint64_t virt_addr = (uint64_t)HIGHER_HALF(phys_addr); - int ret = vmap(pmget(), virt_addr, phys_addr, VMM_PRESENT | VMM_WRITE | VMM_NX); if (ret != 0) { - log_early("error: Failed to map LAPIC base 0x%lx to 0x%lx", phys_addr, virt_addr); + log_early("error: Failed to map LAPIC 0x%lx to 0x%lx (%d)", phys_addr, virt_addr, ret); kpanic(NULL, "LAPIC mapping failed"); } - atomic_store(&lapic_base, virt_addr); - atomic_store(&lapic_addr, virt_addr); + + uint32_t id = lapic_read(LAPIC_ID) >> 24; + if (id > 255) + { + log_early("error: Invalid LAPIC ID %u", id); + kpanic(NULL, "Invalid LAPIC ID"); + } + log_early("LAPIC init: CPU %u at 0x%lx (virt 0x%lx)", id, phys_addr, virt_addr); + lapic_write(LAPIC_EOI, 0); } void lapic_eoi(void) @@ -79,17 +92,19 @@ void lapic_enable(void) volatile uint32_t *base = LAPIC_BASE; if (!base) { - log_early("warning: lapic_enable called before lapic_init"); + log_early("warning: lapic_enable before init"); return; } uint32_t svr = lapic_read(LAPIC_SVR); - svr |= (1 << 8); // Enable APIC - svr &= ~(1 << 9); // Disable focus processor checking - svr = (svr & ~0xFF) | 0xFF; // Set spurious interrupt vector to 0xFF - + svr |= (1 << 8); + svr &= ~(1 << 9); + svr = (svr & ~0xFF) | LAPIC_SPURIOUS_VECTOR; lapic_write(LAPIC_SVR, svr); + + lapic_write(LAPIC_LVT_TIMER, (1 << 16)); lapic_write(LAPIC_TPR, 0); + uint32_t id = lapic_read(LAPIC_ID) >> 24; - log_early("LAPIC enabled and initialized for CPU %u", id); + log_early("LAPIC enabled: CPU %u, spurious vector 0x%x", id, LAPIC_SPURIOUS_VECTOR); } \ No newline at end of file diff --git a/kernel/src/sys/apic/lapic.h b/kernel/src/sys/apic/lapic.h index c7ccbae..8fb45ac 100644 --- a/kernel/src/sys/apic/lapic.h +++ b/kernel/src/sys/apic/lapic.h @@ -7,17 +7,18 @@ #define LAPIC_BASE_MSR 0x1B // Local APIC Registers -#define LAPIC_ID 0x0020 // Local APIC ID -#define LAPIC_VER 0x0030 // Local APIC Version -#define LAPIC_TPR 0x0080 // Task Priority -#define LAPIC_EOI 0x00B0 // End of Interrupt -#define LAPIC_SVR 0x00F0 // Spurious Interrupt Vector -#define LAPIC_ESR 0x0280 // Error Status -#define LAPIC_ICRLO 0x0300 // Interrupt Command (Low) -#define LAPIC_ICRHI 0x0310 // Interrupt Command (High) -#define LAPIC_TIMER 0x0320 // LVT Timer -#define LAPIC_TICR 0x0380 // Timer Initial Count -#define LAPIC_TDCR 0x03E0 // Timer Divide Configuration +#define LAPIC_ID 0x0020 // Local APIC ID +#define LAPIC_VER 0x0030 // Local APIC Version +#define LAPIC_TPR 0x0080 // Task Priority +#define LAPIC_EOI 0x00B0 // End of Interrupt +#define LAPIC_SVR 0x00F0 // Spurious Interrupt Vector +#define LAPIC_ESR 0x0280 // Error Status +#define LAPIC_ICRLO 0x0300 // Interrupt Command (Low) +#define LAPIC_ICRHI 0x0310 // Interrupt Command (High) +#define LAPIC_LVT_TIMER 0x0320 // LVT Timer +#define LAPIC_LVT_LINT0 0x350 // LINT0 +#define LAPIC_TICR 0x0380 // Timer Initial Count +#define LAPIC_TDCR 0x03E0 // Timer Divide Configuration // ICR Fields #define ICR_FIXED 0x00000000 @@ -30,8 +31,7 @@ #define ICR_SEND_PENDING 0x00001000 #define ICR_DESTINATION_SHIFT 24 -// Startup vector address (must be in first 1MB, page-aligned) -#define AP_BOOT_ADDRESS 0x8000 // Example: 32KB physical address +#define LAPIC_SPURIOUS_VECTOR 0xFF extern uint64_t lapic_addr;