From 7b33d0757af58d3cff404fcd3c1e168e13e37710 Mon Sep 17 00:00:00 2001 From: kamkow1 Date: Mon, 22 Dec 2025 19:36:43 +0100 Subject: [PATCH] APIC, HPET, virtual memory --- aux/qemu_amd64.sh | 3 + kernel/amd64/apic.c | 158 +++++++++++++++++++++++++++++++++ kernel/amd64/apic.h | 12 +++ kernel/amd64/bootmain.c | 11 +++ kernel/amd64/hpet.c | 73 ++++++++++++++++ kernel/amd64/hpet.h | 10 +++ kernel/amd64/mm.c | 163 +++++++++++++++++++++++++++++++++++ kernel/amd64/mm.h | 8 ++ kernel/amd64/msr.c | 14 +++ kernel/amd64/msr.h | 9 ++ kernel/amd64/src.mk | 12 ++- kernel/sys/mm.h | 16 ++++ kernel/uACPI/platform_mop3.c | 9 +- 13 files changed, 495 insertions(+), 3 deletions(-) create mode 100755 aux/qemu_amd64.sh create mode 100644 kernel/amd64/apic.c create mode 100644 kernel/amd64/apic.h create mode 100644 kernel/amd64/hpet.c create mode 100644 kernel/amd64/hpet.h create mode 100644 kernel/amd64/mm.c create mode 100644 kernel/amd64/msr.c create mode 100644 kernel/amd64/msr.h diff --git a/aux/qemu_amd64.sh b/aux/qemu_amd64.sh new file mode 100755 index 0000000..5e627a1 --- /dev/null +++ b/aux/qemu_amd64.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +qemu-system-x86_64 -M q35 -m 4G -serial mon:stdio -cdrom mop3.iso diff --git a/kernel/amd64/apic.c b/kernel/amd64/apic.c new file mode 100644 index 0000000..e752095 --- /dev/null +++ b/kernel/amd64/apic.c @@ -0,0 +1,158 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IOAPICS_MAX 24 +#define INTERRUPT_SRC_OVERRIDES_MAX 24 + +static struct acpi_madt_ioapic apics[IOAPICS_MAX]; +/* clang-format off */ +static struct acpi_madt_interrupt_source_override intr_src_overrides[INTERRUPT_SRC_OVERRIDES_MAX]; +/* clang-format on */ +static size_t ioapic_entries = 0; +static size_t intr_src_override_entries = 0; + +extern void amd64_spin (void); + +static uint32_t amd64_ioapic_read (uintptr_t vaddr, uint32_t reg) { + *(volatile uint32_t*)vaddr = reg; + return *(volatile uint32_t*)(vaddr + 0x10); +} + +static void amd64_ioapic_write (uintptr_t vaddr, uint32_t reg, uint32_t value) { + *(volatile uint32_t*)vaddr = reg; + *(volatile uint32_t*)(vaddr + 0x10) = value; +} + +static struct acpi_madt_ioapic* amd64_ioapic_find (uint8_t irq) { + struct acpi_madt_ioapic* apic = NULL; + struct limine_hhdm_response* hhdm = limine_hhdm_request.response; + + for (size_t i = 0; i < ioapic_entries; i++) { + apic = &apics[i]; + uint32_t version = amd64_ioapic_read ( + (uintptr_t)hhdm->offset + (uintptr_t)apic->address, 1); + uint32_t max = (version >> 16); + + if ((apic->gsi_base <= irq) && ((apic->gsi_base + max) > irq)) + break; + } + + return apic; +} + +void amd64_ioapic_route_irq (uint8_t vec, uint8_t irq, uint64_t flags, + uint64_t lapic_id) { + struct acpi_madt_ioapic* apic; + struct acpi_madt_interrupt_source_override* override; + bool found_override = false; + struct limine_hhdm_response* hhdm = limine_hhdm_request.response; + + for (size_t i = 0; i < intr_src_override_entries; i++) { + override = &intr_src_overrides[i]; + if (override->source == irq) { + found_override = true; + break; + } + } + + uint64_t calc_flags = (lapic_id << 56) | (flags) | (vec & 0xFF); + + if (found_override) { + uint8_t polarity = ((override->flags & 0x03) == 0x03) ? 1 : 0; + uint8_t mode = (((override->flags >> 2) & 0x03) == 0x03) ? 1 : 0; + calc_flags = (lapic_id << 56) | (mode << 15) | (polarity << 14) | + (vec & 0xFF) | flags; + } + + apic = amd64_ioapic_find (irq); + + if (apic == NULL) + return; + + uint32_t irq_reg = ((irq - apic->gsi_base) * 2) + 0x10; + + amd64_ioapic_write ((uintptr_t)hhdm->offset + (uintptr_t)apic->address, + irq_reg, (uint32_t)calc_flags); + + amd64_ioapic_write ((uintptr_t)hhdm->offset + (uintptr_t)apic->address, + irq_reg + 1, (uint32_t)(calc_flags >> 32)); +} + +void amd64_ioapic_mask (uint8_t irq) { + struct acpi_madt_ioapic* apic; + struct limine_hhdm_response* hhdm = limine_hhdm_request.response; + + apic = amd64_ioapic_find (irq); + + if (apic == NULL) + return; + + uint32_t irq_reg = ((irq - apic->gsi_base) * 2) + 0x10; + + uint32_t value = amd64_ioapic_read ( + (uintptr_t)hhdm->offset + (uintptr_t)apic->address, irq_reg); + amd64_ioapic_write ((uintptr_t)hhdm->offset + (uintptr_t)apic->address, + irq_reg, value | (1 << 16)); +} + +void amd64_ioapic_unmask (uint8_t irq) { + struct acpi_madt_ioapic* apic; + struct limine_hhdm_response* hhdm = limine_hhdm_request.response; + + apic = amd64_ioapic_find (irq); + + if (apic == NULL) + return; + + uint32_t irq_reg = ((irq - apic->gsi_base) * 2) + 0x10; + + uint32_t value = amd64_ioapic_read ( + (uintptr_t)hhdm->offset + (uintptr_t)apic->address, irq_reg); + amd64_ioapic_write ((uintptr_t)hhdm->offset + (uintptr_t)apic->address, + irq_reg, value & ~(1 << 16)); +} + +void amd64_ioapic_init (void) { + struct limine_hhdm_response* hhdm = limine_hhdm_request.response; + + struct uacpi_table apic_table; + uacpi_status status = + uacpi_table_find_by_signature (ACPI_MADT_SIGNATURE, &apic_table); + if (status != UACPI_STATUS_OK) { + DEBUG ("Could not find MADT table!\n"); + amd64_spin (); + } + + struct acpi_madt* apic = (struct acpi_madt*)apic_table.virt_addr; + struct acpi_entry_hdr* current = (struct acpi_entry_hdr*)apic->entries; + + for (;;) { + if ((uintptr_t)current >= ((uintptr_t)apic->entries + apic->hdr.length - + sizeof (struct acpi_madt))) + break; + + switch (current->type) { + case ACPI_MADT_ENTRY_TYPE_IOAPIC: { + struct acpi_madt_ioapic* ioapic = (struct acpi_madt_ioapic*)current; + mm_map_kernel_page ((uintptr_t)ioapic->address, + (uintptr_t)hhdm->offset + (uintptr_t)ioapic->address, + MM_PG_USER | MM_PG_RW); + apics[ioapic_entries++] = *ioapic; + } break; + case ACPI_MADT_ENTRY_TYPE_INTERRUPT_SOURCE_OVERRIDE: { + struct acpi_madt_interrupt_source_override* override = + (struct acpi_madt_interrupt_source_override*)current; + intr_src_overrides[intr_src_override_entries++] = *override; + } break; + } + + current = (struct acpi_entry_hdr*)((uintptr_t)current + current->length); + } +} diff --git a/kernel/amd64/apic.h b/kernel/amd64/apic.h new file mode 100644 index 0000000..c7593cc --- /dev/null +++ b/kernel/amd64/apic.h @@ -0,0 +1,12 @@ +#ifndef _KERNEL_AMD64_APIC_H +#define _KERNEL_AMD64_APIC_H + +#include + +void amd64_ioapic_route_irq (uint8_t vec, uint8_t irq, uint64_t flags, + uint64_t lapic_id); +void amd64_ioapic_mask (uint8_t irq); +void amd64_ioapic_unmask (uint8_t irq); +void amd64_ioapic_init (void); + +#endif // _KERNEL_AMD64_APIC_H diff --git a/kernel/amd64/bootmain.c b/kernel/amd64/bootmain.c index 0e55a03..022f2b6 100644 --- a/kernel/amd64/bootmain.c +++ b/kernel/amd64/bootmain.c @@ -1,9 +1,12 @@ +#include +#include #include #include #include #include #include #include +#include #include #define UACPI_MEMORY_BUFFER_MAX 4096 @@ -13,14 +16,22 @@ ALIGNED (16) static uint8_t uacpi_memory_buffer[UACPI_MEMORY_BUFFER_MAX]; void bootmain (void) { amd64_init (); pmm_init (); + mm_init (); uacpi_setup_early_table_access ((void*)uacpi_memory_buffer, sizeof (uacpi_memory_buffer)); + amd64_ioapic_init (); + amd64_hpet_init (); + int* a = malloc (sizeof (int)); *a = 6969; DEBUG ("a=%p, *a=%d\n", a, *a); + amd64_hpet_sleep_micro (3000000); + + DEBUG ("woke up!!!\n"); + for (;;) ; } diff --git a/kernel/amd64/hpet.c b/kernel/amd64/hpet.c new file mode 100644 index 0000000..17a4566 --- /dev/null +++ b/kernel/amd64/hpet.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HPET_MCVR 0xF0 /* Main Counter Value Register */ +#define HPET_GCR 0x10 /* General Configuration Register */ +#define HPET_GCIDR 0x00 /* General Capabilities and ID Register */ + +static bool hpet_32bits = 1; +static uintptr_t hpet_paddr; +static uint64_t hpet_clock_nano; + +extern void amd64_spin (void); + +static uint64_t amd64_hpet_read (uint32_t reg) { + struct limine_hhdm_response* hhdm = limine_hhdm_request.response; + uintptr_t hpet_vaddr = hpet_paddr + (uintptr_t)hhdm->offset; + return (hpet_32bits ? *(volatile uint32_t*)(hpet_vaddr + reg) + : *(volatile uint64_t*)(hpet_vaddr + reg)); +} + +static void amd64_hpet_write (uint32_t reg, uint64_t value) { + struct limine_hhdm_response* hhdm = limine_hhdm_request.response; + uintptr_t hpet_vaddr = hpet_paddr + (uintptr_t)hhdm->offset; + if (hpet_32bits) + *(volatile uint32_t*)(hpet_vaddr + reg) = (value & 0xFFFFFFFF); + else + *(volatile uint64_t*)(hpet_vaddr + reg) = value; +} + +static uint64_t amd64_hpet_timestamp (void) { + return amd64_hpet_read (HPET_MCVR); +} + +uint64_t amd64_hpet_current_nano (void) { + return amd64_hpet_timestamp () * hpet_clock_nano; +} + +void amd64_hpet_sleep_micro (uint64_t us) { + uint64_t start = amd64_hpet_timestamp (); + uint64_t conv = us * 1000; + while (((amd64_hpet_timestamp () - start) * hpet_clock_nano) < conv) + __asm__ volatile ("pause" ::: "memory"); +} + +void amd64_hpet_init (void) { + struct uacpi_table hpet_table; + uacpi_status status = + uacpi_table_find_by_signature (ACPI_HPET_SIGNATURE, &hpet_table); + if (status != UACPI_STATUS_OK) { + DEBUG ("Could not find HPET table!\n"); + amd64_spin (); + } + + struct acpi_hpet* hpet = (struct acpi_hpet*)hpet_table.virt_addr; + hpet_paddr = (uintptr_t)hpet->address.address; + + struct limine_hhdm_response* hhdm = limine_hhdm_request.response; + mm_map_kernel_page (hpet_paddr, (uintptr_t)hhdm->offset + hpet_paddr, + MM_PG_PRESENT | MM_PG_RW); + + hpet_32bits = (amd64_hpet_read (HPET_GCIDR) & (1 << 13)) ? 0 : 1; + + amd64_hpet_write (HPET_GCR, amd64_hpet_read (HPET_GCR) | 0x01); + + hpet_clock_nano = amd64_hpet_read (HPET_GCIDR + 4) / 1000000; +} diff --git a/kernel/amd64/hpet.h b/kernel/amd64/hpet.h new file mode 100644 index 0000000..c4aad4f --- /dev/null +++ b/kernel/amd64/hpet.h @@ -0,0 +1,10 @@ +#ifndef _KERNEL_AMD64_HPET_H +#define _KERNEL_AMD64_HPET_H + +#include + +uint64_t amd64_hpet_current_nano (void); +void amd64_hpet_sleep_micro (uint64_t us); +void amd64_hpet_init (void); + +#endif // _KERNEL_AMD64_HPET_H diff --git a/kernel/amd64/mm.c b/kernel/amd64/mm.c new file mode 100644 index 0000000..3640d08 --- /dev/null +++ b/kernel/amd64/mm.c @@ -0,0 +1,163 @@ +#include +#include +#include +#include +#include +#include +#include + +#define AMD64_PG_PRESENT (1 << 0) +#define AMD64_PG_RW (1 << 1) +#define AMD64_PG_USER (1 << 2) + +#define AMD64_PG_TABLE_ENTRIES_MAX 512 + +struct pg_index { + uint16_t pml4, pml3, pml2, pml1; +} PACKED; + +struct pd kernel_pd = {.lock = SPIN_LOCK_INIT}; + +static uintptr_t amd64_current_cr3 (void) { + uintptr_t cr3; + __asm__ volatile ("movq %%cr3, %0" : "=r"(cr3)::"memory"); + return cr3; +} + +static struct pg_index amd64_mm_page_index (uint64_t vaddr) { + struct pg_index ret; + + ret.pml4 = ((vaddr >> 39) & 0x1FF); + ret.pml3 = ((vaddr >> 30) & 0x1FF); + ret.pml2 = ((vaddr >> 21) & 0x1FF); + ret.pml1 = ((vaddr >> 12) & 0x1FF); + + return ret; +} + +static uint64_t* amd64_mm_next_table (uint64_t* table, uint64_t entry_idx, + bool alloc) { + uint64_t entry = table[entry_idx]; + uint64_t paddr; + + struct limine_hhdm_response* hhdm = limine_hhdm_request.response; + + if (entry & AMD64_PG_PRESENT) + paddr = entry & ~0xFFFULL; + else { + if (!alloc) + return NULL; + + paddr = pmm_alloc (1); + + if (paddr == 0) + return NULL; + + memset ((void*)((uintptr_t)hhdm->offset + (uintptr_t)paddr), 0, PAGE_SIZE); + table[entry_idx] = paddr | AMD64_PG_PRESENT | AMD64_PG_RW; + } + + return (uint64_t*)((uintptr_t)hhdm->offset + (uintptr_t)paddr); +} + +static uint64_t amd64_mm_resolve_flags (uint32_t generic) { + uint64_t flags = 0; + + flags |= ((generic & MM_PG_PRESENT) ? AMD64_PG_PRESENT : 0); + flags |= ((generic & MM_PG_RW) ? AMD64_PG_RW : 0); + flags |= ((generic & MM_PG_USER) ? AMD64_PG_USER : 0); + + return flags; +} + +static void amd64_reload_cr3 (void) { + uint64_t cr3; + __asm__ volatile ("movq %%cr3, %0; movq %0, %%cr3" : "=r"(cr3)::"memory"); +} + +void mm_map_page (struct pd* pd, uintptr_t paddr, uintptr_t vaddr, + uint32_t flags) { + struct limine_hhdm_response* hhdm = limine_hhdm_request.response; + bool do_reload = false; + + if (flags & MM_PD_LOCK) + spin_lock (&pd->lock); + + uint64_t amd64_flags = amd64_mm_resolve_flags (flags); + + uint64_t* pml4 = (uint64_t*)(pd->cr3_paddr + (uintptr_t)hhdm->offset); + struct pg_index pg_index = amd64_mm_page_index (vaddr); + + uint64_t* pml3 = amd64_mm_next_table (pml4, pg_index.pml4, true); + if (pml3 == NULL) + goto done; + + uint64_t* pml2 = amd64_mm_next_table (pml3, pg_index.pml3, true); + if (pml2 == NULL) + goto done; + + uint64_t* pml1 = amd64_mm_next_table (pml2, pg_index.pml2, true); + if (pml1 == NULL) + goto done; + + uint64_t* pte = &pml1[pg_index.pml1]; + + *pte = ((paddr & ~0xFFFULL) | (amd64_flags & 0x7ULL)); + do_reload = true; + +done: + if (do_reload) + amd64_reload_cr3 (); + + if (flags & MM_PD_LOCK) + spin_unlock (&pd->lock); +} + +void mm_map_kernel_page (uintptr_t paddr, uintptr_t vaddr, uint32_t flags) { + mm_map_page (&kernel_pd, paddr, vaddr, flags); +} + +void mm_unmap_page (struct pd* pd, uintptr_t vaddr, uint32_t flags) { + struct limine_hhdm_response* hhdm = limine_hhdm_request.response; + bool do_reload = false; + + if (flags & MM_PD_LOCK) + spin_lock (&pd->lock); + + uint64_t* pml4 = (uint64_t*)(pd->cr3_paddr + (uintptr_t)hhdm->offset); + struct pg_index pg_index = amd64_mm_page_index (vaddr); + + uint64_t* pml3 = amd64_mm_next_table (pml4, pg_index.pml4, true); + if (pml3 == NULL) + goto done; + + uint64_t* pml2 = amd64_mm_next_table (pml3, pg_index.pml3, true); + if (pml2 == NULL) + goto done; + + uint64_t* pml1 = amd64_mm_next_table (pml2, pg_index.pml2, true); + if (pml1 == NULL) + goto done; + + uint64_t* pte = &pml1[pg_index.pml1]; + + *pte &= ~AMD64_PG_PRESENT; + do_reload = true; + +done: + if (do_reload) + amd64_reload_cr3 (); + + if (flags & MM_PD_LOCK) + spin_unlock (&pd->lock); +} + +void mm_unmap_kernel_page (uintptr_t vaddr, uint32_t flags) { + mm_unmap_page (&kernel_pd, vaddr, flags); +} + +void mm_lock_kernel (void) { spin_lock (&kernel_pd.lock); } + +void mm_unlock_kernel (void) { spin_unlock (&kernel_pd.lock); } + +void mm_init (void) { kernel_pd.cr3_paddr = amd64_current_cr3 (); } diff --git a/kernel/amd64/mm.h b/kernel/amd64/mm.h index 02379d6..11ec58b 100644 --- a/kernel/amd64/mm.h +++ b/kernel/amd64/mm.h @@ -1,6 +1,14 @@ #ifndef _KERNEL_AMD64_MM_H #define _KERNEL_AMD64_MM_H +#include +#include + #define PAGE_SIZE 4096 +struct pd { + spin_lock_t lock; + uintptr_t cr3_paddr; +}; + #endif // _KERNEL_AMD64_MM_H diff --git a/kernel/amd64/msr.c b/kernel/amd64/msr.c new file mode 100644 index 0000000..903ea38 --- /dev/null +++ b/kernel/amd64/msr.c @@ -0,0 +1,14 @@ +#include +#include + +uint64_t amd64_rdmsr (uint32_t msr) { + uint32_t low, high; + __asm__ volatile ("rdmsr" : "=a"(low), "=d"(high) : "c"(msr)); + return ((uint64_t)high << 32 | (uint64_t)low); +} + +void amd64_wrmsr (uint32_t msr, uint64_t value) { + uint32_t low = (uint32_t)(value & 0xFFFFFFFF); + uint32_t high = (uint32_t)(value >> 32); + __asm__ volatile ("wrmsr" ::"c"(msr), "a"(low), "d"(high)); +} diff --git a/kernel/amd64/msr.h b/kernel/amd64/msr.h new file mode 100644 index 0000000..d1ccfdf --- /dev/null +++ b/kernel/amd64/msr.h @@ -0,0 +1,9 @@ +#ifndef _KERNEL_AMD64_MSR_H +#define _KERNEL_AMD64_MSR_H + +#include + +uint64_t amd64_rdmsr (uint32_t msr); +void amd64_wrmsr (uint32_t msr, uint64_t value); + +#endif // _KERNEL_AMD64_MSR_H diff --git a/kernel/amd64/src.mk b/kernel/amd64/src.mk index 1665801..35fab8b 100644 --- a/kernel/amd64/src.mk +++ b/kernel/amd64/src.mk @@ -4,7 +4,11 @@ c += amd64/bootmain.c \ amd64/io.c \ amd64/debug.c \ amd64/spin_lock.c \ - amd64/intr.c + amd64/intr.c \ + amd64/apic.c \ + amd64/msr.c \ + amd64/hpet.c \ + amd64/mm.c S += amd64/intr_stub.S \ amd64/spin.S @@ -17,4 +21,8 @@ o += amd64/bootmain.o \ amd64/spin_lock.o \ amd64/intr.o \ amd64/intr_stub.o \ - amd64/spin.o + amd64/spin.o \ + amd64/apic.o \ + amd64/msr.o \ + amd64/hpet.o \ + amd64/mm.o diff --git a/kernel/sys/mm.h b/kernel/sys/mm.h index 0ba8606..80a96e6 100644 --- a/kernel/sys/mm.h +++ b/kernel/sys/mm.h @@ -1,8 +1,24 @@ #ifndef _KERNEL_SYS_MM_H #define _KERNEL_SYS_MM_H +#include + #if defined(__x86_64__) #include #endif +#define MM_PG_PRESENT (1 << 0) +#define MM_PG_RW (1 << 1) +#define MM_PG_USER (1 << 2) +#define MM_PD_LOCK (1 << 31) + +void mm_map_page (struct pd* pd, uintptr_t paddr, uintptr_t vaddr, + uint32_t flags); +void mm_map_kernel_page (uintptr_t paddr, uintptr_t vaddr, uint32_t flags); +void mm_unmap_page (struct pd* pd, uintptr_t vaddr, uint32_t flags); +void mm_unmap_kernel_page (uintptr_t vaddr, uint32_t flags); +void mm_lock_kernel (void); +void mm_unlock_kernel (void); +void mm_init (void); + #endif // _KERNEL_SYS_MM_H diff --git a/kernel/uACPI/platform_mop3.c b/kernel/uACPI/platform_mop3.c index 9362499..3e83443 100644 --- a/kernel/uACPI/platform_mop3.c +++ b/kernel/uACPI/platform_mop3.c @@ -1,7 +1,9 @@ +#include #include #include #include #include +#include #include #include @@ -17,11 +19,16 @@ uacpi_status uacpi_kernel_get_rsdp (uacpi_phys_addr* out_rsdp_address) { void* uacpi_kernel_map (uacpi_phys_addr addr, uacpi_size len) { (void)len; + struct limine_hhdm_response* hhdm = limine_hhdm_request.response; + return (void*)((uintptr_t)hhdm->offset + (uintptr_t)addr); } -void uacpi_kernel_unmap (void* addr, uacpi_size len) { (void)addr, (void)len; } +void uacpi_kernel_unmap (void* addr, uacpi_size len) { + (void)addr; + (void)len; +} void uacpi_kernel_log (uacpi_log_level level, const uacpi_char* msg) { const char* prefix = NULL;