From 5fc4c7b5467e5641a341c83f170daafd0a93538a Mon Sep 17 00:00:00 2001 From: keyboard-slayer Date: Fri, 15 Dec 2023 22:31:51 +0100 Subject: [PATCH] feat: added ACPI, APIC and HPET --- src/kernel/archs/x86_64/acpi.c | 2 +- src/kernel/archs/x86_64/apic.c | 197 +++++++++++++++++++++++++++++++++ src/kernel/archs/x86_64/apic.h | 115 +++++++++++++++++++ src/kernel/archs/x86_64/asm.c | 20 ++++ src/kernel/archs/x86_64/asm.h | 27 ++++- src/kernel/archs/x86_64/hpet.c | 45 ++++++++ src/kernel/archs/x86_64/hpet.h | 38 +++++++ src/kernel/archs/x86_64/mod.c | 7 +- 8 files changed, 448 insertions(+), 3 deletions(-) create mode 100644 src/kernel/archs/x86_64/apic.c create mode 100644 src/kernel/archs/x86_64/apic.h create mode 100644 src/kernel/archs/x86_64/hpet.c create mode 100644 src/kernel/archs/x86_64/hpet.h diff --git a/src/kernel/archs/x86_64/acpi.c b/src/kernel/archs/x86_64/acpi.c index 930611e..504bffd 100644 --- a/src/kernel/archs/x86_64/acpi.c +++ b/src/kernel/archs/x86_64/acpi.c @@ -1,5 +1,5 @@ -#include #include +#include #include #include "acpi.h" diff --git a/src/kernel/archs/x86_64/apic.c b/src/kernel/archs/x86_64/apic.c new file mode 100644 index 0000000..fa457f5 --- /dev/null +++ b/src/kernel/archs/x86_64/apic.c @@ -0,0 +1,197 @@ +#include +#include + +#include "apic.h" +#include "asm.h" +#include "hpet.h" + +static Madt *madt = NULL; + +/* --- Lapic --------------------------------------------------------------- */ + +static uint32_t lapic_read(uint32_t reg) +{ + return *((volatile uint32_t *)(hal_mmap_l2h(madt->local_controller_address) + reg)); +} + +static void lapic_write(uint32_t reg, uint32_t value) +{ + *((volatile uint32_t *)(hal_mmap_l2h(madt->local_controller_address) + reg)) = value; +} + +void lapic_timer_start(void) +{ + lapic_write(LAPIC_REG_TIMER_DIV, APIC_TIMER_DIVIDE_BY_16); + lapic_write(LAPIC_REG_TIMER_INITCNT, 0xFFFFFFFF); + + hpet_sleep(10); + + lapic_write(LAPIC_REG_LVT_TIMER, LAPIC_TIMER_MASKED); + + uint32_t tick_in_10ms = 0xFFFFFFFF - lapic_read(LAPIC_REG_TIMER_CURRCNT); + + lapic_write(LAPIC_REG_LVT_TIMER, LAPIC_TIMER_IRQ | LAPIC_TIMER_PERIODIC); + lapic_write(LAPIC_REG_TIMER_DIV, APIC_TIMER_DIVIDE_BY_16); + lapic_write(LAPIC_REG_TIMER_INITCNT, tick_in_10ms / 10); +} + +static void lapic_enable(void) +{ + asm_write_msr(MSR_APIC, (asm_read_msr(MSR_APIC) | LAPIC_ENABLE) & ~((1 << 10))); + lapic_write(LAPIC_REG_SPURIOUS, lapic_read(LAPIC_REG_SPURIOUS) | (LAPIC_SPURIOUS_ALL | LAPIC_SPURIOUS_ENABLE_APIC)); + + lapic_timer_start(); +} + +void lapic_eoi(void) +{ + if (madt == NULL) + { + return; + } + + lapic_write(LAPIC_REG_EOI, 0); +} + +int lapic_id(void) +{ + if (madt == NULL) + { + return -1; + } + + return lapic_read(LAPIC_CPU_ID) >> 24; +} + +/* --- Ioapic --------------------------------------------------------------- */ + +static void ioapic_write(MadtIoapic *io_apic, uint32_t reg, uint32_t value) +{ + uintptr_t base = (uintptr_t)hal_mmap_l2h(io_apic->ioapic_addr); + *(volatile uint32_t *)base = reg; + *(volatile uint32_t *)(base + 16) = value; +} + +static uint32_t ioapic_read(MadtIoapic *ioapic, uint32_t reg) +{ + uintptr_t base = (uintptr_t)hal_mmap_l2h(ioapic->ioapic_addr); + *(volatile uint32_t *)(base) = reg; + return *(volatile uint32_t *)(base + 0x10); +} + +static void ioapic_redirect_legacy(void) +{ + for (size_t i = 0; i < 16; i++) + { + ioapic_redirect_irq(0, i + 32, i); + } +} + +static MadtIso *madt_get_iso_irq(uint8_t irq) +{ + size_t i = 0; + while (i < madt->header.length - sizeof(Madt)) + { + MadtEntry *entry = (MadtEntry *)madt->entries + i; + + if (entry->type == 2) + { + MadtIso *iso = (MadtIso *)entry; + if (iso->irq_src == irq) + { + return iso; + } + } + + i += max$(2, entry->length); + } + + return NULL; +} + +static size_t ioapic_gsi_count(MadtIoapic *ioapic) +{ + uint32_t val = ioapic_read(ioapic, 1); + IoapicVer *ver = (IoapicVer *)&val; + return ver->max_redirect; +} + +MadtIoapic *madt_get_ioapic_from_gsi(uint32_t gsi) +{ + size_t i = 0; + MadtEntry *entry; + while (i < madt->header.length - sizeof(Madt)) + { + entry = (MadtEntry *)(madt->entries + i); + + if (entry->type == 1) + { + MadtIoapic *ioapic = (MadtIoapic *)entry; + + if (gsi >= ioapic->gsib && gsi < ioapic->gsib + ioapic_gsi_count(ioapic)) + { + return ioapic; + } + } + + i += max$(2, entry->length); + } + + return NULL; +} + +static void ioapic_set_gsi_redirect(uint32_t lapic_id, uint8_t intno, uint8_t gsi, uint16_t flags) +{ + uint32_t io_redirect_table; + IoapicRedirect redirect = {0}; + MadtIoapic *ioapic = madt_get_ioapic_from_gsi(gsi); + + if (ioapic == NULL) + { + return; + } + + redirect.vector = intno; + + if (flags & IOAPIC_ACTIVE_HIGH_LOW) + { + redirect.polarity = 1; + } + + if (flags & IOAPIC_TRIGGER_EDGE_LOW) + { + redirect.trigger = 1; + } + + redirect.dest_id = lapic_id; + + io_redirect_table = (gsi - ioapic->gsib) * 2 + 16; + ioapic_write(ioapic, io_redirect_table, (uint32_t)redirect._raw.low_byte); + ioapic_write(ioapic, io_redirect_table + 1, (uint32_t)redirect._raw.high_byte); +} + +void ioapic_redirect_irq(uint32_t lapic_id, uint8_t intno, uint8_t irq) +{ + MadtIso *iso = madt_get_iso_irq(irq); + if (iso != NULL) + { + ioapic_set_gsi_redirect(lapic_id, intno, iso->gsi, iso->flags); + } + else + { + ioapic_set_gsi_redirect(lapic_id, intno, irq, 0); + } +} + +Res apic_init(void) +{ + madt = (Madt *)try$(acpi_parse_sdt("APIC")); + lapic_enable(); + ioapic_redirect_legacy(); + + hal_enable_interrupts(); + + log$("APIC initialised"); + + return ok$(); +} \ No newline at end of file diff --git a/src/kernel/archs/x86_64/apic.h b/src/kernel/archs/x86_64/apic.h new file mode 100644 index 0000000..a6f1d18 --- /dev/null +++ b/src/kernel/archs/x86_64/apic.h @@ -0,0 +1,115 @@ +#pragma once + +#include "acpi.h" + +#define IPI_RESCHED (100) +#define IPI_STOP (101) +#define LAPIC_ENABLE (0x800) +#define LAPIC_SPURIOUS_ALL 0xff +#define LAPIC_SPURIOUS_ENABLE_APIC 0x100 +#define LAPIC_ICR_CPUID_OFFSET 24 +#define LAPIC_ICR_CLEAR_INIT_LEVEL (1 << 14) +#define LAPIC_ICR_DEST_INIT (5 << 8) +#define LAPIC_ICR_DEST_SEND_IPI (6 << 8) +#define IOAPIC_REG_OFFSET (0) +#define IOAPIC_VALUE_OFFSET (16) +#define LAPIC_TIMER_IRQ (32) +#define LAPIC_TIMER_PERIODIC (0x20000) +#define LAPIC_TIMER_MASKED (0x10000) +#define IOAPIC_ACTIVE_HIGH_LOW (1 << 1) +#define IOAPIC_TRIGGER_EDGE_LOW (1 << 3) + +enum lapic_reg +{ + LAPIC_CPU_ID = 0x20, + LAPIC_REG_EOI = 0x0b0, + LAPIC_REG_SPURIOUS = 0x0f0, + LAPIC_REG_ICR0 = 0x300, + LAPIC_REG_ICR1 = 0x310, + LAPIC_REG_LVT_TIMER = 0x320, + LAPIC_REG_TIMER_INITCNT = 0x380, + LAPIC_REG_TIMER_CURRCNT = 0x390, + LAPIC_REG_TIMER_DIV = 0x3e0, +}; + +enum apic_timer_division +{ + APIC_TIMER_DIVIDE_BY_2 = 0, + APIC_TIMER_DIVIDE_BY_4 = 1, + APIC_TIMER_DIVIDE_BY_8 = 2, + APIC_TIMER_DIVIDE_BY_16 = 3, + APIC_TIMER_DIVIDE_BY_32 = 4, + APIC_TIMER_DIVIDE_BY_64 = 5, + APIC_TIMER_DIVIDE_BY_128 = 6, + APIC_TIMER_DIVIDE_BY_1 = 7 +}; + +typedef struct [[gnu::packed]] +{ + SdtHeader header; + uint32_t local_controller_address; + uint32_t flags; + uint8_t entries[]; +} Madt; + +typedef struct [[gnu::packed]] +{ + uint8_t type; + uint8_t length; +} MadtEntry; + +typedef struct [[gnu::packed]] +{ + MadtEntry header; + uint8_t ioapic_id; + uint8_t _reserved; + uint32_t ioapic_addr; + uint32_t gsib; +} MadtIoapic; + +typedef struct [[gnu::packed]] +{ + uint8_t version; + uint8_t reserved; + uint8_t max_redirect; + uint8_t reserved2; +} IoapicVer; + +typedef union [[gnu::packed]] +{ + struct [[gnu::packed]] + { + uint8_t vector; + uint8_t delivery_mode : 3; + uint8_t dest_mode : 1; + uint8_t delivery_status : 1; + uint8_t polarity : 1; + uint8_t remote_irr : 1; + uint8_t trigger : 1; + uint8_t mask : 1; + uint8_t reserved : 7; + uint8_t dest_id; + }; + + struct [[gnu::packed]] + { + uint32_t low_byte; + uint32_t high_byte; + } _raw; +} IoapicRedirect; + +typedef struct [[gnu::packed]] +{ + MadtEntry header; + uint8_t bus_src; + uint8_t irq_src; + uint32_t gsi; + uint16_t flags; +} MadtIso; + +Res apic_init(void); +void lapic_eoi(void); +int lapic_id(void); +void ioapic_redirect_irq(uint32_t lapic_id, uint8_t intno, uint8_t irq); +void lapic_timer_start(void); +void lapic_timer_stop(void); \ No newline at end of file diff --git a/src/kernel/archs/x86_64/asm.c b/src/kernel/archs/x86_64/asm.c index a8b4c4f..6569a9a 100644 --- a/src/kernel/archs/x86_64/asm.c +++ b/src/kernel/archs/x86_64/asm.c @@ -1,3 +1,5 @@ +#include "asm.h" + void hal_disable_interrupts(void) { asm volatile("cli"); @@ -17,3 +19,21 @@ void hal_panic(void) { asm volatile("int $1"); } + +void asm_write_msr(uint64_t msr, uint64_t value) +{ + uint32_t low = value & 0xFFFFFFFF; + uint32_t high = value >> 32; + __asm__ volatile("wrmsr" ::"c"((uint64_t)msr), "a"(low), "d"(high)); +} + +uint64_t asm_read_msr(uint64_t msr) +{ + uint32_t low, high; + __asm__ volatile("rdmsr" + : "=a"(low), "=d"(high) + : "c"((uint64_t)msr)); + return ((uint64_t)high << 32) | low; +} + + diff --git a/src/kernel/archs/x86_64/asm.h b/src/kernel/archs/x86_64/asm.h index bfc77bb..2fd5635 100644 --- a/src/kernel/archs/x86_64/asm.h +++ b/src/kernel/archs/x86_64/asm.h @@ -1,8 +1,33 @@ #pragma once +#include + #define asm_read_cr(n, reg) asm volatile("mov %%cr" #n ", %0" \ : "=r"(reg)) #define asm_write_cr(n, reg) asm volatile("mov %0, %%cr" #n \ : \ - : "r"(reg)) \ No newline at end of file + : "r"(reg)) + +enum msr_registers +{ + MSR_APIC = 0x1B, + MSR_EFER = 0xC0000080, + MSR_STAR = 0xC0000081, + MSR_LSTAR = 0xC0000082, + MSR_COMPAT_STAR = 0xC0000083, + MSR_SYSCALL_FLAG_MASK = 0xC0000084, + MSR_FS_BASE = 0xC0000100, + MSR_GS_BASE = 0xC0000101, + MSR_KERN_GS_BASE = 0xc0000102, +}; + +enum msr_star_reg +{ + STAR_KCODE_OFFSET = 32, + STAR_UCODE_OFFSET = 48, +}; + +void asm_write_msr(uint64_t msr, uint64_t value); + +uint64_t asm_read_msr(uint64_t msr); diff --git a/src/kernel/archs/x86_64/hpet.c b/src/kernel/archs/x86_64/hpet.c new file mode 100644 index 0000000..2295d01 --- /dev/null +++ b/src/kernel/archs/x86_64/hpet.c @@ -0,0 +1,45 @@ +#include +#include + +#include "hpet.h" + +static uintptr_t hpet_base = 0; +static size_t hpet_tick = 0; + +static void hpet_write(uint32_t reg, uint64_t value) +{ + *(volatile uint64_t *)(hpet_base + reg) = value; +} + +static uint64_t hpet_read(uint32_t reg) +{ + return *(volatile uint64_t *)(hpet_base + reg); +} + +Res hpet_init(void) +{ + AcpiHpet *hpet = (AcpiHpet *)try$(acpi_parse_sdt("HPET")); + hpet_base = hal_mmap_l2h(hpet->address); + + if (hpet->address_space_id == HPET_ADDRESS_SPACE_IO) + { + err$(RES_NOENT); + } + + hpet_tick = hpet_read(HPET_GENERAL_CAPABILITIES) >> HPET_CAP_COUNTER_CLOCK_OFFSET; + + hpet_write(HPET_GENERAL_CONFIGUATION, HPET_CONF_TURN_OFF); + hpet_write(HPET_MAIN_COUNTER_VALUE, 0); + hpet_write(HPET_GENERAL_CONFIGUATION, HPET_CONF_TURN_ON); + + log$("Hpet initialised"); + + return ok$(); +} + +void hpet_sleep(int ms) +{ + uint64_t target = hpet_read(HPET_MAIN_COUNTER_VALUE) + (ms * 1000000000000) / hpet_tick; + while (hpet_read(HPET_MAIN_COUNTER_VALUE) <= target) + ; +} \ No newline at end of file diff --git a/src/kernel/archs/x86_64/hpet.h b/src/kernel/archs/x86_64/hpet.h new file mode 100644 index 0000000..8a9ff4a --- /dev/null +++ b/src/kernel/archs/x86_64/hpet.h @@ -0,0 +1,38 @@ +#pragma once + +#include "acpi.h" + +#define HPET_ADDRESS_SPACE_MEMORY 0 +#define HPET_ADDRESS_SPACE_IO 1 + +#define HPET_CAP_COUNTER_CLOCK_OFFSET (32) + +#define HPET_CONF_TURN_ON (1) +#define HPET_CONF_TURN_OFF (0) + +enum hpet_registers +{ + HPET_GENERAL_CAPABILITIES = 0, + HPET_GENERAL_CONFIGUATION = 16, + HPET_MAIN_COUNTER_VALUE = 240, +}; + +typedef struct [[gnu::packed]] +{ + SdtHeader header; + + uint8_t hardware_rev_id; + uint8_t info; + uint16_t pci_vendor_id; + uint8_t address_space_id; + uint8_t register_bit_width; + uint8_t register_bit_offset; + uint8_t reserved1; + uint64_t address; + uint8_t hpet_number; + uint16_t minimum_tick; + uint8_t page_protection; +} AcpiHpet; + +Res hpet_init(void); +void hpet_sleep(int ms); \ No newline at end of file diff --git a/src/kernel/archs/x86_64/mod.c b/src/kernel/archs/x86_64/mod.c index 914be74..64a9af5 100644 --- a/src/kernel/archs/x86_64/mod.c +++ b/src/kernel/archs/x86_64/mod.c @@ -6,6 +6,8 @@ #include "gdt.h" #include "idt.h" #include "paging.h" +#include "hpet.h" +#include "apic.h" Stream hal_dbg_stream(void) { @@ -18,7 +20,10 @@ Res hal_setup(void) { gdt_init(); idt_init(); - paging_init(); + try$(paging_init()); acpi_init(); + try$(hpet_init()); + try$(apic_init()); + return ok$(); } \ No newline at end of file