diff --git a/.vscode/settings.json b/.vscode/settings.json index 09e2812..8ecaea8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,6 +20,7 @@ "serial.h": "c", "kpanic.h": "c", "smp.h": "c", - "string_view": "c" + "string_view": "c", + "lapic.h": "c" } } \ No newline at end of file diff --git a/credits.txt b/credits.txt new file mode 100644 index 0000000..85e89bf --- /dev/null +++ b/credits.txt @@ -0,0 +1 @@ +- Thanks to Raph and https://github.com/SILD-Project/soaplin for most APIC related code \ No newline at end of file diff --git a/kernel/src/arch/idt.c b/kernel/src/arch/idt.c index 80f1e9e..cb515b2 100644 --- a/kernel/src/arch/idt.c +++ b/kernel/src/arch/idt.c @@ -5,6 +5,11 @@ #include #include #include +#include +#include +#include +#include +#include struct idt_entry __attribute__((aligned(16))) idt_descriptor[256] = {0}; idt_intr_handler real_handlers[256] = {0}; @@ -58,6 +63,8 @@ int idt_register_handler(size_t vector, idt_intr_handler handler) if (real_handlers[vector] != idt_default_interrupt_handler) { real_handlers[vector] = handler; + if (vector <= 14) + ioapic_redirect_irq(bootstrap_lapic_id, vector + 32, vector, false); return 0; } return 1; diff --git a/kernel/src/emk.c b/kernel/src/emk.c index 7f2269d..febdd3f 100644 --- a/kernel/src/emk.c +++ b/kernel/src/emk.c @@ -20,6 +20,9 @@ #include #include #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 = { @@ -58,6 +61,12 @@ struct limine_mp_response *mp_response = NULL; struct flanterm_context *ft_ctx = NULL; #endif // FLANTERM_SUPPORT +void tick(struct register_ctx *) +{ + log_early("tick"); + lapic_eoi(); +} + void emk_entry(void) { __asm__ volatile("movq %%rsp, %0" : "=r"(kstack_top)); @@ -168,15 +177,6 @@ void emk_entry(void) kfree(c); log_early("Initialized kernel heap"); - /* Setup SMP */ - if (!mp_request.response) - { - kpanic(NULL, "Failed to get MP request"); - } - - mp_response = mp_request.response; - smp_init(); - /* Setup ACPI and APIC */ rsdp_response = rsdp_request.response; if (!rsdp_response) @@ -188,7 +188,24 @@ void emk_entry(void) /* Setup MADT */ madt_init(); - log_early("Initialized APIC"); + ioapic_init(); + log_early("Initialized IOAPIC"); + lapic_init(); + log_early("Initialized LAPIC"); + + /* Setup timer */ + pit_init(tick); + log_early("Initialized Timer"); + + /* Setup SMP */ + if (!mp_request.response) + { + kpanic(NULL, "Failed to get MP request"); + } + + mp_response = mp_request.response; + smp_init(); + log_early("Initialized SMP"); hlt(); } \ No newline at end of file diff --git a/kernel/src/sys/ioapic.c b/kernel/src/sys/ioapic.c new file mode 100644 index 0000000..973cd10 --- /dev/null +++ b/kernel/src/sys/ioapic.c @@ -0,0 +1,128 @@ +/* EMK 1.0 Copyright (c) 2025 Piraterna */ +#include +#include + +void ioapic_init() +{ + acpi_madt_ioapic_t *ioapic = madt_ioapic_list[0]; + + uint32_t val = ioapic_read(ioapic, IOAPIC_VER); + uint32_t count = ((val >> 16) & 0xFF); + + if ((ioapic_read(ioapic, 0) >> 24) != ioapic->ioapic_id) + { + return; + } + + for (uint8_t i = 0; i <= count; ++i) + { + ioapic_write(ioapic, IOAPIC_REDTBL + 2 * i, 0x00010000 | (32 + i)); + ioapic_write(ioapic, IOAPIC_REDTBL + 2 * i + 1, 0); + } +} + +void ioapic_write(acpi_madt_ioapic_t *ioapic, uint8_t reg, uint32_t val) +{ + *((volatile uint32_t *)(HIGHER_HALF(ioapic->ioapic_addr) + IOAPIC_REGSEL)) = + reg; + *((volatile uint32_t *)(HIGHER_HALF(ioapic->ioapic_addr) + IOAPIC_IOWIN)) = val; +} + +uint32_t ioapic_read(acpi_madt_ioapic_t *ioapic, uint8_t reg) +{ + *((volatile uint32_t *)(HIGHER_HALF(ioapic->ioapic_addr) + IOAPIC_REGSEL)) = + reg; + return *( + (volatile uint32_t *)(HIGHER_HALF(ioapic->ioapic_addr) + IOAPIC_IOWIN)); +} + +void ioapic_set_entry(acpi_madt_ioapic_t *ioapic, uint8_t idx, uint64_t data) +{ + ioapic_write(ioapic, (uint8_t)(IOAPIC_REDTBL + idx * 2), (uint32_t)data); + ioapic_write(ioapic, (uint8_t)(IOAPIC_REDTBL + idx * 2 + 1), + (uint32_t)(data >> 32)); +} + +uint64_t ioapic_gsi_count(acpi_madt_ioapic_t *ioapic) +{ + return (ioapic_read(ioapic, 1) & 0xff0000) >> 16; +} + +acpi_madt_ioapic_t *ioapic_get_gsi(uint32_t gsi) +{ + for (uint64_t i = 0; i < madt_iso_len; i++) + { + acpi_madt_ioapic_t *ioapic = madt_ioapic_list[i]; + if (ioapic->gsi_base <= gsi && + ioapic->gsi_base + ioapic_gsi_count(ioapic) > gsi) + return ioapic; + } + return (acpi_madt_ioapic_t *)0; +} + +void ioapic_redirect_gsi(uint32_t lapic_id, uint8_t vec, uint32_t gsi, + uint16_t flags, bool mask) +{ + acpi_madt_ioapic_t *ioapic = ioapic_get_gsi(gsi); + + uint64_t redirect = vec; + + if ((flags & (1 << 1)) != 0) + { + redirect |= (1 << 13); + } + + if ((flags & (1 << 3)) != 0) + { + redirect |= (1 << 15); + } + + if (mask) + redirect |= (1 << 16); + else + redirect &= ~(1 << 16); + + redirect |= (uint64_t)lapic_id << 56; + + uint32_t redir_table = (gsi - ioapic->gsi_base) * 2 + 16; + ioapic_write(ioapic, redir_table, (uint32_t)redirect); + ioapic_write(ioapic, redir_table + 1, (uint32_t)(redirect >> 32)); +} + +void ioapic_redirect_irq(uint32_t lapic_id, uint8_t vec, uint8_t irq, + bool mask) +{ + uint8_t idx = 0; + acpi_madt_ioapic_src_ovr_t *iso = (acpi_madt_ioapic_src_ovr_t *)0; + + while (idx < madt_iso_len) + { + iso = madt_iso_list[idx]; + if (iso->irq_source == irq) + { + ioapic_redirect_gsi(lapic_id, vec, iso->gsi, iso->flags, mask); + return; + } + idx++; + } + + ioapic_redirect_gsi(lapic_id, vec, irq, 0, mask); +} + +uint32_t ioapic_get_redirect_irq(uint8_t irq) +{ + uint8_t idx = 0; + acpi_madt_ioapic_src_ovr_t *iso; + + while (idx < madt_iso_len) + { + iso = madt_iso_list[idx]; + if (iso->irq_source == irq) + { + return iso->gsi; + } + idx++; + } + + return irq; +} \ No newline at end of file diff --git a/kernel/src/sys/ioapic.h b/kernel/src/sys/ioapic.h new file mode 100644 index 0000000..587ff64 --- /dev/null +++ b/kernel/src/sys/ioapic.h @@ -0,0 +1,30 @@ +/* EMK 1.0 Copyright (c) 2025 Piraterna */ +#ifndef IOAPIC_H +#define IOAPIC_H + +// Credits: https://github.com/SILD-Project/soaplin/blob/main/kernel/src/dev/ioapic.h + +#include +#include +#include + +#define IOAPIC_REGSEL 0x0 +#define IOAPIC_IOWIN 0x10 + +#define IOAPIC_ID 0x0 +#define IOAPIC_VER 0x01 +#define IOAPIC_ARB 0x02 +#define IOAPIC_REDTBL 0x10 + +void ioapic_write(acpi_madt_ioapic_t *ioapic, uint8_t reg, uint32_t val); +uint32_t ioapic_read(acpi_madt_ioapic_t *ioapic, uint8_t reg); + +void ioapic_redirect_irq(uint32_t lapic_id, uint8_t vec, uint8_t irq, + bool mask); +uint32_t ioapic_get_redirect_irq(uint8_t irq); + +void ioapic_set_entry(acpi_madt_ioapic_t *ioapic, uint8_t idx, uint64_t data); + +void ioapic_init(); + +#endif // IOAPIC_H \ No newline at end of file diff --git a/kernel/src/sys/lapic.c b/kernel/src/sys/lapic.c new file mode 100644 index 0000000..77b8ab5 --- /dev/null +++ b/kernel/src/sys/lapic.c @@ -0,0 +1,66 @@ +/* EMK 1.0 Copyright (c) 2025 Piraterna */ +#include +#include + +uint64_t apic_ticks = 0; + +void lapic_init() +{ + lapic_write(0xf0, 0x1ff); +} + +void lapic_stop_timer() +{ + lapic_write(LAPIC_TIMER_INITCNT, 0); + lapic_write(LAPIC_TIMER_LVT, LAPIC_TIMER_DISABLE); +} + +void lapic_oneshot(uint8_t vec, uint64_t ms) +{ + lapic_stop_timer(); + lapic_write(LAPIC_TIMER_DIV, 0); + lapic_write(LAPIC_TIMER_LVT, vec); + lapic_write(LAPIC_TIMER_INITCNT, apic_ticks * ms); +} + +void lapic_calibrate_timer() +{ + lapic_stop_timer(); + lapic_write(LAPIC_TIMER_DIV, 0); + lapic_write(LAPIC_TIMER_LVT, (1 << 16) | 0xff); + lapic_write(LAPIC_TIMER_INITCNT, 0xFFFFFFFF); + lapic_write(LAPIC_TIMER_LVT, LAPIC_TIMER_DISABLE); + uint32_t ticks = 0xFFFFFFFF - lapic_read(LAPIC_TIMER_CURCNT); + apic_ticks = ticks; + lapic_stop_timer(); +} + +void lapic_write(uint32_t reg, uint32_t val) +{ + *((volatile uint32_t *)(HIGHER_HALF(0xfee00000) + reg)) = val; +} + +uint32_t lapic_read(uint32_t reg) +{ + return *((volatile uint32_t *)(HIGHER_HALF(0xfee00000) + reg)); +} + +void lapic_eoi() { lapic_write((uint8_t)0xb0, 0x0); } + +void lapic_ipi(uint32_t id, uint8_t dat) +{ + lapic_write(LAPIC_ICRHI, id << LAPIC_ICDESTSHIFT); + lapic_write(LAPIC_ICRLO, dat); +} + +void lapic_send_all_int(uint32_t id, uint32_t vec) +{ + lapic_ipi(id, vec | LAPIC_ICRAIS); +} + +void lapic_send_others_int(uint32_t id, uint32_t vec) +{ + lapic_ipi(id, vec | LAPIC_ICRAES); +} + +uint32_t lapic_get_id() { return lapic_read(0x0020) >> LAPIC_ICDESTSHIFT; } \ No newline at end of file diff --git a/kernel/src/sys/lapic.h b/kernel/src/sys/lapic.h new file mode 100644 index 0000000..e22ca20 --- /dev/null +++ b/kernel/src/sys/lapic.h @@ -0,0 +1,56 @@ +/* EMK 1.0 Copyright (c) 2025 Piraterna */ +#ifndef LAPIC_H +#define LAPIC_H + +// Credits: https://github.com/SILD-Project/soaplin/blob/main/kernel/src/dev/lapic.h + +#include + +#define LAPIC_PPR 0x00a0 + +#define LAPIC_ICRLO 0x0300 +#define LAPIC_ICRHI 0x0310 + +#define LAPIC_ICINI 0x0500 +#define LAPIC_ICSTR 0x0600 + +#define LAPIC_ICEDGE 0x0000 + +#define LAPIC_ICPEND 0x00001000 +#define LAPIC_ICPHYS 0x00000000 +#define LAPIC_ICASSR 0x00004000 +#define LAPIC_ICSHRTHND 0x00000000 +#define LAPIC_ICDESTSHIFT 24 + +#define LAPIC_ICRAIS 0x00080000 +#define LAPIC_ICRAES 0x000c0000 + +// Timer +#define LAPIC_TIMER_DIV 0x3E0 +#define LAPIC_TIMER_INITCNT 0x380 +#define LAPIC_TIMER_LVT 0x320 +#define LAPIC_TIMER_DISABLE 0x10000 +#define LAPIC_TIMER_CURCNT 0x390 +#define LAPIC_TIMER_PERIODIC 0x20000 + +void lapic_init(); + +void lapic_stop_timer(); +void lapic_oneshot(uint8_t vec, uint64_t ms); +void lapic_calibrate_timer(); + +void lapic_write(uint32_t reg, uint32_t val); +uint32_t lapic_read(uint32_t reg); + +void lapic_eoi(); + +void lapic_ipi(uint32_t id, uint8_t dat); + +void lapic_send_all_int(uint32_t id, uint32_t vec); +void lapic_send_others_int(uint32_t id, uint32_t vec); + +void lapic_init_cpu(uint32_t id); +void lapic_start_cpu(uint32_t id, uint32_t vec); +uint32_t lapic_get_id(); + +#endif // LAPIC_H \ No newline at end of file diff --git a/kernel/src/sys/pit.c b/kernel/src/sys/pit.c new file mode 100644 index 0000000..db3a2b8 --- /dev/null +++ b/kernel/src/sys/pit.c @@ -0,0 +1,24 @@ +/* EMK 1.0 Copyright (c) 2025 Piraterna */ +#include +#include +#include + +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(void (*callback)(struct register_ctx *ctx)) +{ + if (callback) + pit_callback = callback; + outb(0x43, 0x36); + idt_register_handler(IDT_IRQ_BASE + 0, pit_handler); + uint16_t divisor = 5966; // ~200Hz + outb(0x40, divisor & 0xFF); + outb(0x40, (divisor >> 8) & 0xFF); +} \ No newline at end of file diff --git a/kernel/src/sys/pit.h b/kernel/src/sys/pit.h new file mode 100644 index 0000000..b05d9ed --- /dev/null +++ b/kernel/src/sys/pit.h @@ -0,0 +1,9 @@ +/* EMK 1.0 Copyright (c) 2025 Piraterna */ +#ifndef PIT_H +#define PIT_H + +#include + +void pit_init(void (*callback)(struct register_ctx *ctx)); + +#endif // PIT_H \ No newline at end of file