diff --git a/CMakeLists.txt b/CMakeLists.txt index dd06b28..6acb0b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ # VoidFrame CMake Build Script - v0.0.2-development4 # ============================================================================ cmake_minimum_required(VERSION 3.30) +set_property(GLOBAL PROPERTY RULE_MESSAGES OFF) project(VoidFrame VERSION 0.0.2 LANGUAGES C CXX ASM_NASM @@ -18,10 +19,10 @@ include(cache) include(features) include(variable) include(dependencies) -include(flags) include(configuration) include(source) include(rust_utils) +include(flags) # ============================================================================ # Platform Checks @@ -87,16 +88,28 @@ set(RUST_ATOMIC_MANIFEST_PATH "${CMAKE_SOURCE_DIR}/kernel/atomic/rust/Cargo.toml # ============================================================================ # Corrosion # ============================================================================ + add_subdirectory(corrosion) corrosion_import_crate( MANIFEST_PATH ${RUST_HEAP_MANIFEST_PATH} NO_STD + ${Corrosion_CARGO_BUILD_FLAGS} ) corrosion_import_crate( MANIFEST_PATH ${RUST_ATOMIC_MANIFEST_PATH} NO_STD + ${Corrosion_CARGO_BUILD_FLAGS} ) +if (SILENT_BUILD) + corrosion_add_target_rustflags(voidframe_mm "-A" "warnings" "-C" "link-arg=-s") + corrosion_add_target_rustflags(voidframe_spinlock "-A" "warnings" "-C" "link-arg=-s") +endif() + +# ============================================================================ +# Flags setup +# ============================================================================ + # ============================================================================ # Kernel Linking # ============================================================================ diff --git a/arch/x86_64/asm/pxs.asm b/arch/x86_64/asm/pxs.asm index 1daa3ca..d9a6298 100644 --- a/arch/x86_64/asm/pxs.asm +++ b/arch/x86_64/asm/pxs.asm @@ -34,7 +34,7 @@ vbe_tag_end: dd 8 ; size header_end: -bits 32 +[bits 32] section .text @@ -187,29 +187,29 @@ start: ; Parse multiboot memory map and find highest available address -; Returns: highest_phys_addr in [highest_phys_addr_low:highest_phys_addr_high] +; Returns: highest_phys_addr in [highest_phys_addr_low:highest_phys_addr_high] parse_memory_map: pusha - + ; Get multiboot info pointer mov esi, [multiboot_info] - + ; Skip multiboot header (8 bytes: total_size + reserved) add esi, 8 - + ; Initialize highest address to 0 mov dword [highest_phys_addr_low], 0 mov dword [highest_phys_addr_high], 0 - + .find_mmap_tag: ; Check if we've reached end tag (type = 0) cmp dword [esi], 0 je .parse_done - + ; Check if this is memory map tag (type = 6) cmp dword [esi], 6 je .process_mmap - + ; Move to next tag (aligned to 8 bytes) mov eax, [esi + 4] ; Get tag size add eax, 7 ; Round up to 8-byte boundary @@ -223,39 +223,39 @@ parse_memory_map: sub ecx, 16 ; Subtract tag header size mov edx, [esi + 12] ; Get entry size add esi, 16 ; Skip to first entry - + .process_entry: cmp ecx, 0 jle .parse_done - + ; Check if this is available memory (type = 1) cmp dword [esi + 16], 1 jne .next_entry - + ; Calculate end address (base + length) mov eax, [esi] ; base_addr_low mov ebx, [esi + 4] ; base_addr_high add eax, [esi + 8] ; + length_low adc ebx, [esi + 12] ; + length_high (with carry) - + ; Compare with current highest address cmp ebx, [highest_phys_addr_high] ja .update_highest jb .next_entry - + ; High parts equal, compare low parts cmp eax, [highest_phys_addr_low] jbe .next_entry - + .update_highest: mov [highest_phys_addr_low], eax mov [highest_phys_addr_high], ebx - + .next_entry: add esi, edx ; Move to next entry sub ecx, edx ; Decrease remaining size jmp .process_entry - + .parse_done: popa ret @@ -263,27 +263,27 @@ parse_memory_map: ; Setup dynamic paging based on detected physical memory setup_dynamic_paging: pusha - + ; Calculate how much memory we need to map (round up to 1GB boundary) mov eax, [highest_phys_addr_low] mov ebx, [highest_phys_addr_high] - + ; For simplicity, cap at 64GB to avoid too many page tables cmp ebx, 0x10 ; 64GB = 0x1000000000 jb .size_ok mov ebx, 0x10 mov eax, 0 - + .size_ok: ; Round up to 1GB boundary (0x40000000) add eax, 0x3FFFFFFF adc ebx, 0 and eax, 0xC0000000 ; Clear lower 30 bits - + ; Store the total size to map mov [memory_to_map_low], eax mov [memory_to_map_high], ebx - + ; Calculate number of 1GB regions needed ; Each PDP entry covers 1GB, so we need (total_size / 1GB) entries push edx @@ -292,76 +292,76 @@ setup_dynamic_paging: mov ecx, 0x40000000 ; 1GB div ecx ; EAX = number of 1GB regions pop edx - + ; Cap at 512 entries (512GB max) cmp eax, 512 jbe .pdp_entries_ok mov eax, 512 - + .pdp_entries_ok: mov [num_pdp_entries], eax - + ; Zero out initial page tables mov edi, pml4_table mov ecx, 4096 * 6 ; Clear PML4, PDP, and 4 initial PD tables xor eax, eax rep stosb debug_print 'Z' ; Page Tables Zeroed - + ; Set CR3 to PML4 mov eax, pml4_table mov cr3, eax debug_print '4' ; CR3 Loaded - + ; Setup PML4[0] -> PDP Table mov edi, pml4_table lea eax, [pdp_table + 3] mov [edi], eax - + ; Setup PDP entries and corresponding PD tables mov edi, pdp_table mov esi, pd_table ; Start with first PD table mov ecx, [num_pdp_entries] - + .setup_pdp_loop: push ecx - + ; Link PDP entry to PD table lea eax, [esi + 3] ; PD table address + flags mov [edi], eax - + ; Fill the PD table with 2MB pages push edi push esi mov edi, esi ; EDI = current PD table mov ebx, 512 ; 512 entries per PD table - + ; Calculate starting physical address for this PD table mov eax, [num_pdp_entries] sub eax, ecx ; Current PDP index push edx - mov edx, 0x40000000 ; 1GB per PDP entry + mov edx, 0x40000000 ; 1GB per PDP entry mul edx ; EAX = starting physical address pop edx or eax, 0x83 ; Add Present + Writable + Large page flags - + .fill_pd_loop: mov [edi], eax ; Store PDE add edi, 8 ; Next PDE add eax, 0x200000 ; Next 2MB physical address dec ebx jnz .fill_pd_loop - + pop esi pop edi - + ; Move to next PDP entry and PD table add edi, 8 ; Next PDP entry add esi, 4096 ; Next PD table - + pop ecx loop .setup_pdp_loop - + popa ret @@ -441,9 +441,9 @@ check_and_enable_features: .no_cpuid_present: ret -bits 64 +[bits 64] -extern KernelMain +[extern KernelMain] long_mode: debug_print '8' ; Entered 64-bit mode @@ -463,6 +463,7 @@ long_mode: ; RDI is the first argument in the System V AMD64 ABI ; RSI is the second argument ; Use RDI/RSI directly, as Multiboot2 info pointer can be > 4GB + [default rel] mov edi, [multiboot_magic] ; EAX holds magic, so EDI will get the 32-bit magic mov rsi, [multiboot_info] ; EBX holds info pointer, but could be 64-bit, so use RSI diff --git a/arch/x86_64/interrupts/Interrupts.asm b/arch/x86_64/interrupts/Interrupts.asm index 967a6ae..cd83637 100644 --- a/arch/x86_64/interrupts/Interrupts.asm +++ b/arch/x86_64/interrupts/Interrupts.asm @@ -142,75 +142,6 @@ isr%1: iretq %endmacro -%macro ISR_ERRCODE 1 -section .text -global isr%1 -isr%1: - ; CPU pushes: Error Code, RIP, CS, RFLAGS, RSP, SS - ; Push interrupt number - push qword %1 - ; Push general purpose registers - push r15 - push r14 - push r13 - push r12 - push r11 - push r10 - push r9 - push r8 - push rbp - push rsi - push rdi - push rdx - push rcx - push rbx - push rax - ; Push segment registers (in reverse order of struct: gs, fs, es, ds) - ; Use general-purpose registers to push/pop segment register values - mov rax, gs - push rax - mov rax, fs - push rax - mov rax, es - push rax - mov rax, ds - push rax - - ; The pointer to the Registers struct should be RSP - mov rdi, rsp - call InterruptHandler - - ; Pop segment registers (in order: ds, es, fs, gs) - pop rax - mov ds, rax - pop rax - mov es, rax - pop rax - mov fs, rax - pop rax - mov gs, rax - ; Pop general purpose registers - pop rax - pop rbx - pop rcx - pop rdx - pop rdi - pop rsi - pop rbp - pop r8 - pop r9 - pop r10 - pop r11 - pop r12 - pop r13 - pop r14 - pop r15 - - ; Pop interrupt number - add rsp, 8 - iretq -%endmacro - ISR_NOERRCODE 0 ISR_NOERRCODE 1 ISR_NOERRCODE 2 diff --git a/cmake/flags.cmake b/cmake/flags.cmake index 56088df..b22a5d9 100644 --- a/cmake/flags.cmake +++ b/cmake/flags.cmake @@ -5,6 +5,10 @@ set(C_FLAGS " -m64 -target x86_64-unknown-none-elf -O2 -fno-omit-frame-pointer - if(SILENT_BUILD) string(APPEND C_FLAGS " -w") + string(APPEND CMAKE_ASM_NASM_FLAGS " -w-all -Wno-all") + set(Corrosion_CARGO_BUILD_FLAGS FLAGS "--quiet") +else() + set(Corrosion_CARGO_BUILD_FLAGS "") endif() if(STACK_PROTECTION) diff --git a/drivers/APIC/APIC.c b/drivers/APIC/APIC.c index fd374f8..ee2a2c7 100644 --- a/drivers/APIC/APIC.c +++ b/drivers/APIC/APIC.c @@ -40,13 +40,22 @@ #define IOAPIC_DEFAULT_PHYS_ADDR 0xFEC00000 #define LAPIC_LVT_TIMER_SCALE_FACTOR 1 // --- Global Variables --- -volatile uint32_t* s_lapic_base = NULL; +// For now, a single global instance for BSP. Will be replaced by true per-CPU data in SMP. +static PerCpuData g_bsp_per_cpu_data = { + .lapic_base = NULL, + .apic_id = 0, + .apic_timer_freq_hz = 1000, + .apic_timer_ticks = 0, + .apic_bus_freq = 0, + .apic_calibrated = false, +}; + +PerCpuData* GetPerCpuData(void) { + return &g_bsp_per_cpu_data; +} + static volatile uint32_t* s_ioapic_base = NULL; -static uint32_t s_apic_timer_freq_hz = 1000; // Default to 1KHz -volatile uint32_t s_apic_timer_ticks = 0; volatile uint32_t APIC_HZ = 250; -uint32_t s_apic_bus_freq = 0; // Cached bus frequency, non static for TSC.c access -static bool s_calibrated = false; // Calibration flag // --- Forward Declarations --- static void lapic_write(uint32_t reg, uint32_t value); @@ -79,11 +88,11 @@ void PICMaskAll() { // --- MMIO Functions --- static void lapic_write(uint32_t reg, uint32_t value) { - s_lapic_base[reg / 4] = value; + GetPerCpuData()->lapic_base[reg / 4] = value; } static uint32_t lapic_read(uint32_t reg) { - return s_lapic_base[reg / 4]; + return GetPerCpuData()->lapic_base[reg / 4]; } uint8_t lapic_get_id() { @@ -129,7 +138,6 @@ bool ApicInstall() { return false; } - PrintKernelSuccess("APIC: Successfully initialized Local APIC and I/O APIC.\n"); return true; } @@ -174,21 +182,23 @@ void ApicMaskAll() { // --- APIC Timer Management --- void ApicTimerInstall(uint32_t frequency_hz) { - s_apic_timer_freq_hz = frequency_hz; + PerCpuData* cpu_data = GetPerCpuData(); + cpu_data->apic_timer_freq_hz = frequency_hz; // Calibrate and set the initial count - ApicTimerSetFrequency(s_apic_timer_freq_hz); + ApicTimerSetFrequency(cpu_data->apic_timer_freq_hz); PrintKernelF("APIC: Timer installed at %d Hz.\n", frequency_hz); } void ApicTimerSetFrequency(uint32_t frequency_hz) { if (frequency_hz == 0) return; - s_apic_timer_freq_hz = frequency_hz; - APIC_HZ = frequency_hz; + PerCpuData* cpu_data = GetPerCpuData(); + cpu_data->apic_timer_freq_hz = frequency_hz; + APIC_HZ = frequency_hz; // Global for now, but should be per-CPU // Only calibrate once to avoid expensive operations - if (!s_calibrated) { + if (!cpu_data->apic_calibrated) { // Set divider first lapic_write(LAPIC_TIMER_DIV, 0xB); // Divide by 1 (no division) @@ -223,17 +233,15 @@ void ApicTimerSetFrequency(uint32_t frequency_hz) { if (timeout > 0) { uint32_t ticks_per_10ms = start_count - end_count; - s_apic_bus_freq = ticks_per_10ms * 100; - s_calibrated = true; - // PrintKernelF("APIC: Calibrated - ticks/10ms=%u, bus_freq=%u Hz\n", ticks_per_10ms, s_apic_bus_freq); + cpu_data->apic_bus_freq = ticks_per_10ms * 100; + cpu_data->apic_calibrated = true; } else { - s_apic_bus_freq = 100000000; // Fallback: 100MHz + cpu_data->apic_bus_freq = 100000000; // Fallback: 100MHz PrintKernelWarning("APIC: Calibration timeout, using fallback frequency\n"); } } - uint32_t initial_count = s_apic_bus_freq / frequency_hz; - // PrintKernelF("APIC: Setting timer - freq=%u Hz, bus_freq=%u, initial_count=%u\n", frequency_hz, s_apic_bus_freq, initial_count); + uint32_t initial_count = cpu_data->apic_bus_freq / frequency_hz; lapic_write(LAPIC_LVT_TIMER, 32 | (0b01 << 17)); // Periodic mode lapic_write(LAPIC_TIMER_INIT_COUNT, initial_count); } @@ -255,18 +263,19 @@ static bool setup_lapic() { uint64_t lapic_phys_base = lapic_base_msr & 0xFFFFFFFFFFFFF000ULL; // Map the LAPIC into virtual memory - s_lapic_base = (volatile uint32_t*)VMemAlloc(PAGE_SIZE); - if (!s_lapic_base) { + PerCpuData* cpu_data = GetPerCpuData(); + cpu_data->lapic_base = (volatile uint32_t*)VMemAlloc(PAGE_SIZE); + if (!cpu_data->lapic_base) { PrintKernelError("APIC: Failed to allocate virtual memory for LAPIC.\n"); return false; } - if (VMemUnmap((uint64_t)s_lapic_base, PAGE_SIZE) != VMEM_SUCCESS) { + if (VMemUnmap((uint64_t)cpu_data->lapic_base, PAGE_SIZE) != VMEM_SUCCESS) { PrintKernelError("APIC: Failed to unmap LAPIC MMIO.\n"); return false; } - if (VMemMapMMIO((uint64_t)s_lapic_base, lapic_phys_base, PAGE_SIZE, PAGE_WRITABLE | PAGE_NOCACHE) != VMEM_SUCCESS) { + if (VMemMapMMIO((uint64_t)cpu_data->lapic_base, lapic_phys_base, PAGE_SIZE, PAGE_WRITABLE | PAGE_NOCACHE) != VMEM_SUCCESS) { PrintKernelError("APIC: Failed to map LAPIC MMIO.\n"); return false; } @@ -277,9 +286,6 @@ static bool setup_lapic() { lapic_write(LAPIC_SVR, 0x1FF); // Set TPR to 0 to accept all interrupts lapic_write(LAPIC_TPR, 0); - PrintKernelF("APIC: LAPIC enabled at physical addr 0x%llx, mapped to 0x%llx\n", - (unsigned long long)lapic_phys_base, - (unsigned long long)(uintptr_t)s_lapic_base); return true; } @@ -300,7 +306,6 @@ static bool setup_ioapic() { // Read the I/O APIC version to verify it's working uint32_t version_reg = ioapic_read(IOAPIC_REG_VER); uint8_t max_redirects = (version_reg >> 16) & 0xFF; - PrintKernelF("APIC: I/O APIC version %d, max redirects: %d\n", version_reg & 0xFF, max_redirects + 1); // Mask all interrupts initially ApicMaskAll(); diff --git a/drivers/APIC/APIC.h b/drivers/APIC/APIC.h index d33aa48..9743f90 100644 --- a/drivers/APIC/APIC.h +++ b/drivers/APIC/APIC.h @@ -4,6 +4,19 @@ #include #include +// Per-CPU data structure for APIC +typedef struct { + volatile uint32_t* lapic_base; // Mapped virtual address of this CPU's LAPIC + uint32_t apic_id; // This CPU's APIC ID + uint32_t apic_timer_freq_hz; // This CPU's APIC timer frequency + uint32_t apic_timer_ticks; // This CPU's APIC timer ticks + uint32_t apic_bus_freq; // This CPU's APIC bus frequency + bool apic_calibrated; // Flag if this CPU's APIC timer is calibrated +} PerCpuData; + +// Function to get the current CPU's PerCpuData +PerCpuData* GetPerCpuData(void); + // Main initialization function to detect and set up both Local APIC and I/O APIC. // Returns true on success, false on failure (e.g., no APIC found). bool ApicInstall(); diff --git a/drivers/TSC.c b/drivers/TSC.c index 4ed150b..842d97d 100644 --- a/drivers/TSC.c +++ b/drivers/TSC.c @@ -1,6 +1,7 @@ #include "TSC.h" #include "x64.h" #include "Console.h" +#include "APIC/APIC.h" uint64_t tsc_freq_hz = 0; static bool tsc_calibrated = false; @@ -8,7 +9,7 @@ static bool tsc_calibrated = false; void TSCInit(void) { // Calibrate TSC frequency using APIC timer extern volatile uint32_t APIC_HZ; - extern uint32_t s_apic_bus_freq; + PerCpuData* cpu_data = GetPerCpuData(); if (APIC_HZ == 0) { tsc_freq_hz = 3000000000ULL; // Fallback: 3GHz @@ -19,12 +20,11 @@ void TSCInit(void) { // Use 10ms calibration period uint64_t start_tsc = rdtsc(); - uint32_t calibration_ticks = s_apic_bus_freq / 100; // 10 ms worth of APIC ticks + uint32_t calibration_ticks = cpu_data->apic_bus_freq / 100; // 10 ms worth of APIC ticks // Wait using APIC timer current count - extern volatile uint32_t* s_lapic_base; - uint32_t target = s_lapic_base[0x390/4] - calibration_ticks; - while (s_lapic_base[0x390/4] > target); + uint32_t target = cpu_data->lapic_base[0x390/4] - calibration_ticks; + while (cpu_data->lapic_base[0x390/4] > target); uint64_t end_tsc = rdtsc(); tsc_freq_hz = (end_tsc - start_tsc) * 100; // Scale to 1 second