diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index a3dce49..ad0c078 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -42,7 +42,6 @@ jobs: cmake .. -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DVF_SCHEDULER=EEVDF \ - -DVF_CONFIG_HEAP_LANG=RUST \ -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchain/linux-x64.cmake \ -DCMAKE_ASM_NASM_COMPILER=$(which nasm) cd .. diff --git a/CMakeLists.txt b/CMakeLists.txt index dc1cfa5..dd06b28 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,11 @@ else() message(STATUS "Using toolchain file: ${CMAKE_TOOLCHAIN_FILE}") endif() +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type" FORCE) + message(WARNING "CMAKE_BUILD_TYPE not set. Defaulting to Release.") +endif() + if(CMAKE_GENERATOR STREQUAL "Ninja") message(STATUS "CMake: Generator: ${CMAKE_GENERATOR}") else() @@ -63,7 +68,6 @@ message(STATUS "CMake Current Binary Directory: ${CMAKE_CURRENT_BINARY_DIR}") message(STATUS "CMake Host System Name: ${CMAKE_HOST_SYSTEM_NAME}") message(STATUS "CMake Host System Processor: ${CMAKE_HOST_SYSTEM_PROCESSOR}") message(STATUS "VoidFrame Scheduler: ${VF_SCHEDULER}") -message(STATUS "VoidFrame Heap Management Language: ${VF_CONFIG_HEAP_LANG}") message(STATUS "CMake sources configured.") # ============================================================================ diff --git a/README.md b/README.md index 6c88172..48dd778 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,7 @@ cd build cmake .. -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchain/-x64.cmake \ -G Ninja \ - -DVF_SCHEDULER= \ - -DVF_CONFIG_HEAP_LANG= + -DVF_SCHEDULER= ccmake . # Optinal, tune as needed ninja -j$(nproc) ninja run diff --git a/cmake/cache.cmake b/cmake/cache.cmake index 7f3b835..24aef03 100644 --- a/cmake/cache.cmake +++ b/cmake/cache.cmake @@ -3,6 +3,3 @@ # ============================================================================ set(VF_SCHEDULER "EEVDF" CACHE STRING "Scheduler type: MLFQ or EEVDF") set_property(CACHE VF_SCHEDULER PROPERTY STRINGS MLFQ EEVDF) - -set(VF_CONFIG_HEAP_LANG "C" CACHE STRING "Heap language: RUST or C") -set_property(CACHE VF_CONFIG_HEAP_LANG PROPERTY STRINGS RUST C) diff --git a/cmake/features.cmake b/cmake/features.cmake index dffefe6..f0a5f92 100644 --- a/cmake/features.cmake +++ b/cmake/features.cmake @@ -14,7 +14,6 @@ add_compile_definitions( VF_CONFIG_ENABLE_NVME VF_CONFIG_ENABLE_GENERIC_SOUND VF_CONFIG_RTC_CENTURY - VF_CONFIG_LOAD_MB_MODULES VF_CONFIG_ENFORCE_MEMORY_PROTECTION VF_CONFIG_VM_HOST VF_CONFIG_SNOOZE_ON_BOOT @@ -34,6 +33,7 @@ add_compile_definitions( # VF_CONFIG_CERBERUS_THREAT_REPORTING # VF_CONFIG_PROCINFO_AUTO_CLEANUP # VF_CONFIG_PANIC_OVERRIDE + # VF_CONFIG_LOAD_MB_MODULES ) if(EXCLUDE_EXTRA_OBJECTS) @@ -50,12 +50,4 @@ elseif(VF_SCHEDULER STREQUAL "EEVDF") add_compile_definitions(VF_CONFIG_SCHED_EEVDF) else() message(FATAL_ERROR "Invalid scheduler: ${VF_SCHEDULER}. Did you pass: -DVF_SCHEDULER=?") -endif() - -if(VF_CONFIG_HEAP_LANG STREQUAL "RUST") - add_compile_definitions(VF_CONFIG_HEAP_RUST) -elseif(VF_CONFIG_HEAP_LANG STREQUAL "C") - add_compile_definitions(VF_CONFIG_HEAP_C) -else() - message(FATAL_ERROR "Invalid heap language: ${VF_CONFIG_HEAP_LANG}. Did you pass: -DVF_CONFIG_HEAP_LANG=?") -endif() +endif() \ No newline at end of file diff --git a/cmake/source.cmake b/cmake/source.cmake index 2a44276..858a56c 100644 --- a/cmake/source.cmake +++ b/cmake/source.cmake @@ -17,10 +17,14 @@ set(ASM_SOURCES set(KERNEL_CORE_SOURCES kernel/core/Kernel.c kernel/core/Panic.c - vfcompositor/Compositor.c kernel/core/InitRD.c ) +set(VFC_SOURCES + vfcompositor/Compositor.c + vfcompositor/app/GUIShell.c +) + set(SCHED_SOURCES kernel/sched/MLFQ.c kernel/sched/EEVDF.c @@ -57,6 +61,7 @@ set(MM_SOURCES mm/MemPool.c mm/trace/StackTrace.c mm/security/Cerberus.c + mm/dynamic/c/Magazine.c mm/PageFaultHandler.c ) @@ -128,7 +133,6 @@ set(OBJ_SOURCES) # ============================================================================ include_directories( . - vfcompositor include kernel/atomic kernel/core @@ -175,16 +179,13 @@ include_directories( arch/x86_64/idt arch/x86_64/interrupts arch/x86_64/syscall + vfcompositor + vfcompositor/app ) # ============================================================================ # Sources Configuration # ============================================================================ -if (VF_CONFIG_HEAP_LANG STREQUAL "C") - list(APPEND MM_SOURCES - mm/dynamic/c/Magazine.c - ) -endif() if(NOT EXCLUDE_EXTRA_OBJECTS) set(OBJ_SOURCES @@ -207,4 +208,5 @@ set(C_SOURCES ${DRIVER_SOURCES} ${ARCH_SOURCES} ${INCLUDE_SOURCES} + ${VFC_SOURCES} ) \ No newline at end of file diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 6fd2ff8..cf312f5 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -29,7 +29,9 @@ Here are the commands to install the required dependencies on various platforms. ```bash # Update package list and install dependencies -sudo pacman -Syu cmake ninja clang nasm qemu-full dosfstools e2fsprogs grub xorriso mtools +sudo pacman -Syu cmake ninja clang nasm qemu-full dosfstools e2fsprogs grub xorriso mtools rustup +rustup target add x86_64-unknown-none +rustup toolchain install nightly ``` @@ -41,6 +43,10 @@ sudo pacman -Syu cmake ninja clang nasm qemu-full dosfstools e2fsprogs grub xorr # Update package list and install dependencies sudo apt update sudo apt install -y cmake ninja-build clang nasm qemu-system-x86 dosfstools e2fsprogs grub-pc-bin xorriso mtools +curl --proto '=https' --tlsv1.3 https://sh.rustup.rs -sSf | sh -s -- -y +source ~/.cargo/env +rustup toolchain install nightly +rustup target add x86_64-unknown-none ``` @@ -51,6 +57,10 @@ sudo apt install -y cmake ninja-build clang nasm qemu-system-x86 dosfstools e2fs ```bash # Install dependencies sudo dnf install -y cmake ninja-build clang nasm qemu-system-x86 dosfstools e2fsprogs grub2-tools-extra xorriso mtools +curl --proto '=https' --tlsv1.3 https://sh.rustup.rs -sSf | sh -s -- -y +source ~/.cargo/env +rustup toolchain install nightly +rustup target add x86_64-unknown-none ``` diff --git a/include/Scheduler.h b/include/Scheduler.h index 5fa567f..331117c 100644 --- a/include/Scheduler.h +++ b/include/Scheduler.h @@ -2,6 +2,7 @@ ///@brief Unified wrapper for multiple scheduler implementations #ifndef VOIDFRAME_SCHEDULER_H #define VOIDFRAME_SCHEDULER_H +#include "MLFQ.h" #if defined(VF_CONFIG_SCHED_MLFQ) #include "MLFQ.h" @@ -39,6 +40,16 @@ static inline __attribute__((always_inline)) uint32_t CreateProcess(const char * #endif } +static inline __attribute__((always_inline)) uint32_t CreateSecureProcess(const char * name, void (*entry_point)(), uint8_t priv, uint8_t flag) { +#if defined(VF_CONFIG_SCHED_MLFQ) + return MLFQCreateSecureProcess(name, entry_point, priv, flag); +#elif defined(VF_CONFIG_SCHED_EEVDF) + return EEVDFCreateSecureProcess(name, entry_point, priv, flag); +#elif defined(VF_CONFIG_SCHED_CFS) + return 0; // not implemented +#endif +} + static inline __attribute__((always_inline)) CurrentProcessControlBlock* GetCurrentProcess() { #if defined(VF_CONFIG_SCHED_MLFQ) return MLFQGetCurrentProcess(); diff --git a/include/Window.h b/include/Window.h index 6e68cf5..33180f9 100644 --- a/include/Window.h +++ b/include/Window.h @@ -31,6 +31,7 @@ struct Window { // Window state bool minimized; + uint32_t owner_pid; // PID of the process that created this window }; #endif // WINDOW_H diff --git a/kernel/core/InitRD.c b/kernel/core/InitRD.c index 55635cb..f42daec 100644 --- a/kernel/core/InitRD.c +++ b/kernel/core/InitRD.c @@ -2,9 +2,6 @@ #include "Multiboot2.h" #include "Console.h" #include "VFS.h" -#include "KernelHeap.h" -#include "MemOps.h" - extern uint32_t g_multiboot_info_addr; void InitRDLoad(void) { diff --git a/kernel/core/Pallete.h b/kernel/core/Pallete.h index 6415d06..77e5b91 100644 --- a/kernel/core/Pallete.h +++ b/kernel/core/Pallete.h @@ -9,7 +9,7 @@ #define WINDOW_BG_1 0x151515 // Very dark gray #define TITLE_BAR_1 0x1A1A2E // Dark blue-gray #define BORDER_1 0x2A2A3A // Subtle border -#define ACCENT_1 0x6B73FF // Bright blue accent +#define ACCENT_1 0x1D234A // Bright blue accent #define ERROR_1 0xFF6B6B // Soft red #define SUCCESS_1 0x51CF66 // Muted green @@ -101,7 +101,7 @@ // Easy palette switching system // ============================================================================= #ifndef CURRENT_PALETTE -#define CURRENT_PALETTE 2 +#define CURRENT_PALETTE 1 #endif #if CURRENT_PALETTE == 1 diff --git a/kernel/etc/Console.c b/kernel/etc/Console.c index fdba366..4347da6 100644 --- a/kernel/etc/Console.c +++ b/kernel/etc/Console.c @@ -1,8 +1,7 @@ #include "Console.h" #include "Format.h" #include "Io.h" -#include "Pallete.h" -#include "Scheduler.h" +#include "Compositor.h" #include "Serial.h" #include "SpinlockRust.h" #include "VBEConsole.h" @@ -76,6 +75,13 @@ void Unsnooze() { __atomic_store_n(&snooze, 0, __ATOMIC_RELEASE); } +extern CompositorContext g_compositor_ctx; +static Window* console_window = NULL; + +void ConsoleSetWindowPrint(Window* w) { + console_window = w; +} + // Initialize console - auto-detect VBE or VGA void ConsoleInit(void) { console_lock = rust_spinlock_new(); @@ -120,6 +126,7 @@ static void ConsolePutcharAt(char c, uint32_t x, uint32_t y, uint8_t color) { void ClearScreen(void) { // Snooze mode: skip physical console updates + if (console_window) WindowClearText(&g_compositor_ctx, console_window); if (snooze) return; rust_spinlock_lock(console_lock); if (!console.buffer) console.buffer = (volatile uint16_t*)VGA_BUFFER_ADDR; @@ -128,6 +135,7 @@ void ClearScreen(void) { } static void ConsoleScroll(void) { + if (console_window) WindowScrollUp(&g_compositor_ctx, console_window); if (use_vbe) return; // VBE handles scrolling internally for (uint32_t i = 0; i < (VGA_HEIGHT - 1) * VGA_WIDTH; i++) { @@ -190,6 +198,7 @@ void ConsoleSetColor(uint8_t color) { void PrintKernel(const char* str) { if (!str) return; SerialWrite(str); + if (console_window) WindowPrintString(&g_compositor_ctx, console_window, str); if (snooze) return; rust_spinlock_lock(console_lock); diff --git a/kernel/etc/Console.h b/kernel/etc/Console.h index 589745b..1ce2bbe 100644 --- a/kernel/etc/Console.h +++ b/kernel/etc/Console.h @@ -74,6 +74,7 @@ void SerialWriteF(const char* format, ...); void PrintKernelErrorF(const char* format, ...); void PrintKernelWarningF(const char* format, ...); void PrintKernelSuccessF(const char* format, ...); +// void ConsoleSetWindowPrint(Window* w); #ifdef __cplusplus } #endif @@ -82,4 +83,6 @@ static inline __attribute__((always_inline)) void PrintNewline(void) { PrintKernel("\n"); } +#define LOG() PrintKernelF("[%s:%d]:[%s]\n", __FILE__, __LINE__, __FUNCTION__) + #endif // VOIDFRAME_CONSOLE_H diff --git a/kernel/etc/POST.c b/kernel/etc/POST.c index 4588808..5ab1766 100644 --- a/kernel/etc/POST.c +++ b/kernel/etc/POST.c @@ -6,6 +6,7 @@ #include "Serial.h" #include "VMem.h" #include "stdbool.h" +#include "x64.h" #define N 512 void * ptrs[N] = {0}; @@ -21,23 +22,32 @@ bool SerialTest() { } bool MemoryTest() { + uint64_t start = rdtsc(); for (int i = 1; i < 1000; i++) { size_t sz = (i % 7 == 0) ? 4096 : (i % 100 + 1); void *ptr = KernelMemoryAlloc(sz); if (!ptr) return false; KernelFree(ptr); } + PrintKernelF("Loop 1 took: %llu sycles\n", rdtsc() - start); + start = rdtsc(); for (int i = 0; i < N; i++) ptrs[i] = KernelMemoryAlloc(128); + PrintKernelF("Loop 2 took: %llu sycles\n", rdtsc() - start); + start = rdtsc(); // free every other block for (int i = 0; i < N; i += 2) KernelFree(ptrs[i]); + PrintKernelF("Loop 3 took: %llu sycles\n", rdtsc() - start); + start = rdtsc(); // re-allocate in different sizes for (int i = 0; i < N/2; i++) { ptrs[i] = KernelMemoryAlloc((i % 2) ? 64 : 256); } + PrintKernelF("Loop 4 took: %llu sycles\n", rdtsc() - start); + start = rdtsc(); for (int i = 0; i < 1000; i++) { size_t sz = (i % 500) + 1; uint8_t *p = (uint8_t*)KernelMemoryAlloc(sz); @@ -46,6 +56,7 @@ bool MemoryTest() { if (p[j] != (uint8_t)(i ^ j)) PANIC("Memory corruption!"); KernelFree(p); } + PrintKernelF("Loop 5 took: %llu sycles\n", rdtsc() - start); return true; } diff --git a/kernel/etc/Shell.c b/kernel/etc/Shell.c index f84b02b..f1cee4d 100644 --- a/kernel/etc/Shell.c +++ b/kernel/etc/Shell.c @@ -43,7 +43,7 @@ static char command_buffer[256]; static int cmd_pos = 0; -static char current_dir[256] = "/"; +char current_dir[256] = "/"; extern uint8_t _kernel_phys_start[]; extern uint8_t _kernel_phys_end[]; @@ -186,6 +186,7 @@ static const HelpEntry system_cmds[] = { {"memstat", "Show memory statistics"}, {"stacksize", "Show stack usage"}, {"lscpu", "List CPU features"}, + {"vfc NULL/fork", "Start VFCompositor as another process or on the currently session"}, {"snoozer ", "Snooze messages from PrintKernel"}, {"acpi sd/rb", "Shutdown (sd) or reboot (rb) via ACPI"}, }; diff --git a/kernel/sched/EEVDF.c b/kernel/sched/EEVDF.c index 4684398..589d68a 100644 --- a/kernel/sched/EEVDF.c +++ b/kernel/sched/EEVDF.c @@ -3,6 +3,7 @@ #ifdef VF_CONFIG_USE_CERBERUS #include "Cerberus.h" #endif +#include "Compositor.h" #include "Console.h" #include "Format.h" #include "Ipc.h" @@ -864,7 +865,7 @@ int EEVDFSchedInit(void) { #ifdef VF_CONFIG_USE_VFSHELL // Create shell process PrintKernel("System: Creating shell process...\n"); - const uint32_t shell_pid = EEVDFCreateProcess("VFShell", ShellProcess); + const uint32_t shell_pid = EEVDFCreateSecureProcess("VFShell", ShellProcess, EEVDF_PROC_PRIV_SYSTEM, EEVDF_PROC_FLAG_CORE); if (!shell_pid) { #ifndef VF_CONFIG_PANIC_OVERRIDE PANIC("CRITICAL: Failed to create shell process"); @@ -881,7 +882,7 @@ int EEVDFSchedInit(void) { return 0; } -uint32_t EEVDFCreateProcess(const char* name, void (*entry_point)(void)) { +uint32_t EEVDFCreateSecureProcess(const char* name, void (*entry_point)(void), uint8_t priv, uint8_t flag) { if (UNLIKELY(!entry_point)) { PANIC("EEVDFCreateProcess: NULL entry point"); } @@ -938,7 +939,7 @@ uint32_t EEVDFCreateProcess(const char* name, void (*entry_point)(void)) { proc->pid = new_pid; proc->state = PROC_READY; proc->stack = stack; - proc->privilege_level = EEVDF_PROC_PRIV_NORM; + proc->privilege_level = priv; proc->creation_time = EEVDFGetSystemTicks(); EEVDFSetTaskNice(proc, EEVDF_DEFAULT_NICE); @@ -950,8 +951,8 @@ uint32_t EEVDFCreateProcess(const char* name, void (*entry_point)(void)) { EEVDFSecurityToken* token = &proc->token; token->magic = EEVDF_SECURITY_MAGIC; token->creator_pid = creator->pid; - token->privilege = EEVDF_PROC_PRIV_NORM; - token->flags = EEVDF_PROC_FLAG_NONE; + token->privilege = priv; + token->flags = flag; token->creation_tick = proc->creation_time; token->checksum = EEVDFCalculateSecureChecksum(token, new_pid); @@ -993,6 +994,10 @@ uint32_t EEVDFCreateProcess(const char* name, void (*entry_point)(void)) { return new_pid; } +uint32_t EEVDFCreateProcess(const char* name, void (*entry_point)(void)) { + return EEVDFCreateSecureProcess(name, entry_point, EEVDF_PROC_PRIV_NORM, EEVDF_PROC_FLAG_NONE); +} + EEVDFProcessControlBlock* EEVDFGetCurrentProcess(void) { if (current_process >= EEVDF_MAX_PROCESSES) { PANIC("EEVDFGetCurrentProcess: Invalid current process index"); diff --git a/kernel/sched/EEVDF.h b/kernel/sched/EEVDF.h index 9d1a41d..d6bc603 100644 --- a/kernel/sched/EEVDF.h +++ b/kernel/sched/EEVDF.h @@ -180,6 +180,7 @@ typedef struct { // Core scheduler functions int EEVDFSchedInit(void); uint32_t EEVDFCreateProcess(const char* name, void (*entry_point)(void)); +uint32_t EEVDFCreateSecureProcess(const char* name, void (*entry_point)(void), uint8_t priv, uint8_t flag); EEVDFProcessControlBlock* EEVDFGetCurrentProcess(void); EEVDFProcessControlBlock* EEVDFGetCurrentProcessByPID(uint32_t pid); void EEVDFYield(void); diff --git a/kernel/sched/MLFQ.c b/kernel/sched/MLFQ.c index c821e94..9c4d696 100644 --- a/kernel/sched/MLFQ.c +++ b/kernel/sched/MLFQ.c @@ -1011,7 +1011,7 @@ void ProcessExitStub() { __builtin_unreachable(); } -static __attribute__((visibility("hidden"))) uint32_t CreateSecureProcess(const char * name, void (*entry_point)(void), uint8_t privilege, uint32_t initial_flags) { +uint32_t CreateSecureProcess(const char * name, void (*entry_point)(void), uint8_t privilege, uint32_t initial_flags) { irq_flags_t flags = rust_spinlock_lock_irqsave(scheduler_lock); if (UNLIKELY(!entry_point)) { rust_spinlock_unlock_irqrestore(scheduler_lock, flags); diff --git a/kernel/sched/MLFQ.h b/kernel/sched/MLFQ.h index 942e888..58e24a2 100644 --- a/kernel/sched/MLFQ.h +++ b/kernel/sched/MLFQ.h @@ -206,6 +206,7 @@ typedef struct { // Core process functions int MLFQSchedInit(void); uint32_t MLFQCreateProcess(const char * name, void (*entry_point)(void)); +uint32_t MLFQCreateSecureProcess(const char * name, void (*entry_point)(void), uint8_t privilege, uint32_t initial_flags); MLFQProcessControlBlock* MLFQGetCurrentProcess(void); MLFQProcessControlBlock* MLFQGetCurrentProcessByPID(uint32_t pid); void MLFQCleanupTerminatedProcess(void); diff --git a/mm/KernelHeap.h b/mm/KernelHeap.h index ddbf783..874f168 100644 --- a/mm/KernelHeap.h +++ b/mm/KernelHeap.h @@ -7,38 +7,73 @@ #include "stdint.h" #include "stddef.h" +#include "Console.h" +#include "VMem.h" +#include "APIC.h" +#include "Magazine.h" +#include "KernelHeapRust.h" -#if defined(VF_CONFIG_HEAP_C) +// Tier 1: C Magazine Allocator for extreme speed on small allocations +#define MAGAZINE_MAX_SIZE 1024 -#include "Magazine.h" +// Tier 2: Rust Allocator for general-purpose medium-sized allocations +#define RUST_MAX_SIZE (128 * 1024) -#define KernelHeapInit() MagazineInit() -#define KernelMemoryAlloc(size) MagazineAlloc(size) -#define KernelAllocate(num, size) MagazineAllocate(num, size) -#define KernelReallocate(ptr, size) MagazineReallocate(ptr, size) -#define KernelFree(ptr) MagazineFree(ptr) -#define PrintHeapStats() -#define KernelHeapSetValidationLevel(level) -#define KernelHeapFlushCaches() -#define KernelHeapTune(small_alloc_threshold, fast_cache_capacity) -#define KernelHeapPerfMode(mode) +// Helper function to wrap large VMem allocations with a header +static inline void* LargeBlockAlloc(size_t size) { + size_t total_size = sizeof(LargeBlockHeader) + size; + void* raw_mem = VMemAlloc(total_size); + if (!raw_mem) { + return NULL; + } + LargeBlockHeader* header = (LargeBlockHeader*)raw_mem; + header->magic = LARGE_BLOCK_MAGIC; + header->size = size; + return (void*)(header + 1); +} -#elif defined(VF_CONFIG_HEAP_RUST) +// Dispatcher for freeing memory based on header magic numbers +static inline void HybridFree(void* ptr) { + if (!ptr) { + return; + } -#include "APIC.h" -#include "KernelHeapRust.h" + // Check for small magazine allocation + MagazineBlockHeader* small_header = (MagazineBlockHeader*)ptr - 1; + if (small_header->magic == MAGAZINE_BLOCK_MAGIC) { + MagazineFree(ptr); + return; + } -#define KernelHeapInit() rust_heap_enable_percpu() -#define KernelMemoryAlloc(size) rust_kmalloc(size) -#define KernelAllocate(num, size) rust_kcalloc(num, size) -#define KernelReallocate(ptr, size) rust_krealloc(ptr, size) -#define KernelFree(ptr) rust_kfree(ptr) -#define PrintHeapStats() -#define KernelHeapSetValidationLevel(level) -#define KernelHeapFlushCaches() rust_heap_flush_cpu(lapic_get_id()) -#define KernelHeapTune(small_alloc_threshold, fast_cache_capacity) -#define KernelHeapPerfMode(mode) rust_heap_set_performance_mode(mode) + // Check for large VMem allocation + LargeBlockHeader* large_header = (LargeBlockHeader*)ptr - 1; + if (large_header->magic == LARGE_BLOCK_MAGIC) { + VMemFree((void*)large_header, large_header->size + sizeof(LargeBlockHeader)); + return; + } -#endif + // Otherwise, assume it's from the Rust allocator + rust_kfree(ptr); +} + + +#define KernelHeapInit() do { MagazineInit(); rust_heap_enable_percpu(); } while (0) + +#define KernelMemoryAlloc(size) \ + ((size) <= MAGAZINE_MAX_SIZE) ? MagazineAlloc(size) : \ + ((size) <= RUST_MAX_SIZE) ? rust_kmalloc(size) : \ + LargeBlockAlloc(size) + +#define KernelAllocate(num, size) KernelMemoryAlloc((num)*(size)) // Simplified; assumes no overflow + +#define KernelReallocate(ptr, size) MagazineReallocate((ptr), (size)) // Realloc remains complex, handled by Magazine for now + +#define KernelFree(ptr) HybridFree(ptr) + +#define PrintHeapStats() MagazinePrintStats() +#define KernelHeapSetValidationLevel(level) MagazineSetValidationLevel((level)) +#define KernelHeapFlushCaches() do { MagazineFlushCaches(); rust_heap_flush_cpu(lapic_get_id()); } while (0) +#define KernelHeapTune(small_alloc_threshold, fast_cache_capacity) +#define KernelHeapPerfMode(mode) do { MagazineSetPerfMode((mode)); rust_heap_set_performance_mode((mode)); } while (0) #endif // KHEAP_H \ No newline at end of file diff --git a/mm/dynamic/c/Magazine.c b/mm/dynamic/c/Magazine.c index d50206f..0e93b60 100644 --- a/mm/dynamic/c/Magazine.c +++ b/mm/dynamic/c/Magazine.c @@ -1,12 +1,53 @@ #include "Magazine.h" #include "MemOps.h" #include "Console.h" -#include "../VMem.h" +#include "VMem.h" #include "Panic.h" #include "SpinlockRust.h" -#include "../drivers/APIC/APIC.h" -#include "../../arch/x86_64/features/x64.h" -#include "../../include/Io.h" +#include "drivers/APIC/APIC.h" +#include "arch/x86_64/features/x64.h" +#include "include/Io.h" +#include "mm/KernelHeap.h" +#ifndef KHEAP_VALIDATION_NONE +#define KHEAP_VALIDATION_NONE 0 +#define KHEAP_VALIDATION_BASIC 1 +#define KHEAP_VALIDATION_FULL 2 +#endif + +// ================================================================================================= +// Statistics (lightweight, best-effort) +// ================================================================================================= + +typedef struct { + uint64_t alloc_fast_hits[NUM_SIZE_CLASSES]; + uint64_t alloc_slow_refills[NUM_SIZE_CLASSES]; + uint64_t free_fast_hits[NUM_SIZE_CLASSES]; + uint64_t free_slow_paths[NUM_SIZE_CLASSES]; + uint64_t magazine_swaps[NUM_SIZE_CLASSES]; + uint64_t slabs_allocated[NUM_SIZE_CLASSES]; +} HeapStatsPerCpu; + +static __attribute__((aligned(64))) HeapStatsPerCpu heap_stats_per_cpu[MAX_CPU_CORES]; + +// Runtime knobs +static volatile int g_validation_level = KHEAP_VALIDATION_NONE; // 0=none,1=basic,2=full +static volatile int g_stats_enabled = 1; // can be toggled by perf mode + +static inline void StatsAdd(uint32_t cpu, int sc, volatile uint64_t* ctr) { + // Best-effort increment; tearing is acceptable for stats. + if (!g_stats_enabled) return; + (void)cpu; (void)sc; + (*((uint64_t*)ctr))++; +} + +static void StatsSlabAllocated(int sc) { + for (;;) { + // Use CPU id defensively; OK if called under depot lock from any CPU. + uint32_t cpu = lapic_get_id(); + heap_stats_per_cpu[cpu].slabs_allocated[sc]++; + return; + } +} // ================================================================================================= // Global Variables @@ -30,6 +71,10 @@ static Magazine* magazine_pool_head = NULL; static Magazine* DepotRefill(int size_class_index); static void DepotReturn(Magazine* mag, int size_class_index); static Slab* FindSlabForPointer(void* ptr); +#ifdef VF_CONFIG_HEAP_HYBRID +#include "mm/dynamic/rust/KernelHeapRust.h" +#include "drivers/APIC/APIC.h" +#endif // ================================================================================================= // Helper Functions @@ -66,7 +111,9 @@ static Magazine* AllocMagazine(void) { } Magazine* mag = magazine_pool_head; magazine_pool_head = magazine_pool_head->next; - FastMemset(mag, 0, sizeof(Magazine)); // Clear contents + // Avoid full memset for speed; only initialize fields we rely on + mag->count = 0; + mag->next = NULL; return mag; } @@ -93,6 +140,7 @@ void MagazineInit() { PANIC("Failed to initialize depot lock for magazine allocator"); } + // Initialize all depot and cache structures to zero. FastMemset(&depot.size_class_depots, 0, sizeof(depot.size_class_depots)); FastMemset(&per_cpu_caches, 0, sizeof(per_cpu_caches)); @@ -117,17 +165,10 @@ void* MagazineAlloc(size_t size) { int sc_idx = GetSizeClass(size); - // --- Large Allocation Fallback --- + // With the new three-tier dispatcher, MagazineAlloc should never be called for large allocations. + // If it is, this is a critical logic error in the dispatcher. if (sc_idx < 0) { - // Allocate space for the header + requested size - size_t total_size = size + sizeof(LargeBlockHeader); - void* raw_mem = VMemAlloc(total_size); - if (!raw_mem) { - return NULL; - } - LargeBlockHeader* header = (LargeBlockHeader*)raw_mem; - header->size = size; - return (void*)(header + 1); // Return pointer to user data + PANIC("MagazineAlloc called with oversized allocation"); } // --- Small Allocation Fast Path --- @@ -138,7 +179,19 @@ void* MagazineAlloc(size_t size) { // If the active magazine is not empty, pop a block and return it. if (mag && mag->count > 0) { mag->count--; - return mag->blocks[mag->count]; + StatsAdd(cpu_id, sc_idx, &heap_stats_per_cpu[cpu_id].alloc_fast_hits[sc_idx]); + + void* raw_block = mag->blocks[mag->count]; + MagazineBlockHeader* header = (MagazineBlockHeader*)raw_block; + header->magic = MAGAZINE_BLOCK_MAGIC; + header->sc_idx = sc_idx; + + void* user_ptr = (void*)(header + 1); + + if (g_validation_level == KHEAP_VALIDATION_FULL) { + FastMemset(user_ptr, 0xCD, size_classes[sc_idx]); + } + return user_ptr; } // --- Slow Path: Refill from Depot --- @@ -146,6 +199,12 @@ void* MagazineAlloc(size_t size) { mag = DepotRefill(sc_idx); rust_spinlock_unlock_irqrestore(depot.lock, flags); + if (mag) { + // stats: slow-path refill and magazine swap + StatsAdd(cpu_id, sc_idx, &heap_stats_per_cpu[cpu_id].alloc_slow_refills[sc_idx]); + StatsAdd(cpu_id, sc_idx, &heap_stats_per_cpu[cpu_id].magazine_swaps[sc_idx]); + } + if (!mag) { PrintKernelError("Heap: Failed to refill magazine, out of memory.\n"); return NULL; @@ -154,154 +213,115 @@ void* MagazineAlloc(size_t size) { // Install the new magazine and retry the allocation. cache->active_magazines[sc_idx] = mag; mag->count--; - return mag->blocks[mag->count]; + + void* raw_block = mag->blocks[mag->count]; + MagazineBlockHeader* header = (MagazineBlockHeader*)raw_block; + header->magic = MAGAZINE_BLOCK_MAGIC; + header->sc_idx = sc_idx; + + void* user_ptr = (void*)(header + 1); + + if (g_validation_level == KHEAP_VALIDATION_FULL) { + FastMemset(user_ptr, 0xCD, size_classes[sc_idx]); + } + return user_ptr; } /** * @brief Frees a block of memory. */ -void MagazineFree(void* ptr) { - if (!ptr) { - return; +static inline void PoisonOnFreeSmall(void* ptr, size_t size) { + if (g_validation_level == KHEAP_VALIDATION_NONE) return; + size_t sz = size; + if (g_validation_level == KHEAP_VALIDATION_BASIC) { + // Poison a small header and trailer region if possible + size_t n = sz < 32 ? sz : 32; + FastMemset(ptr, 0xDD, n); + } else { // FULL + FastMemset(ptr, 0xDD, sz); } +} - // --- Determine Slab and Size Class (lockless via page-hash) --- - Slab* slab = FindSlabForPointer(ptr); - if (!slab) { - // Assume it's a large allocation - LargeBlockHeader* header = (LargeBlockHeader*)ptr - 1; // Get pointer to header - // Basic sanity: header must be mapped; we trust VMem - VMemFree((void*)header, header->size + sizeof(LargeBlockHeader)); +void MagazineFree(void* ptr) { + if (!ptr) { return; } - // Bounds and alignment checks - if (ptr < slab->base_ptr || ptr >= (uint8_t*)slab->base_ptr + SLAB_SIZE) { - PrintKernelError("Heap: Freeing pointer outside of slab bounds!\n"); - return; - } - uintptr_t off = (uintptr_t)((uint8_t*)ptr - (uint8_t*)slab->base_ptr); - if ((off % slab->block_size) != 0) { - PrintKernelError("Heap: Free pointer not aligned to block size!\n"); - return; - } + // Check for small allocation magic number from our header + MagazineBlockHeader* header = (MagazineBlockHeader*)ptr - 1; - int sc_idx = slab->size_class_index; + if (header && header->magic == MAGAZINE_BLOCK_MAGIC) { + // --- Small Allocation Fast Path --- + int sc_idx = header->sc_idx; + void* raw_block = (void*)header; - // --- Small Allocation Fast Path (per-CPU, IRQ-disabled) --- - irq_flags_t iflags = save_irq_flags(); - cli(); - uint32_t cpu_id = GetCpuId(); - PerCpuCache* cache = &per_cpu_caches[cpu_id]; - Magazine* mag = cache->active_magazines[sc_idx]; + irq_flags_t iflags = save_irq_flags(); + cli(); + uint32_t cpu_id = GetCpuId(); + PerCpuCache* cache = &per_cpu_caches[cpu_id]; + Magazine* mag = cache->active_magazines[sc_idx]; - if (mag && mag->count < MAGAZINE_CAPACITY) { - mag->blocks[mag->count] = ptr; - mag->count++; + if (mag && mag->count < MAGAZINE_CAPACITY) { + PoisonOnFreeSmall(ptr, size_classes[sc_idx]); + mag->blocks[mag->count] = raw_block; + mag->count++; + StatsAdd(cpu_id, sc_idx, &heap_stats_per_cpu[cpu_id].free_fast_hits[sc_idx]); + restore_irq_flags(iflags); + return; + } restore_irq_flags(iflags); - return; - } - restore_irq_flags(iflags); - // --- Slow Path: Return to Depot and/or install new magazine --- - uint64_t flags = rust_spinlock_lock_irqsave(depot.lock); - // Reload per-CPU magazine under lock in case another context changed it - cpu_id = GetCpuId(); - cache = &per_cpu_caches[cpu_id]; - mag = cache->active_magazines[sc_idx]; + // --- Slow Path: Return to Depot --- + uint64_t flags = rust_spinlock_lock_irqsave(depot.lock); + cpu_id = GetCpuId(); // Reload per-CPU magazine under lock + cache = &per_cpu_caches[cpu_id]; + mag = cache->active_magazines[sc_idx]; - if (mag) { - DepotReturn(mag, sc_idx); + if (mag) { + DepotReturn(mag, sc_idx); + } + + Magazine* new_mag = AllocMagazine(); + if (!new_mag) { + PANIC("Magazine pool exhausted during free operation!"); + } + PoisonOnFreeSmall(ptr, size_classes[sc_idx]); + new_mag->blocks[0] = raw_block; + new_mag->count = 1; + new_mag->next = NULL; + cache->active_magazines[sc_idx] = new_mag; + + StatsAdd(cpu_id, sc_idx, &heap_stats_per_cpu[cpu_id].free_slow_paths[sc_idx]); + StatsAdd(cpu_id, sc_idx, &heap_stats_per_cpu[cpu_id].magazine_swaps[sc_idx]); + + rust_spinlock_unlock_irqrestore(depot.lock, flags); + return; } - Magazine* new_mag = AllocMagazine(); - if (!new_mag) { - PANIC("Magazine pool exhausted during free operation!"); + // --- Large or Foreign Allocation --- + LargeBlockHeader* large_header = (LargeBlockHeader*)ptr - 1; + if (large_header && large_header->magic == LARGE_BLOCK_MAGIC) { + if (g_validation_level != KHEAP_VALIDATION_NONE) { + FastMemset(ptr, 0xDD, large_header->size); + } + VMemFree((void*)large_header, large_header->size + sizeof(LargeBlockHeader)); + return; } - new_mag->blocks[0] = ptr; - new_mag->count = 1; - new_mag->next = NULL; - cache->active_magazines[sc_idx] = new_mag; - rust_spinlock_unlock_irqrestore(depot.lock, flags); +#ifdef VF_CONFIG_HEAP_HYBRID + // Delegate to Rust heap for unknown blocks + rust_kfree(ptr); + return; +#else + PANIC("MagazineFree: unknown pointer freed"); +#endif } // ================================================================================================= // Depot Logic (Slow Path) - Implementation // ================================================================================================= -/** - * @brief Finds the Slab that contains the given pointer. - * This is a linear scan and can be slow. Needs optimization for production. - * Assumes depot.lock is held by the caller. - */ -// Lightweight slab hash for O(1) pointer-to-slab lookup -#define SLAB_HASH_SIZE 4096 - -typedef struct SlabHashNode { - uintptr_t page_key; // (addr >> PAGE_SHIFT) - Slab* slab; - struct SlabHashNode* next; -} SlabHashNode; - -static SlabHashNode* slab_hash[SLAB_HASH_SIZE]; -static inline uint32_t slab_hash_index(uintptr_t page_key) { - return (uint32_t)(page_key & (SLAB_HASH_SIZE - 1)); -} - -static void SlabHashInsert(Slab* slab) { - // Map each page in the slab region to this slab - uintptr_t start = (uintptr_t)slab->base_ptr; - for (uintptr_t addr = start; addr < start + SLAB_SIZE; addr += PAGE_SIZE) { - uintptr_t key = addr >> PAGE_SHIFT; - uint32_t idx = slab_hash_index(key); - SlabHashNode* node = (SlabHashNode*)VMemAlloc(sizeof(SlabHashNode)); - if (!node) { - PANIC("SlabHashInsert: OOM"); - } - node->page_key = key; - node->slab = slab; - node->next = slab_hash[idx]; - slab_hash[idx] = node; - } -} - -static void SlabHashRemove(Slab* slab) { - uintptr_t start = (uintptr_t)slab->base_ptr; - for (uintptr_t addr = start; addr < start + SLAB_SIZE; addr += PAGE_SIZE) { - uintptr_t key = addr >> PAGE_SHIFT; - uint32_t idx = slab_hash_index(key); - SlabHashNode** pp = &slab_hash[idx]; - while (*pp) { - if ((*pp)->page_key == key) { - SlabHashNode* dead = *pp; - *pp = dead->next; - VMemFree(dead, sizeof(SlabHashNode)); - break; - } - pp = &(*pp)->next; - } - } -} - -static Slab* FindSlabForPointer(void* ptr) { - uintptr_t key = ((uintptr_t)ptr) >> PAGE_SHIFT; - uint32_t idx = slab_hash_index(key); - SlabHashNode* node = slab_hash[idx]; - while (node) { - if (node->page_key == key) { - Slab* slab = node->slab; - // Verify bounds - if (ptr >= slab->base_ptr && ptr < (uint8_t*)slab->base_ptr + SLAB_SIZE) { - return slab; - } - return NULL; - } - node = node->next; - } - return NULL; -} /** * @brief Gets a new or partially full magazine from the depot. @@ -310,10 +330,17 @@ static Slab* FindSlabForPointer(void* ptr) { */ static Magazine* DepotRefill(int size_class_index) { SizeClassDepot* sc_depot = &depot.size_class_depots[size_class_index]; - size_t block_size = size_classes[size_class_index]; Magazine* mag = NULL; - // 1. Prioritize getting a partial magazine + // 1. Prefer a full magazine for maximum fast-path allocations + if (sc_depot->full_magazines) { + mag = sc_depot->full_magazines; + sc_depot->full_magazines = mag->next; + mag->next = NULL; + return mag; + } + + // 2. Next, try a partial magazine if (sc_depot->partial_magazines) { mag = sc_depot->partial_magazines; sc_depot->partial_magazines = mag->next; @@ -321,11 +348,12 @@ static Magazine* DepotRefill(int size_class_index) { return mag; } - // 2. If no partial magazines, try to get an empty magazine and fill it from a slab + // 3. If no magazines with content, get an empty one (or create) and fill from slabs if (sc_depot->empty_magazines) { mag = sc_depot->empty_magazines; sc_depot->empty_magazines = mag->next; mag->next = NULL; + mag->count = 0; } else { // If no empty magazines in depot, allocate a new one from the pool mag = AllocMagazine(); @@ -352,7 +380,7 @@ static Magazine* DepotRefill(int size_class_index) { current_slab = current_slab->next; } - // 3. If no existing slabs have free blocks, allocate a new slab + // 4. If no existing slabs have free blocks, allocate a new slab Slab* new_slab = VMemAlloc(sizeof(Slab)); // Allocate Slab metadata if (!new_slab) { FreeMagazine(mag); // Free the magazine we just allocated @@ -366,20 +394,22 @@ static Magazine* DepotRefill(int size_class_index) { FreeMagazine(mag); return NULL; } - FastMemset(mem, 0, SLAB_SIZE); + // Avoid zeroing the entire slab for speed; blocks are uninitialized by design. + + size_t chunk_size = sizeof(MagazineBlockHeader) + size_classes[size_class_index]; new_slab->alloc_base = mem; new_slab->alloc_size = SLAB_SIZE; new_slab->base_ptr = mem; // Keep identical; SLAB_SIZE granularity not required - new_slab->block_size = block_size; + new_slab->block_size = chunk_size; new_slab->size_class_index = size_class_index; - new_slab->total_blocks = SLAB_SIZE / block_size; + new_slab->total_blocks = SLAB_SIZE / chunk_size; new_slab->free_blocks = new_slab->total_blocks; new_slab->cookie = rdtsc() ^ ((uintptr_t)new_slab); // Build free list within the new slab for (int i = 0; i < new_slab->total_blocks; i++) { - void* block = (uint8_t*)new_slab->base_ptr + (i * block_size); + void* block = (uint8_t*)new_slab->base_ptr + (i * chunk_size); *((void**)block) = new_slab->free_list_head; // Push to free list new_slab->free_list_head = block; } @@ -387,8 +417,8 @@ static Magazine* DepotRefill(int size_class_index) { // Add new slab to depot's slab list new_slab->next = sc_depot->slabs; sc_depot->slabs = new_slab; - // Insert into fast slab lookup hash - SlabHashInsert(new_slab); + // Stats: track slab allocation + StatsSlabAllocated(size_class_index); // Fill the magazine from the new slab for (int i = 0; i < MAGAZINE_CAPACITY && new_slab->free_blocks > 0; i++) { @@ -411,61 +441,19 @@ static Magazine* DepotRefill(int size_class_index) { static void DepotReturn(Magazine* mag, int size_class_index) { SizeClassDepot* sc_depot = &depot.size_class_depots[size_class_index]; - // Store original count before returning blocks to slabs - uint32_t original_mag_count = mag->count; - - // Return blocks from the magazine to their respective slabs. - for (int i = 0; i < original_mag_count; i++) { - void* block_ptr = mag->blocks[i]; - Slab* slab = FindSlabForPointer(block_ptr); // depot.lock is held by caller - if (slab) { - // Basic check: ensure the pointer is within the slab's bounds - if (block_ptr < slab->base_ptr || block_ptr >= (uint8_t*)slab->base_ptr + SLAB_SIZE) { - PrintKernelError("Heap: DepotReturn: Freeing pointer outside of slab bounds!\n"); - continue; // Skip this block - } - - // Push block back to slab's free list - *((void**)block_ptr) = slab->free_list_head; - slab->free_list_head = block_ptr; - slab->free_blocks++; - - // Check if slab is now completely empty and can be freed - if (slab->free_blocks == slab->total_blocks) { - // Remove slab from sc_depot->slabs list - Slab* prev_slab = NULL; - Slab* current_slab = sc_depot->slabs; - while (current_slab && current_slab != slab) { - prev_slab = current_slab; - current_slab = current_slab->next; - } - - if (current_slab == slab) { - if (prev_slab) { - prev_slab->next = slab->next; - } - else { - sc_depot->slabs = slab->next; - } - // Free the slab's memory and metadata - SlabHashRemove(slab); - VMemFree(slab->alloc_base, slab->alloc_size); - VMemFree(slab, sizeof(Slab)); - } - } - } - } - mag->count = 0; // Magazine is now empty - - // Categorize the magazine and add to appropriate list based on its original state - if (original_mag_count == MAGAZINE_CAPACITY) { + // Do not spill blocks back to slabs; keep magazines intact for speed. + // Simply classify the magazine based on its current fill level. + if (mag->count >= MAGAZINE_CAPACITY) { + mag->count = MAGAZINE_CAPACITY; mag->next = sc_depot->full_magazines; sc_depot->full_magazines = mag; - } else if (original_mag_count > 0) { + } else if (mag->count > 0) { mag->next = sc_depot->partial_magazines; sc_depot->partial_magazines = mag; - } else { // Empty: return to magazine pool - FreeMagazine(mag); + } else { + // Empty magazine: keep it in the depot's empty list for reuse + mag->next = sc_depot->empty_magazines; + sc_depot->empty_magazines = mag; } } @@ -495,22 +483,87 @@ void* MagazineReallocate(void* ptr, size_t size) { return NULL; } - // Get original size from header for large allocations, or from slab for small. + // Get original size from header for large allocations, or from our new header for small. size_t old_size = 0; - Slab* slab = FindSlabForPointer(ptr); - if (slab) { - old_size = slab->block_size; // For small allocations, size is block_size + MagazineBlockHeader* small_header = (MagazineBlockHeader*)ptr - 1; + + if (small_header && small_header->magic == MAGAZINE_BLOCK_MAGIC) { + old_size = size_classes[small_header->sc_idx]; } else { - // Assume large allocation - LargeBlockHeader* header = (LargeBlockHeader*)ptr - 1; - old_size = header->size; + LargeBlockHeader* large_header = (LargeBlockHeader*)ptr - 1; + if (large_header && large_header->magic == LARGE_BLOCK_MAGIC) { + old_size = large_header->size; + } else { + // In a hybrid system, realloc should be handled by a dispatcher + // that knows which allocator owns the pointer. + // Since we can't know the size, we can't safely reallocate. + PANIC("MagazineReallocate: unknown pointer type"); + } } void* new_ptr = MagazineAlloc(size); if (!new_ptr) { return NULL; } - FastMemcpy(new_ptr, ptr, (size < old_size) ? size : old_size); + + size_t copy_size = (size < old_size) ? size : old_size; + FastMemcpy(new_ptr, ptr, copy_size); MagazineFree(ptr); return new_ptr; -} \ No newline at end of file +} + +// ================================================================================================= +// Stats printing +// ================================================================================================= +static void PrintOneStatLine(int sc_idx, size_t block_sz, uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e, uint64_t f) { + PrintKernelF("SC[%d] sz=%zu | alloc_fast=%llu alloc_slow=%llu free_fast=%llu free_slow=%llu swaps=%llu slabs=%llu\n", + sc_idx, block_sz, a, b, c, d, e, f); +} + +void MagazineFlushCaches(void) { + uint64_t flags = rust_spinlock_lock_irqsave(depot.lock); + // Return all per-CPU active magazines to the depot lists and clear them + for (int cpu = 0; cpu < MAX_CPU_CORES; cpu++) { + PerCpuCache* cache = &per_cpu_caches[cpu]; + for (int sc = 0; sc < NUM_SIZE_CLASSES; sc++) { + Magazine* mag = cache->active_magazines[sc]; + if (mag) { + DepotReturn(mag, sc); + cache->active_magazines[sc] = NULL; + } + } + } + rust_spinlock_unlock_irqrestore(depot.lock, flags); +} + +void MagazineSetValidationLevel(int level) { + if (level < KHEAP_VALIDATION_NONE) level = KHEAP_VALIDATION_NONE; + if (level > KHEAP_VALIDATION_FULL) level = KHEAP_VALIDATION_FULL; + g_validation_level = level; +} + +void MagazineSetPerfMode(int mode) { + // mode==0: disable stats; nonzero: enable + g_stats_enabled = (mode != 0); +} + +void MagazinePrintStats(void) { + PrintKernel("\n[Heap] Magazine allocator statistics\n"); + uint64_t totals[6] = {0,0,0,0,0,0}; + for (int sc = 0; sc < NUM_SIZE_CLASSES; sc++) { + uint64_t a=0,b=0,c=0,d=0,e=0,f=0; + for (int cpu = 0; cpu < MAX_CPU_CORES; cpu++) { + a += heap_stats_per_cpu[cpu].alloc_fast_hits[sc]; + b += heap_stats_per_cpu[cpu].alloc_slow_refills[sc]; + c += heap_stats_per_cpu[cpu].free_fast_hits[sc]; + d += heap_stats_per_cpu[cpu].free_slow_paths[sc]; + e += heap_stats_per_cpu[cpu].magazine_swaps[sc]; + f += heap_stats_per_cpu[cpu].slabs_allocated[sc]; + } + totals[0]+=a; totals[1]+=b; totals[2]+=c; totals[3]+=d; totals[4]+=e; totals[5]+=f; + PrintOneStatLine(sc, size_classes[sc], a,b,c,d,e,f); + } + PrintKernel("-----------------------------------------------------------\n"); + PrintKernelF("TOTAL | alloc_fast=%llu alloc_slow=%llu free_fast=%llu free_slow=%llu swaps=%llu slabs=%llu\n", + totals[0], totals[1], totals[2], totals[3], totals[4], totals[5]); +} diff --git a/mm/dynamic/c/Magazine.h b/mm/dynamic/c/Magazine.h index c230df6..3d7b81c 100644 --- a/mm/dynamic/c/Magazine.h +++ b/mm/dynamic/c/Magazine.h @@ -41,8 +41,18 @@ static const size_t size_classes[NUM_SIZE_CLASSES] = { * @brief Header for large allocations that bypass the magazine system. * This allows tracking the size of such allocations for proper freeing. */ +#define LARGE_BLOCK_MAGIC 0x4C42484D41474E31ULL /* 'LBHmagN1' */ +#define MAGAZINE_BLOCK_MAGIC 0x4D42484D41474E31ULL /* 'MBHmagN1' */ + +typedef struct { + uint64_t magic; + uint8_t sc_idx; +} MagazineBlockHeader; + + typedef struct LargeBlockHeader { - size_t size; // The original requested size of the allocation. + uint64_t magic; // Magic to identify magazine large blocks + size_t size; // The original requested size of the allocation. } LargeBlockHeader; /** @@ -118,5 +128,9 @@ void* MagazineAlloc(size_t size); void MagazineFree(void* ptr); void* MagazineAllocate(size_t num, size_t size); void* MagazineReallocate(void* ptr, size_t size); +void MagazinePrintStats(void); +void MagazineSetValidationLevel(int level); +void MagazineFlushCaches(void); +void MagazineSetPerfMode(int mode); #endif // MAGAZINE_HEAP_H diff --git a/mm/dynamic/rust/src/backend.rs b/mm/dynamic/rust/src/backend.rs index 0c92cd8..dc38906 100644 --- a/mm/dynamic/rust/src/backend.rs +++ b/mm/dynamic/rust/src/backend.rs @@ -40,15 +40,15 @@ pub struct HeapBlock { impl HeapBlock { #[inline(always)] pub(crate) fn is_free(&self) -> bool { self.is_free != 0 } - + #[inline(always)] pub(crate) fn set_free(&mut self, free: bool) { self.is_free = if free { 1 } else { 0 }; } - + #[inline(always)] pub(crate) fn in_cache(&self) -> bool { self.in_cache != 0 } - + #[inline(always)] pub(crate) fn set_in_cache(&mut self, cached: bool) { self.in_cache = if cached { 1 } else { 0 }; @@ -156,17 +156,17 @@ impl HeapBlock { if self.next.is_null() || !(*self.next).is_free() || (*self.next).in_cache() { return false; } - + if !self.are_adjacent(self.next) { return false; } - + let next_block = self.next; self.size += size_of::() + (*next_block).size; self.next = (*next_block).next; - + if !self.next.is_null() { (*self.next).prev = self; } - + self.checksum = compute_checksum(self as *const HeapBlock); COALESCE_COUNTER.fetch_add(1, Ordering::Relaxed); true @@ -264,11 +264,11 @@ unsafe fn find_free_block(size: usize) -> *mut HeapBlock { let block = &*current; if block.is_free() && !block.in_cache() && block.size >= size { if block.size == size { return current; } - + if size <= 1024 && block.size <= size * 2 { return current; // Close fit for small allocations } - + if block.size < best_size { best_fit = current; best_size = block.size; @@ -316,7 +316,7 @@ pub unsafe fn rust_kmalloc_backend(size: usize) -> *mut u8 { let actual_size = SIZE_CLASSES[size_class]; let mut heap = HEAP.lock(); let cache = &mut heap.fast_caches[size_class]; - + if !cache.free_list.is_null() { let block = cache.free_list; cache.free_list = (*block).cache_next; @@ -337,7 +337,7 @@ pub unsafe fn rust_kmalloc_backend(size: usize) -> *mut u8 { let block = find_free_block(aligned_size); let block = if !block.is_null() { if !(*block).validate() { return ptr::null_mut(); } - + if (*block).size > aligned_size { split_block(block, aligned_size); } @@ -402,7 +402,7 @@ pub unsafe fn rust_kfree_backend(ptr: *mut u8) { if block_size == SIZE_CLASSES[size_class] { // Zero user data for security ptr::write_bytes(ptr, 0, block_size); - + let mut heap = HEAP.lock(); let cache = &mut heap.fast_caches[size_class]; if cache.count < FAST_CACHE_SIZE as i32 { @@ -419,11 +419,11 @@ pub unsafe fn rust_kfree_backend(ptr: *mut u8) { // Standard free path with coalescing ptr::write_bytes(ptr, 0, block_size); (*block).init(block_size, true); - + // Coalesce with adjacent blocks let current = block; while (*current).coalesce_with_next() {} - + // Try coalescing with previous if !(*current).prev.is_null() && (*(*current).prev).is_free() { let prev = (*current).prev; diff --git a/vfcompositor/Compositor.c b/vfcompositor/Compositor.c index 364a09a..5c6ea40 100644 --- a/vfcompositor/Compositor.c +++ b/vfcompositor/Compositor.c @@ -13,6 +13,7 @@ #include "SpinlockRust.h" #include "StringOps.h" #include "Vesa.h" +#include "app/GUIShell.h" #ifndef MAX #define MAX(a, b) ((a) > (b) ? (a) : (b)) @@ -25,6 +26,27 @@ CompositorContext g_compositor_ctx; +#define MOUSE_CURSOR_WIDTH 16 +#define MOUSE_CURSOR_HEIGHT 16 +const uint32_t mouse_cursor_bitmap[MOUSE_CURSOR_HEIGHT * MOUSE_CURSOR_WIDTH] = { + 0xFF000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // X + 0xFF000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // XW + 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // XWW + 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // XWWW + 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // XWWWW + 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // XWWWWW + 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // XWWWWWW + 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // XWWWWWWW + 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // XWWWWWWWW + 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // XWWWWWWX + 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // XWWWWWX + 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // XWWWWX + 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // XWWWX + 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // XWWX + 0xFF000000, 0xFFFFFFFF, 0xFF000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // XWX + 0xFF000000, 0xFF000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 // XX +}; + void RequestDestroyWindow(CompositorContext* ctx, Window* w) { if (!w) return; // avoid duplicates @@ -105,12 +127,15 @@ Window* GetWindowByTitle(CompositorContext* ctx, const char* title) { static void DrawMouseCursor(CompositorContext* ctx) { if (!ctx->g_vbe_info || !ctx->g_compositor_buffer) return; - for (int y = 0; y < 10 && (ctx->g_mouse_y + y) < ctx->g_vbe_info->height; y++) { - for (int x = 0; x < 10 && (ctx->g_mouse_x + x) < ctx->g_vbe_info->width; x++) { + for (int y = 0; y < MOUSE_CURSOR_HEIGHT; y++) { + for (int x = 0; x < MOUSE_CURSOR_WIDTH; x++) { int screen_x = ctx->g_mouse_x + x; int screen_y = ctx->g_mouse_y + y; - if (screen_x >= 0 && screen_y >= 0) { - ctx->g_compositor_buffer[screen_y * ctx->g_vbe_info->width + screen_x] = 0xFFFFFF; // White cursor + if (screen_x >= 0 && screen_y >= 0 && screen_x < (int)ctx->g_vbe_info->width && screen_y < (int)ctx->g_vbe_info->height) { + uint32_t pixel_color = mouse_cursor_bitmap[y * MOUSE_CURSOR_WIDTH + x]; + if (pixel_color != 0x00000000) { // Assuming 0x00000000 is transparent + ctx->g_compositor_buffer[screen_y * ctx->g_vbe_info->width + screen_x] = pixel_color; + } } } } @@ -415,7 +440,7 @@ void VFCompositor(void) { } CompositorInit(&g_compositor_ctx); - Window* w = CreateWindow(&g_compositor_ctx, 50, 50, 480, 360, "VFCompositor Help Menu"); + Window* w = CreateWindow(&g_compositor_ctx, 50, 50, 480, 360, "VFCompositor Help Menu", 0); if (w) { w->minimized = false; WindowFill(w, WINDOW_BG); @@ -424,10 +449,12 @@ void VFCompositor(void) { WindowPrintString(&g_compositor_ctx, w, "[--- Version: v0.0.2-development4 ---]\n"); WindowPrintString(&g_compositor_ctx, w, "CTRL + W: Closes active window\n"); WindowPrintString(&g_compositor_ctx, w, "CTRL + M: Minimize active window\n"); - WindowPrintString(&g_compositor_ctx, w, "CTRL + T: Make the active window move with your mouse\n"); - WindowPrintString(&g_compositor_ctx, w, "CTRL + T: Create a blank window\n"); + WindowPrintString(&g_compositor_ctx, w, "CTRL + L: Make the active window move with your mouse\n"); + WindowPrintString(&g_compositor_ctx, w, "CTRL + T: Creates new window\n"); + WindowPrintString(&g_compositor_ctx, w, "CTRL + S: Creates VFShell GUI\n"); WindowPrintString(&g_compositor_ctx, w, "CTRL + : Quit VFCompositor\n"); WindowPrintString(&g_compositor_ctx, w, "ATL + : Switches between window\n"); + WindowDrawRect(w, 0, 25, w->rect.width, w->rect.height - 25, TERMINAL_TEXT); } while (1) { @@ -442,20 +469,18 @@ void VFCompositor(void) { VFCompositorShutdown(&g_compositor_ctx); return; } - if (c == PS2_CalcCombo(K_CTRL, 'N')) { - Window* w = CreateWindow(&g_compositor_ctx, 50, 50, 480, 360, "Window"); - if (w) { - w->minimized = false; - WindowFill(w, WINDOW_BG); - WindowDrawRect(w, 0, 0, w->rect.width, 20, TITLE_BAR); - g_compositor_ctx.g_focused_window = w; - WindowPrintString(&g_compositor_ctx, w, "This is a test window.\n"); - } - } else if (c == PS2_CalcCombo(K_CTRL, 'W')) { + if (c == PS2_CalcCombo(K_CTRL, 'T')) { + Window* w = CreateWindow(&g_compositor_ctx, 50, 50, 480, 360, "Window", 0); + w->minimized = false; + WindowFill(w, WINDOW_BG); + WindowPrintString(&g_compositor_ctx, w, "Blank window\n"); + } else if (c == PS2_CalcCombo(K_CTRL, 'W')) { Window* w = g_compositor_ctx.g_focused_window; if (w) { RequestDestroyWindow(&g_compositor_ctx, w); } + } else if (c == PS2_CalcCombo(K_CTRL, 'S')) { + CreateProcess("VFShellGUI", VFShellProcess); } else if (c == PS2_CalcCombo(K_CTRL, 'M')) { Window* w = g_compositor_ctx.g_focused_window; if (w) { w->minimized = !w->minimized; } - } else if (c == PS2_CalcCombo(K_CTRL, 'T')) { + } else if (c == PS2_CalcCombo(K_CTRL, 'L')) { Window* w = g_compositor_ctx.g_focused_window; if (w) { w->is_moving = true; w->move_offset_x = g_compositor_ctx.g_mouse_x - w->rect.x; } } else if (c == PS2_CalcCombo(K_SUPER, 'W')) { Window* w = g_compositor_ctx.g_focused_window; if (w) { RequestDestroyWindow(&g_compositor_ctx, w); } @@ -483,8 +508,8 @@ void VFCompositor(void) { int min_x = close_x - 2 - btn_size; WindowDrawRect(current, min_x, pad, btn_size, btn_size, BORDER); WindowDrawRect(current, close_x, pad, btn_size, btn_size, ERROR_COLOR); - WindowDrawChar(current, min_x + 3, pad + 1, '-', TERMINAL_TEXT); - WindowDrawChar(current, close_x + 3, pad + 1, 'x', TERMINAL_TEXT); + WindowDrawChar(current, min_x + 3, pad, '-', TERMINAL_TEXT); + WindowDrawChar(current, close_x + 3, pad - 1, 'x', TERMINAL_TEXT); // Redraw text content int text_y = 25; // Start below title bar @@ -540,7 +565,7 @@ void CompositorInit(CompositorContext* ctx) { ctx->g_mouse_y = ctx->g_vbe_info->height / 2; } -Window* CreateWindow(CompositorContext* ctx, int x, int y, int width, int height, const char* title) { +Window* CreateWindow(CompositorContext* ctx, int x, int y, int width, int height, const char* title, uint32_t owner_pid) { Window* window = (Window*)KernelMemoryAlloc(sizeof(Window)); if (!window) return NULL; @@ -553,6 +578,7 @@ Window* CreateWindow(CompositorContext* ctx, int x, int y, int width, int height window->move_offset_x = 0; window->move_offset_y = 0; window->minimized = false; + window->owner_pid = owner_pid; // Store the owner PID window->next = NULL; window->prev = NULL; @@ -626,6 +652,11 @@ void DestroyWindow(CompositorContext* ctx, Window* window) { if (window->title) KernelFree((void*)window->title); KernelFree(window); + // Kill the owning process if it exists + if (window->owner_pid != 0) { + KillProcess(window->owner_pid); + } + // Reassign focus to a safe window if (was_focused) { ctx->g_focused_window = ctx->g_window_list_tail; @@ -746,7 +777,7 @@ void OnMouseButtonDown(CompositorContext* ctx, int x, int y, uint8_t button) { // Start button if (x >= 2 && x < START_BTN_WIDTH - 2) { if (!ctx->g_start_menu_window) { - ctx->g_start_menu_window = CreateWindow(ctx, 2, taskbar_y0 - 200, 220, 180, "Start"); + ctx->g_start_menu_window = CreateWindow(ctx, 2, taskbar_y0 - 200, 220, 180, "Start", 0); if (ctx->g_start_menu_window) { WindowFill(ctx->g_start_menu_window, WINDOW_BG); WindowDrawRect(ctx->g_start_menu_window, 0, 0, ctx->g_start_menu_window->rect.width, 20, TITLE_BAR); diff --git a/vfcompositor/Compositor.h b/vfcompositor/Compositor.h index ceddf14..0a5d4e3 100644 --- a/vfcompositor/Compositor.h +++ b/vfcompositor/Compositor.h @@ -66,7 +66,7 @@ extern CompositorContext g_compositor_ctx; // Window management functions void CompositorInit(CompositorContext* ctx); Window* GetWindowByTitle(CompositorContext* ctx, const char* title); -Window* CreateWindow(CompositorContext* ctx, int x, int y, int width, int height, const char* title); +Window* CreateWindow(CompositorContext* ctx, int x, int y, int width, int height, const char* title, uint32_t owner_pid); void DestroyWindow(CompositorContext* ctx, Window* window); void RequestDestroyWindow(CompositorContext* ctx, Window* w); diff --git a/vfcompositor/app/GUIShell.c b/vfcompositor/app/GUIShell.c new file mode 100644 index 0000000..4f1b1a5 --- /dev/null +++ b/vfcompositor/app/GUIShell.c @@ -0,0 +1,47 @@ +#include "Compositor.h" +#include "Shell.h" +#include "GUIShell.h" +#include "Keyboard.h" +#include "Pallete.h" +#include "Scheduler.h" + +static char command_buffer[256]; +static int cmd_pos = 0; +extern char current_dir[256]; + +extern CompositorContext g_compositor_ctx; +extern void ConsoleSetWindowPrint(Window* w); + +void VFShellProcess(void) { + Window* w = CreateWindow(&g_compositor_ctx, 0, 0, 480, 360, "VFShell", GetCurrentProcess()->pid); + if (!w) return; + w->minimized = false; + WindowFill(w, WINDOW_BG); + WindowPrintString(&g_compositor_ctx, w, "[--- VFShell - GUI ---]\n/>"); + ConsoleSetWindowPrint(w); + while (1) { + if (HasInput()) { + const char c = GetChar(); + + if (c == '\n') { + WindowPrintString(&g_compositor_ctx, w, "\n"); + command_buffer[cmd_pos] = 0; + ExecuteCommand(command_buffer); + cmd_pos = 0; + WindowPrintString(&g_compositor_ctx, w, current_dir); + WindowPrintString(&g_compositor_ctx, w,"> "); + } else if (c == '\b') { + if (cmd_pos > 0) { + cmd_pos--; + WindowPrintString(&g_compositor_ctx, w, "\b \b"); // Visual backspace + } + } else if (cmd_pos < 255) { + command_buffer[cmd_pos++] = c; + const char str[2] = {c, 0}; + WindowPrintString(&g_compositor_ctx, w, str); + } + } else { + Yield(); + } + } +} diff --git a/vfcompositor/app/GUIShell.h b/vfcompositor/app/GUIShell.h new file mode 100644 index 0000000..a43ad07 --- /dev/null +++ b/vfcompositor/app/GUIShell.h @@ -0,0 +1,6 @@ +#ifndef VOIDFRAME_GUISHELL_H +#define VOIDFRAME_GUISHELL_H + +void VFShellProcess(); + +#endif //VOIDFRAME_GUISHELL_H \ No newline at end of file