diff --git a/.github/workflows/qemu-test.yml b/.github/workflows/qemu-test.yml index 9efa83e..51829d7 100644 --- a/.github/workflows/qemu-test.yml +++ b/.github/workflows/qemu-test.yml @@ -25,6 +25,7 @@ jobs: sudo apt-get update sudo apt-get install -y \ build-essential \ + clang \ cmake \ nasm \ qemu-system-x86 \ @@ -49,7 +50,7 @@ jobs: run: | mkdir -p build cd build - cmake .. -DCMAKE_TOOLCHAIN_FILE=../build/Release/generators/conan_toolchain.cmake + CC=clang CXX=clang++ cmake .. -DCMAKE_TOOLCHAIN_FILE=../build/Release/generators/conan_toolchain.cmake - name: Build bootloader and kernel run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9908761..2cc3df1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,6 +24,7 @@ jobs: sudo apt-get update sudo apt-get install -y \ build-essential \ + clang \ cmake \ nasm \ qemu-system-x86 \ @@ -47,7 +48,7 @@ jobs: run: | mkdir -p build cd build - cmake .. -DCMAKE_TOOLCHAIN_FILE=../build/Release/generators/conan_toolchain.cmake + CC=clang CXX=clang++ cmake .. -DCMAKE_TOOLCHAIN_FILE=../build/Release/generators/conan_toolchain.cmake - name: Build bootloader and kernel run: | diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 20f373d..1e0c9a2 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -22,7 +22,7 @@ jobs: set -e # Exit on any error sudo apt-get update - sudo apt-get install -y build-essential cmake python3 python3-pip + sudo apt-get install -y build-essential clang cmake python3 python3-pip # Install Conan pip3 install conan @@ -38,7 +38,7 @@ jobs: run: | mkdir -p build cd build - cmake .. -DCMAKE_TOOLCHAIN_FILE=../build/Release/generators/conan_toolchain.cmake + CC=clang CXX=clang++ cmake .. -DCMAKE_TOOLCHAIN_FILE=../build/Release/generators/conan_toolchain.cmake - name: Build unit tests run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 0888c4a..563a655 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,17 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) # Export compile commands for IDE support set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +# Compiler detection and configuration +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + message(STATUS "Using Clang compiler") + set(USING_CLANG TRUE) +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + message(STATUS "Using GCC compiler") + set(USING_GCC TRUE) +else() + message(WARNING "Unsupported compiler: ${CMAKE_CXX_COMPILER_ID}") +endif() + # Default to Release build if not specified if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) diff --git a/Dockerfile b/Dockerfile index 320e0ca..caf276f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,8 +13,9 @@ WORKDIR /metalos RUN apt-get update && apt-get install -y \ # Build essentials build-essential \ - gcc \ - g++ \ + clang \ + clang++ \ + lld \ nasm \ make \ cmake \ @@ -62,6 +63,8 @@ RUN cp /usr/share/OVMF/OVMF_CODE.fd /metalos/deps/ovmf/ 2>/dev/null || \ # Set environment variables ENV PATH="/metalos/tools:${PATH}" ENV METALOS_ROOT="/metalos" +ENV CC=clang +ENV CXX=clang++ # Default command CMD ["/bin/bash"] diff --git a/README.md b/README.md index 95d45af..aea37e9 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ See [docs/ROADMAP.md](docs/ROADMAP.md) for detailed phase breakdown. ## Building -MetalOS uses **CMake** as its build system for a modern, cross-platform build experience. +MetalOS uses **CMake** as its build system for a modern, cross-platform build experience. **Clang** is the default compiler. ### Quick Start (CMake) @@ -87,6 +87,24 @@ ninja ninja qemu ``` +### Building with GCC + +MetalOS supports both Clang (default) and GCC compilers. To build with GCC: + +```bash +mkdir build && cd build +CC=gcc CXX=g++ cmake .. +cmake --build . +``` + +Or with Ninja: + +```bash +mkdir build && cd build +CC=gcc CXX=g++ cmake -G Ninja .. +ninja +``` + ### Conan (With Package Management) ```bash diff --git a/bootloader/CMakeLists.txt b/bootloader/CMakeLists.txt index 627aa12..fb73049 100644 --- a/bootloader/CMakeLists.txt +++ b/bootloader/CMakeLists.txt @@ -30,12 +30,21 @@ set(UEFI_CFLAGS ) # Linker flags for UEFI -set(UEFI_LDFLAGS - -shared - -Bsymbolic - -nostdlib - -znocombreloc -) +if(CMAKE_C_COMPILER_ID MATCHES "Clang") + set(UEFI_LDFLAGS + -shared + -Bsymbolic + -nostdlib + -Wl,-z,nocombreloc + ) +else() + set(UEFI_LDFLAGS + -shared + -Bsymbolic + -nostdlib + -znocombreloc + ) +endif() # Create object library add_library(bootloader_obj OBJECT ${BOOTLOADER_SOURCES}) diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 72e139d..618cdd8 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -3,16 +3,16 @@ cmake_minimum_required(VERSION 3.16) -project(MetalOS_Kernel C ASM) +project(MetalOS_Kernel CXX ASM) # Source files # Note: Using GLOB for simplicity. For production, consider listing files explicitly. # If new files aren't detected, re-run cmake configuration. -file(GLOB KERNEL_C_SOURCES "src/*.c") +file(GLOB KERNEL_CXX_SOURCES "src/*.cpp") file(GLOB KERNEL_ASM_SOURCES "src/*.asm") set(KERNEL_SOURCES - ${KERNEL_C_SOURCES} + ${KERNEL_CXX_SOURCES} ) # Find NASM assembler @@ -42,7 +42,7 @@ else() endif() # Compiler flags for kernel -set(KERNEL_CFLAGS +set(KERNEL_CXXFLAGS -Wall -Wextra -Werror @@ -50,26 +50,49 @@ set(KERNEL_CFLAGS -fno-stack-protector -mno-red-zone -mcmodel=large + -fno-exceptions + -fno-rtti -O2 ) +# Add Clang-specific flags +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + list(APPEND KERNEL_CXXFLAGS + -target x86_64-unknown-none-elf + -fno-builtin + -fno-pie + ) + message(STATUS "Added Clang-specific flags for kernel") +endif() + # Create object library add_library(kernel_obj OBJECT ${KERNEL_SOURCES}) target_include_directories(kernel_obj PRIVATE include) -target_compile_options(kernel_obj PRIVATE ${KERNEL_CFLAGS}) +target_compile_options(kernel_obj PRIVATE ${KERNEL_CXXFLAGS}) # Create kernel binary add_executable(kernel_elf $ ${KERNEL_ASM_OBJECTS}) set_target_properties(kernel_elf PROPERTIES OUTPUT_NAME metalos.elf - LINKER_LANGUAGE C + LINKER_LANGUAGE CXX POSITION_INDEPENDENT_CODE OFF ) -target_link_options(kernel_elf PRIVATE - -nostdlib - -no-pie - -T ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld -) + +# Set linker options based on compiler +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_link_options(kernel_elf PRIVATE + -nostdlib + -Wl,--no-pie + -T ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld + -target x86_64-unknown-none-elf + ) +else() + target_link_options(kernel_elf PRIVATE + -nostdlib + -no-pie + -T ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld + ) +endif() # Custom command to create flat binary add_custom_command( diff --git a/kernel/include/kernel/apic.h b/kernel/include/kernel/apic.h index ed1c5c3..dab4832 100644 --- a/kernel/include/kernel/apic.h +++ b/kernel/include/kernel/apic.h @@ -19,11 +19,37 @@ #define APIC_IPI_INIT 0x500 #define APIC_IPI_STARTUP 0x600 -// APIC functions +#ifdef __cplusplus +// C++ APIC class +class APIC { +private: + volatile uint32_t* apicBase; + + uint32_t read(uint32_t offset) const; + void write(uint32_t offset, uint32_t value); + +public: + APIC(); + + bool isAvailable() const; + void init(); + uint8_t getId() const; + void sendEOI(); + void sendIPI(uint8_t destApicId, uint8_t vector, uint32_t deliveryMode); +}; + +extern "C" { +#endif + +// C-compatible APIC functions bool apic_is_available(void); void apic_init(void); uint8_t apic_get_id(void); void apic_send_eoi(void); void apic_send_ipi(uint8_t dest_apic_id, uint8_t vector, uint32_t delivery_mode); +#ifdef __cplusplus +} +#endif + #endif // METALOS_KERNEL_APIC_H diff --git a/kernel/include/kernel/gdt.h b/kernel/include/kernel/gdt.h index b170611..5d29d04 100644 --- a/kernel/include/kernel/gdt.h +++ b/kernel/include/kernel/gdt.h @@ -19,7 +19,29 @@ typedef struct { uint64_t base; } __attribute__((packed)) gdt_ptr_t; -// Initialize the Global Descriptor Table +#ifdef __cplusplus +// C++ GDT class +class GDT { +private: + gdt_entry_t entries[5]; + gdt_ptr_t gdtPtr; + + void setGate(int num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran); + +public: + GDT(); + + void init(); +}; + +extern "C" { +#endif + +// C-compatible GDT function void gdt_init(void); +#ifdef __cplusplus +} +#endif + #endif // METALOS_KERNEL_GDT_H diff --git a/kernel/include/kernel/interrupts.h b/kernel/include/kernel/interrupts.h index d29dcd6..f353b3e 100644 --- a/kernel/include/kernel/interrupts.h +++ b/kernel/include/kernel/interrupts.h @@ -28,10 +28,32 @@ typedef struct { uint64_t rip, cs, rflags, rsp, ss; } __attribute__((packed)) registers_t; -// Initialize Interrupt Descriptor Table -void idt_init(void); +#ifdef __cplusplus +// C++ InterruptManager class +class InterruptManager { +private: + idt_entry_t idt[256]; + idt_ptr_t idtPtr; + + void setGate(uint8_t num, uint64_t handler, uint16_t selector, uint8_t flags); + void remapPIC(); -// Generic interrupt handler (called from assembly) +public: + InterruptManager(); + + void init(); + void handleInterrupt(registers_t* regs); +}; + +extern "C" { +#endif + +// C-compatible functions +void idt_init(void); void interrupt_handler(registers_t* regs); +#ifdef __cplusplus +} +#endif + #endif // METALOS_KERNEL_INTERRUPTS_H diff --git a/kernel/include/kernel/kernel.h b/kernel/include/kernel/kernel.h index 0955dce..b598920 100644 --- a/kernel/include/kernel/kernel.h +++ b/kernel/include/kernel/kernel.h @@ -27,7 +27,15 @@ typedef struct { void* rsdp; } BootInfo; +#ifdef __cplusplus +extern "C" { +#endif + // Kernel entry point void kernel_main(BootInfo* boot_info); +#ifdef __cplusplus +} +#endif + #endif // METALOS_KERNEL_KERNEL_H diff --git a/kernel/include/kernel/memory.h b/kernel/include/kernel/memory.h index ff0e4a9..c34b7f4 100644 --- a/kernel/include/kernel/memory.h +++ b/kernel/include/kernel/memory.h @@ -8,14 +8,51 @@ // Memory constants #define PAGE_SIZE 4096 -// Physical memory manager +#ifdef __cplusplus +// C++ PhysicalMemoryManager class +class PhysicalMemoryManager { +private: + uint8_t pageBitmap[32768]; // Supports up to 128MB with 4KB pages + uint64_t totalPages; + uint64_t usedPages; + +public: + PhysicalMemoryManager(); + + void init(BootInfo* bootInfo); + void* allocPage(); + void freePage(void* page); + uint64_t getTotalMemory() const; + uint64_t getFreeMemory() const; +}; + +// C++ HeapAllocator class +class HeapAllocator { +private: + uint8_t* heapStart; + uint8_t* heapCurrent; + uint8_t* heapEnd; + +public: + HeapAllocator(); + + void init(void* start, size_t size); + void* alloc(size_t size); + void* calloc(size_t num, size_t size); + void free(void* ptr); +}; + +extern "C" { +#endif + +// C-compatible physical memory manager void pmm_init(BootInfo* boot_info); void* pmm_alloc_page(void); void pmm_free_page(void* page); uint64_t pmm_get_total_memory(void); uint64_t pmm_get_free_memory(void); -// Simple kernel heap allocator (bump allocator for now) +// C-compatible kernel heap allocator void heap_init(void* start, size_t size); void* kmalloc(size_t size); void* kcalloc(size_t num, size_t size); @@ -26,4 +63,8 @@ void* memset(void* dest, int val, size_t count); void* memcpy(void* dest, const void* src, size_t count); int memcmp(const void* s1, const void* s2, size_t count); +#ifdef __cplusplus +} +#endif + #endif // METALOS_KERNEL_MEMORY_H diff --git a/kernel/include/kernel/pci.h b/kernel/include/kernel/pci.h index b3b0449..5cc160d 100644 --- a/kernel/include/kernel/pci.h +++ b/kernel/include/kernel/pci.h @@ -7,6 +7,9 @@ #define PCI_CONFIG_ADDRESS 0xCF8 #define PCI_CONFIG_DATA 0xCFC +// Maximum devices we'll track +#define MAX_PCI_DEVICES 256 + // PCI Device Structure typedef struct { uint8_t bus; @@ -21,11 +24,51 @@ typedef struct { uint32_t bar[6]; // Base Address Registers } pci_device_t; -// PCI Functions +#ifdef __cplusplus +// C++ PCIDevice class-compatible structure +struct PCIDevice { + uint8_t bus; + uint8_t device; + uint8_t function; + uint16_t vendor_id; + uint16_t device_id; + uint8_t class_code; + uint8_t subclass; + uint8_t prog_if; + uint8_t revision_id; + uint32_t bar[6]; +}; + +// C++ PCIManager class +class PCIManager { +private: + PCIDevice devices[MAX_PCI_DEVICES]; + uint32_t deviceCount; + + void probeDevice(uint8_t bus, uint8_t device, uint8_t function); + +public: + PCIManager(); + + void init(); + uint32_t readConfig(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset); + void writeConfig(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value); + PCIDevice* findDevice(uint16_t vendor_id, uint16_t device_id); + void enableBusMastering(PCIDevice* dev); +}; + +extern "C" { +#endif + +// C-compatible PCI functions void pci_init(void); uint32_t pci_read_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset); void pci_write_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value); pci_device_t* pci_find_device(uint16_t vendor_id, uint16_t device_id); void pci_enable_bus_mastering(pci_device_t* dev); +#ifdef __cplusplus +} +#endif + #endif // METALOS_KERNEL_PCI_H diff --git a/kernel/include/kernel/smp.h b/kernel/include/kernel/smp.h index e1a8113..03d8b1b 100644 --- a/kernel/include/kernel/smp.h +++ b/kernel/include/kernel/smp.h @@ -15,22 +15,42 @@ typedef struct { uint64_t kernel_stack; } cpu_info_t; -// SMP initialization +#ifdef __cplusplus +// C++ SMPManager class +class SMPManager { +private: + cpu_info_t cpuInfo[MAX_CPUS]; + uint8_t cpuCount; + bool smpEnabled; + + void initCPU(uint8_t cpuId, uint8_t apicId); + void delay(uint32_t microseconds); + bool startAP(uint8_t apicId); + +public: + SMPManager(); + + void init(); + uint8_t getCPUCount() const; + uint8_t getCurrentCPU() const; + bool isEnabled() const; + cpu_info_t* getCPUInfo(uint8_t cpuId); + void markCPUOnline(uint8_t cpuId); +}; + +extern "C" { +#endif + +// C-compatible SMP functions void smp_init(void); - -// Get number of CPUs detected uint8_t smp_get_cpu_count(void); - -// Get current CPU ID uint8_t smp_get_current_cpu(void); - -// Check if SMP is enabled bool smp_is_enabled(void); - -// Get CPU info cpu_info_t* smp_get_cpu_info(uint8_t cpu_id); - -// Mark CPU as online (internal use by AP startup) void smp_cpu_online(uint8_t cpu_id); +#ifdef __cplusplus +} +#endif + #endif // METALOS_KERNEL_SMP_H diff --git a/kernel/include/kernel/spinlock.h b/kernel/include/kernel/spinlock.h index 7ec431d..dd9a3ca 100644 --- a/kernel/include/kernel/spinlock.h +++ b/kernel/include/kernel/spinlock.h @@ -4,24 +4,39 @@ #include #include -// Spinlock structure +#ifdef __cplusplus +// C++ Spinlock class +class Spinlock { +private: + volatile uint32_t lock; + +public: + Spinlock(); + + void init(); + void acquire(); + bool tryAcquire(); + void release(); + bool isLocked() const; +}; + +extern "C" { +#endif + +// C-compatible spinlock structure typedef struct { volatile uint32_t lock; } spinlock_t; -// Initialize spinlock +// C-compatible functions void spinlock_init(spinlock_t* lock); - -// Acquire spinlock void spinlock_acquire(spinlock_t* lock); - -// Try to acquire spinlock (non-blocking) bool spinlock_try_acquire(spinlock_t* lock); - -// Release spinlock void spinlock_release(spinlock_t* lock); - -// Check if locked bool spinlock_is_locked(spinlock_t* lock); +#ifdef __cplusplus +} +#endif + #endif // METALOS_KERNEL_SPINLOCK_H diff --git a/kernel/include/kernel/timer.h b/kernel/include/kernel/timer.h index 3d93428..2dc83c4 100644 --- a/kernel/include/kernel/timer.h +++ b/kernel/include/kernel/timer.h @@ -6,12 +6,32 @@ // Timer frequency (Hz) #define TIMER_FREQUENCY 1000 // 1ms per tick -// Timer functions +#ifdef __cplusplus +// C++ Timer class +class Timer { +private: + volatile uint64_t ticks; + +public: + Timer(); + + void init(uint32_t frequency); + uint64_t getTicks() const; + void wait(uint32_t waitTicks) const; + void handleInterrupt(); +}; + +extern "C" { +#endif + +// C-compatible timer functions void timer_init(uint32_t frequency); uint64_t timer_get_ticks(void); void timer_wait(uint32_t ticks); - -// Timer interrupt handler (called from ISR) void timer_handler(void); +#ifdef __cplusplus +} +#endif + #endif // METALOS_KERNEL_TIMER_H diff --git a/kernel/src/apic.c b/kernel/src/apic.cpp similarity index 50% rename from kernel/src/apic.c rename to kernel/src/apic.cpp index e96f4c1..10bac9e 100644 --- a/kernel/src/apic.c +++ b/kernel/src/apic.cpp @@ -9,10 +9,9 @@ // APIC base address (default, can be read from MSR) #define APIC_BASE_MSR 0x1B -static volatile uint32_t* apic_base = (volatile uint32_t*)0xFEE00000; // Read CPUID to check for APIC -static bool cpuid_has_apic(void) { +static bool cpuidHasAPIC(void) { uint32_t eax, ebx, ecx, edx; // CPUID function 1 @@ -26,52 +25,76 @@ static bool cpuid_has_apic(void) { return (edx & (1 << 9)) != 0; } -// Read APIC register -static uint32_t apic_read(uint32_t offset) { - return apic_base[offset / 4]; +// APIC class implementation +APIC::APIC() : apicBase((volatile uint32_t*)0xFEE00000) {} + +uint32_t APIC::read(uint32_t offset) const { + return apicBase[offset / 4]; } -// Write APIC register -static void apic_write(uint32_t offset, uint32_t value) { - apic_base[offset / 4] = value; +void APIC::write(uint32_t offset, uint32_t value) { + apicBase[offset / 4] = value; } -// Check if APIC is available -bool apic_is_available(void) { - return cpuid_has_apic(); +bool APIC::isAvailable() const { + return cpuidHasAPIC(); } -// Initialize Local APIC -void apic_init(void) { +void APIC::init() { // Enable APIC via spurious interrupt vector register // Set spurious vector to 0xFF and enable APIC (bit 8) - apic_write(APIC_REG_SPURIOUS, 0x1FF); + write(APIC_REG_SPURIOUS, 0x1FF); // Set Task Priority Register to 0 (accept all interrupts) - apic_write(APIC_REG_TPR, 0); + write(APIC_REG_TPR, 0); } -// Get APIC ID -uint8_t apic_get_id(void) { - uint32_t id_reg = apic_read(APIC_REG_ID); - return (id_reg >> 24) & 0xFF; +uint8_t APIC::getId() const { + uint32_t idReg = read(APIC_REG_ID); + return (idReg >> 24) & 0xFF; } -// Send End of Interrupt -void apic_send_eoi(void) { - apic_write(APIC_REG_EOI, 0); +void APIC::sendEOI() { + write(APIC_REG_EOI, 0); } -// Send Inter-Processor Interrupt (IPI) -void apic_send_ipi(uint8_t dest_apic_id, uint8_t vector, uint32_t delivery_mode) { +void APIC::sendIPI(uint8_t destApicId, uint8_t vector, uint32_t deliveryMode) { // Wait for previous IPI to complete - while (apic_read(APIC_REG_ICR_LOW) & (1 << 12)) { + while (read(APIC_REG_ICR_LOW) & (1 << 12)) { __asm__ volatile("pause"); } // Set destination in high register - apic_write(APIC_REG_ICR_HIGH, ((uint32_t)dest_apic_id) << 24); + write(APIC_REG_ICR_HIGH, ((uint32_t)destApicId) << 24); // Send IPI with delivery mode and vector in low register - apic_write(APIC_REG_ICR_LOW, delivery_mode | vector); + write(APIC_REG_ICR_LOW, deliveryMode | vector); } + +// Global APIC instance +static APIC globalAPIC; + +// C-compatible wrapper functions +extern "C" { + +bool apic_is_available(void) { + return globalAPIC.isAvailable(); +} + +void apic_init(void) { + globalAPIC.init(); +} + +uint8_t apic_get_id(void) { + return globalAPIC.getId(); +} + +void apic_send_eoi(void) { + globalAPIC.sendEOI(); +} + +void apic_send_ipi(uint8_t dest_apic_id, uint8_t vector, uint32_t delivery_mode) { + globalAPIC.sendIPI(dest_apic_id, vector, delivery_mode); +} + +} // extern "C" diff --git a/kernel/src/gdt.c b/kernel/src/gdt.c deleted file mode 100644 index e5729d9..0000000 --- a/kernel/src/gdt.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * MetalOS Kernel - Global Descriptor Table (GDT) - * - * Minimal GDT setup for x86_64 long mode - * Only what's needed for our single-app OS - */ - -#include "kernel/gdt.h" - -// GDT entries (minimal for x86_64) -// In long mode, most segmentation is ignored, but we still need a valid GDT -static gdt_entry_t gdt[5]; -static gdt_ptr_t gdt_ptr; - -// Set a GDT entry -static void gdt_set_gate(int num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran) { - gdt[num].base_low = (base & 0xFFFF); - gdt[num].base_middle = (base >> 16) & 0xFF; - gdt[num].base_high = (base >> 24) & 0xFF; - - gdt[num].limit_low = (limit & 0xFFFF); - gdt[num].granularity = (limit >> 16) & 0x0F; - gdt[num].granularity |= gran & 0xF0; - gdt[num].access = access; -} - -// Load GDT (assembly) -extern void gdt_flush(uint64_t); - -// Initialize GDT -void gdt_init(void) { - gdt_ptr.limit = (sizeof(gdt_entry_t) * 5) - 1; - gdt_ptr.base = (uint64_t)&gdt; - - // Null descriptor - gdt_set_gate(0, 0, 0, 0, 0); - - // Kernel code segment (64-bit) - // Access: Present, Ring 0, Code, Executable, Readable - gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xA0); - - // Kernel data segment (64-bit) - // Access: Present, Ring 0, Data, Writable - gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xC0); - - // User code segment (64-bit) - // Access: Present, Ring 3, Code, Executable, Readable - gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xA0); - - // User data segment (64-bit) - // Access: Present, Ring 3, Data, Writable - gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xC0); - - // Flush GDT - gdt_flush((uint64_t)&gdt_ptr); -} diff --git a/kernel/src/gdt.cpp b/kernel/src/gdt.cpp new file mode 100644 index 0000000..6100ca9 --- /dev/null +++ b/kernel/src/gdt.cpp @@ -0,0 +1,64 @@ +/* + * MetalOS Kernel - Global Descriptor Table (GDT) + * + * Minimal GDT setup for x86_64 long mode + * Only what's needed for our single-app OS + */ + +#include "kernel/gdt.h" + +// Load GDT (assembly) +extern "C" void gdt_flush(uint64_t); + +// GDT class implementation +GDT::GDT() { + gdtPtr.limit = (sizeof(gdt_entry_t) * 5) - 1; + gdtPtr.base = (uint64_t)&entries; +} + +void GDT::setGate(int num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran) { + entries[num].base_low = (base & 0xFFFF); + entries[num].base_middle = (base >> 16) & 0xFF; + entries[num].base_high = (base >> 24) & 0xFF; + + entries[num].limit_low = (limit & 0xFFFF); + entries[num].granularity = (limit >> 16) & 0x0F; + entries[num].granularity |= gran & 0xF0; + entries[num].access = access; +} + +void GDT::init() { + // Null descriptor + setGate(0, 0, 0, 0, 0); + + // Kernel code segment (64-bit) + // Access: Present, Ring 0, Code, Executable, Readable + setGate(1, 0, 0xFFFFFFFF, 0x9A, 0xA0); + + // Kernel data segment (64-bit) + // Access: Present, Ring 0, Data, Writable + setGate(2, 0, 0xFFFFFFFF, 0x92, 0xC0); + + // User code segment (64-bit) + // Access: Present, Ring 3, Code, Executable, Readable + setGate(3, 0, 0xFFFFFFFF, 0xFA, 0xA0); + + // User data segment (64-bit) + // Access: Present, Ring 3, Data, Writable + setGate(4, 0, 0xFFFFFFFF, 0xF2, 0xC0); + + // Flush GDT + gdt_flush((uint64_t)&gdtPtr); +} + +// Global GDT instance +static GDT globalGDT; + +// C-compatible wrapper function +extern "C" { + +void gdt_init(void) { + globalGDT.init(); +} + +} // extern "C" diff --git a/kernel/src/interrupts.c b/kernel/src/interrupts.c deleted file mode 100644 index e4d6151..0000000 --- a/kernel/src/interrupts.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * MetalOS Kernel - Interrupt Handling - * - * Minimal IDT and interrupt handlers - * Supports both PIC (legacy) and APIC (multicore) modes - */ - -#include "kernel/interrupts.h" -#include "kernel/timer.h" -#include "kernel/smp.h" -#include "kernel/apic.h" - -// I/O port access functions -static inline void outb(uint16_t port, uint8_t value) { - __asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port)); -} - -// PIC ports -#define PIC1_COMMAND 0x20 -#define PIC1_DATA 0x21 -#define PIC2_COMMAND 0xA0 -#define PIC2_DATA 0xA1 - -// IDT entries (256 interrupts in x86_64) -static idt_entry_t idt[256]; -static idt_ptr_t idt_ptr; - -// External ISR handlers (defined in interrupts_asm.asm) -extern void isr0(void); -extern void isr1(void); -extern void isr2(void); -extern void isr3(void); -extern void isr4(void); -extern void isr5(void); -extern void isr6(void); -extern void isr7(void); -extern void isr8(void); -extern void isr9(void); -extern void isr10(void); -extern void isr11(void); -extern void isr12(void); -extern void isr13(void); -extern void isr14(void); -extern void isr15(void); -extern void isr16(void); -extern void isr17(void); -extern void isr18(void); -extern void isr19(void); -extern void isr20(void); -extern void isr21(void); -extern void isr22(void); -extern void isr23(void); -extern void isr24(void); -extern void isr25(void); -extern void isr26(void); -extern void isr27(void); -extern void isr28(void); -extern void isr29(void); -extern void isr30(void); -extern void isr31(void); - -// IRQ handlers -extern void irq0(void); -extern void irq1(void); - -// Set an IDT entry -static void idt_set_gate(uint8_t num, uint64_t handler, uint16_t selector, uint8_t flags) { - idt[num].offset_low = handler & 0xFFFF; - idt[num].offset_mid = (handler >> 16) & 0xFFFF; - idt[num].offset_high = (handler >> 32) & 0xFFFFFFFF; - idt[num].selector = selector; - idt[num].ist = 0; - idt[num].type_attr = flags; - idt[num].zero = 0; -} - -// Remap PIC (Programmable Interrupt Controller) -static void pic_remap(void) { - // ICW1: Initialize PIC - outb(PIC1_COMMAND, 0x11); // Master PIC - outb(PIC2_COMMAND, 0x11); // Slave PIC - - // ICW2: Set interrupt vector offsets - outb(PIC1_DATA, 0x20); // Master offset to 0x20 - outb(PIC2_DATA, 0x28); // Slave offset to 0x28 - - // ICW3: Set up cascade - outb(PIC1_DATA, 0x04); // Tell master about slave - outb(PIC2_DATA, 0x02); // Tell slave its cascade - - // ICW4: Set mode - outb(PIC1_DATA, 0x01); - outb(PIC2_DATA, 0x01); - - // Mask all interrupts initially - outb(PIC1_DATA, 0xFF); - outb(PIC2_DATA, 0xFF); -} - -// Initialize IDT -void idt_init(void) { - idt_ptr.limit = (sizeof(idt_entry_t) * 256) - 1; - idt_ptr.base = (uint64_t)&idt; - - // Clear IDT - for (int i = 0; i < 256; i++) { - idt_set_gate(i, 0, 0, 0); - } - - // Install exception handlers (ISRs 0-31) - idt_set_gate(0, (uint64_t)isr0, 0x08, 0x8E); - idt_set_gate(1, (uint64_t)isr1, 0x08, 0x8E); - idt_set_gate(2, (uint64_t)isr2, 0x08, 0x8E); - idt_set_gate(3, (uint64_t)isr3, 0x08, 0x8E); - idt_set_gate(4, (uint64_t)isr4, 0x08, 0x8E); - idt_set_gate(5, (uint64_t)isr5, 0x08, 0x8E); - idt_set_gate(6, (uint64_t)isr6, 0x08, 0x8E); - idt_set_gate(7, (uint64_t)isr7, 0x08, 0x8E); - idt_set_gate(8, (uint64_t)isr8, 0x08, 0x8E); - idt_set_gate(9, (uint64_t)isr9, 0x08, 0x8E); - idt_set_gate(10, (uint64_t)isr10, 0x08, 0x8E); - idt_set_gate(11, (uint64_t)isr11, 0x08, 0x8E); - idt_set_gate(12, (uint64_t)isr12, 0x08, 0x8E); - idt_set_gate(13, (uint64_t)isr13, 0x08, 0x8E); - idt_set_gate(14, (uint64_t)isr14, 0x08, 0x8E); - idt_set_gate(15, (uint64_t)isr15, 0x08, 0x8E); - idt_set_gate(16, (uint64_t)isr16, 0x08, 0x8E); - idt_set_gate(17, (uint64_t)isr17, 0x08, 0x8E); - idt_set_gate(18, (uint64_t)isr18, 0x08, 0x8E); - idt_set_gate(19, (uint64_t)isr19, 0x08, 0x8E); - idt_set_gate(20, (uint64_t)isr20, 0x08, 0x8E); - idt_set_gate(21, (uint64_t)isr21, 0x08, 0x8E); - idt_set_gate(22, (uint64_t)isr22, 0x08, 0x8E); - idt_set_gate(23, (uint64_t)isr23, 0x08, 0x8E); - idt_set_gate(24, (uint64_t)isr24, 0x08, 0x8E); - idt_set_gate(25, (uint64_t)isr25, 0x08, 0x8E); - idt_set_gate(26, (uint64_t)isr26, 0x08, 0x8E); - idt_set_gate(27, (uint64_t)isr27, 0x08, 0x8E); - idt_set_gate(28, (uint64_t)isr28, 0x08, 0x8E); - idt_set_gate(29, (uint64_t)isr29, 0x08, 0x8E); - idt_set_gate(30, (uint64_t)isr30, 0x08, 0x8E); - idt_set_gate(31, (uint64_t)isr31, 0x08, 0x8E); - - // Remap PIC - pic_remap(); - - // Install IRQ handlers (IRQs 0-15 mapped to 32-47) - idt_set_gate(32, (uint64_t)irq0, 0x08, 0x8E); // Timer - idt_set_gate(33, (uint64_t)irq1, 0x08, 0x8E); // Keyboard - - // Load IDT - __asm__ volatile("lidt %0" : : "m"(idt_ptr)); - - // Enable interrupts - __asm__ volatile("sti"); -} - -// Generic interrupt handler -void interrupt_handler(registers_t* regs) { - // Handle specific interrupts - if (regs->int_no == 32) { - // Timer interrupt (IRQ0) - timer_handler(); - } - - // TODO: Handle other interrupts (keyboard, etc.) - - // Send EOI (End of Interrupt) - if (regs->int_no >= 32 && regs->int_no < 48) { - // Check if we're using APIC or PIC - if (smp_is_enabled() && apic_is_available()) { - // Use APIC EOI - apic_send_eoi(); - } else { - // Use legacy PIC EOI - if (regs->int_no >= 40) { - // Slave PIC - outb(PIC2_COMMAND, 0x20); - } - // Master PIC - outb(PIC1_COMMAND, 0x20); - } - } -} diff --git a/kernel/src/interrupts.cpp b/kernel/src/interrupts.cpp new file mode 100644 index 0000000..b2fc199 --- /dev/null +++ b/kernel/src/interrupts.cpp @@ -0,0 +1,166 @@ +/* + * MetalOS Kernel - Interrupt Handling + * + * Minimal IDT and interrupt handlers + * Supports both PIC (legacy) and APIC (multicore) modes + */ + +#include "kernel/interrupts.h" +#include "kernel/timer.h" +#include "kernel/smp.h" +#include "kernel/apic.h" + +// I/O port access functions +static inline void outb(uint16_t port, uint8_t value) { + __asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port)); +} + +// PIC ports +#define PIC1_COMMAND 0x20 +#define PIC1_DATA 0x21 +#define PIC2_COMMAND 0xA0 +#define PIC2_DATA 0xA1 + +// External ISR handlers (defined in interrupts_asm.asm) +extern "C" { + void isr0(void); void isr1(void); void isr2(void); void isr3(void); + void isr4(void); void isr5(void); void isr6(void); void isr7(void); + void isr8(void); void isr9(void); void isr10(void); void isr11(void); + void isr12(void); void isr13(void); void isr14(void); void isr15(void); + void isr16(void); void isr17(void); void isr18(void); void isr19(void); + void isr20(void); void isr21(void); void isr22(void); void isr23(void); + void isr24(void); void isr25(void); void isr26(void); void isr27(void); + void isr28(void); void isr29(void); void isr30(void); void isr31(void); + void irq0(void); void irq1(void); +} + +// InterruptManager class implementation +InterruptManager::InterruptManager() { + idtPtr.limit = (sizeof(idt_entry_t) * 256) - 1; + idtPtr.base = (uint64_t)&idt; +} + +void InterruptManager::setGate(uint8_t num, uint64_t handler, uint16_t selector, uint8_t flags) { + idt[num].offset_low = handler & 0xFFFF; + idt[num].offset_mid = (handler >> 16) & 0xFFFF; + idt[num].offset_high = (handler >> 32) & 0xFFFFFFFF; + idt[num].selector = selector; + idt[num].ist = 0; + idt[num].type_attr = flags; + idt[num].zero = 0; +} + +void InterruptManager::remapPIC() { + // ICW1: Initialize PIC + outb(PIC1_COMMAND, 0x11); + outb(PIC2_COMMAND, 0x11); + + // ICW2: Set interrupt vector offsets + outb(PIC1_DATA, 0x20); + outb(PIC2_DATA, 0x28); + + // ICW3: Set up cascade + outb(PIC1_DATA, 0x04); + outb(PIC2_DATA, 0x02); + + // ICW4: Set mode + outb(PIC1_DATA, 0x01); + outb(PIC2_DATA, 0x01); + + // Mask all interrupts initially + outb(PIC1_DATA, 0xFF); + outb(PIC2_DATA, 0xFF); +} + +void InterruptManager::init() { + // Clear IDT + for (int i = 0; i < 256; i++) { + setGate(i, 0, 0, 0); + } + + // Install exception handlers (ISRs 0-31) + setGate(0, (uint64_t)isr0, 0x08, 0x8E); + setGate(1, (uint64_t)isr1, 0x08, 0x8E); + setGate(2, (uint64_t)isr2, 0x08, 0x8E); + setGate(3, (uint64_t)isr3, 0x08, 0x8E); + setGate(4, (uint64_t)isr4, 0x08, 0x8E); + setGate(5, (uint64_t)isr5, 0x08, 0x8E); + setGate(6, (uint64_t)isr6, 0x08, 0x8E); + setGate(7, (uint64_t)isr7, 0x08, 0x8E); + setGate(8, (uint64_t)isr8, 0x08, 0x8E); + setGate(9, (uint64_t)isr9, 0x08, 0x8E); + setGate(10, (uint64_t)isr10, 0x08, 0x8E); + setGate(11, (uint64_t)isr11, 0x08, 0x8E); + setGate(12, (uint64_t)isr12, 0x08, 0x8E); + setGate(13, (uint64_t)isr13, 0x08, 0x8E); + setGate(14, (uint64_t)isr14, 0x08, 0x8E); + setGate(15, (uint64_t)isr15, 0x08, 0x8E); + setGate(16, (uint64_t)isr16, 0x08, 0x8E); + setGate(17, (uint64_t)isr17, 0x08, 0x8E); + setGate(18, (uint64_t)isr18, 0x08, 0x8E); + setGate(19, (uint64_t)isr19, 0x08, 0x8E); + setGate(20, (uint64_t)isr20, 0x08, 0x8E); + setGate(21, (uint64_t)isr21, 0x08, 0x8E); + setGate(22, (uint64_t)isr22, 0x08, 0x8E); + setGate(23, (uint64_t)isr23, 0x08, 0x8E); + setGate(24, (uint64_t)isr24, 0x08, 0x8E); + setGate(25, (uint64_t)isr25, 0x08, 0x8E); + setGate(26, (uint64_t)isr26, 0x08, 0x8E); + setGate(27, (uint64_t)isr27, 0x08, 0x8E); + setGate(28, (uint64_t)isr28, 0x08, 0x8E); + setGate(29, (uint64_t)isr29, 0x08, 0x8E); + setGate(30, (uint64_t)isr30, 0x08, 0x8E); + setGate(31, (uint64_t)isr31, 0x08, 0x8E); + + // Remap PIC + remapPIC(); + + // Install IRQ handlers (IRQs 0-15 mapped to 32-47) + setGate(32, (uint64_t)irq0, 0x08, 0x8E); + setGate(33, (uint64_t)irq1, 0x08, 0x8E); + + // Load IDT + __asm__ volatile("lidt %0" : : "m"(idtPtr)); + + // Enable interrupts + __asm__ volatile("sti"); +} + +void InterruptManager::handleInterrupt(registers_t* regs) { + // Handle specific interrupts + if (regs->int_no == 32) { + // Timer interrupt (IRQ0) + timer_handler(); + } + + // Send EOI (End of Interrupt) + if (regs->int_no >= 32 && regs->int_no < 48) { + // Check if we're using APIC or PIC + if (smp_is_enabled() && apic_is_available()) { + // Use APIC EOI + apic_send_eoi(); + } else { + // Use legacy PIC EOI + if (regs->int_no >= 40) { + outb(PIC2_COMMAND, 0x20); + } + outb(PIC1_COMMAND, 0x20); + } + } +} + +// Global interrupt manager instance +static InterruptManager globalInterruptManager; + +// C-compatible wrapper functions +extern "C" { + +void idt_init(void) { + globalInterruptManager.init(); +} + +void interrupt_handler(registers_t* regs) { + globalInterruptManager.handleInterrupt(regs); +} + +} // extern "C" diff --git a/kernel/src/main.c b/kernel/src/main.cpp similarity index 69% rename from kernel/src/main.c rename to kernel/src/main.cpp index 114ad30..cf7e071 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.cpp @@ -4,6 +4,8 @@ * EXTREME MINIMAL kernel - only what's needed for QT6 Hello World. * Now with basic multicore support for better performance! * Just: boot -> init hardware (all cores) -> run app. + * + * C++ implementation: Root level function hands off to classes */ #include "kernel/kernel.h" @@ -18,21 +20,21 @@ * Kernel main entry point * Called by bootloader with boot information * - * Initializes all hardware including multicore support. - * Simple design: all cores initialized but only BSP runs app. - * Future: could distribute work across cores for better performance. + * This is the root-level function that hands off to C++ classes + * for hardware initialization and system management. */ -void kernel_main(BootInfo* boot_info) { - // Initialize GDT (Global Descriptor Table) +extern "C" void kernel_main(BootInfo* boot_info) { + // Initialize GDT (Global Descriptor Table) - using GDT class gdt_init(); - // Initialize IDT (Interrupt Descriptor Table) + // Initialize IDT (Interrupt Descriptor Table) - using InterruptManager class idt_init(); - // Initialize physical memory manager + // Initialize physical memory manager - using PhysicalMemoryManager class pmm_init(boot_info); // Initialize kernel heap (allocate 256 pages = 1MB for kernel heap) + // Using HeapAllocator class void* heap_start_page = pmm_alloc_page(); if (heap_start_page) { // Allocate additional pages for heap (256 pages total) @@ -42,13 +44,13 @@ void kernel_main(BootInfo* boot_info) { heap_init(heap_start_page, 256 * PAGE_SIZE); // 1MB heap } - // Initialize timer (1000 Hz = 1ms per tick) + // Initialize timer (1000 Hz = 1ms per tick) - using Timer class timer_init(TIMER_FREQUENCY); - // Initialize PCI bus + // Initialize PCI bus - using PCIManager class pci_init(); - // Initialize SMP (Symmetric Multi-Processing) + // Initialize SMP (Symmetric Multi-Processing) - using SMPManager class // This will detect and start all available CPU cores smp_init(); @@ -87,4 +89,10 @@ void kernel_main(BootInfo* boot_info) { * All cores available for future parallel processing. * * Total kernel size target: < 150 KB (with multicore support) + * + * C++ Design: + * - Root-level kernel_main() function serves as entry point + * - All subsystems implemented as classes (GDT, InterruptManager, Timer, etc.) + * - Classes encapsulate state and behavior + * - C-compatible wrappers maintain compatibility with assembly and bootloader */ diff --git a/kernel/src/memory.c b/kernel/src/memory.cpp similarity index 50% rename from kernel/src/memory.c rename to kernel/src/memory.cpp index 5c9dd77..e500827 100644 --- a/kernel/src/memory.c +++ b/kernel/src/memory.cpp @@ -7,45 +7,41 @@ #include "kernel/memory.h" -// Physical memory bitmap -#define PAGE_SIZE 4096 +// Physical memory bitmap constants #define BITMAP_SIZE 32768 // Supports up to 128MB with 4KB pages -static uint8_t page_bitmap[BITMAP_SIZE]; -static uint64_t total_pages = 0; -static uint64_t used_pages = 0; +// PhysicalMemoryManager class implementation +PhysicalMemoryManager::PhysicalMemoryManager() + : totalPages(0), usedPages(0) { + for (uint64_t i = 0; i < BITMAP_SIZE; i++) { + pageBitmap[i] = 0; + } +} -// Heap for kernel allocations -static uint8_t* heap_start = NULL; -static uint8_t* heap_current = NULL; -static uint8_t* heap_end = NULL; - -// Initialize physical memory manager -void pmm_init(BootInfo* boot_info) { - (void)boot_info; // TODO: Parse UEFI memory map +void PhysicalMemoryManager::init(BootInfo* bootInfo) { + (void)bootInfo; // TODO: Parse UEFI memory map // For now, assume 128MB of usable memory starting at 16MB - total_pages = (128 * 1024 * 1024) / PAGE_SIZE; + totalPages = (128 * 1024 * 1024) / PAGE_SIZE; // Clear bitmap for (uint64_t i = 0; i < BITMAP_SIZE; i++) { - page_bitmap[i] = 0; + pageBitmap[i] = 0; } - used_pages = 0; + usedPages = 0; } -// Allocate a physical page -void* pmm_alloc_page(void) { +void* PhysicalMemoryManager::allocPage() { // Find first free page in bitmap - for (uint64_t i = 0; i < total_pages; i++) { + for (uint64_t i = 0; i < totalPages; i++) { uint64_t byte = i / 8; uint64_t bit = i % 8; - if (!(page_bitmap[byte] & (1 << bit))) { + if (!(pageBitmap[byte] & (1 << bit))) { // Mark as used - page_bitmap[byte] |= (1 << bit); - used_pages++; + pageBitmap[byte] |= (1 << bit); + usedPages++; // Return physical address // Assuming memory starts at 16MB @@ -54,68 +50,66 @@ void* pmm_alloc_page(void) { } // Out of memory - return NULL; + return nullptr; } -// Free a physical page -void pmm_free_page(void* page) { +void PhysicalMemoryManager::freePage(void* page) { uint64_t addr = (uint64_t)page; // Calculate page index - uint64_t page_idx = (addr - 0x01000000UL) / PAGE_SIZE; + uint64_t pageIdx = (addr - 0x01000000UL) / PAGE_SIZE; - if (page_idx >= total_pages) { + if (pageIdx >= totalPages) { return; // Invalid address } - uint64_t byte = page_idx / 8; - uint64_t bit = page_idx % 8; + uint64_t byte = pageIdx / 8; + uint64_t bit = pageIdx % 8; // Mark as free - page_bitmap[byte] &= ~(1 << bit); - used_pages--; + pageBitmap[byte] &= ~(1 << bit); + usedPages--; } -// Get total memory -uint64_t pmm_get_total_memory(void) { - return total_pages * PAGE_SIZE; +uint64_t PhysicalMemoryManager::getTotalMemory() const { + return totalPages * PAGE_SIZE; } -// Get free memory -uint64_t pmm_get_free_memory(void) { - return (total_pages - used_pages) * PAGE_SIZE; +uint64_t PhysicalMemoryManager::getFreeMemory() const { + return (totalPages - usedPages) * PAGE_SIZE; } -// Initialize heap allocator -void heap_init(void* start, size_t size) { - heap_start = (uint8_t*)start; - heap_current = heap_start; - heap_end = heap_start + size; +// HeapAllocator class implementation +HeapAllocator::HeapAllocator() + : heapStart(nullptr), heapCurrent(nullptr), heapEnd(nullptr) {} + +void HeapAllocator::init(void* start, size_t size) { + heapStart = (uint8_t*)start; + heapCurrent = heapStart; + heapEnd = heapStart + size; } -// Simple bump allocator (no free support in this version) -void* kmalloc(size_t size) { - if (!heap_start) { - return NULL; +void* HeapAllocator::alloc(size_t size) { + if (!heapStart) { + return nullptr; } // Align to 16 bytes size = (size + 15) & ~15; - if (heap_current + size > heap_end) { - return NULL; // Out of heap memory + if (heapCurrent + size > heapEnd) { + return nullptr; // Out of heap memory } - void* ptr = heap_current; - heap_current += size; + void* ptr = heapCurrent; + heapCurrent += size; return ptr; } -// Allocate and zero memory -void* kcalloc(size_t num, size_t size) { +void* HeapAllocator::calloc(size_t num, size_t size) { size_t total = num * size; - void* ptr = kmalloc(total); + void* ptr = alloc(total); if (ptr) { memset(ptr, 0, total); @@ -124,15 +118,13 @@ void* kcalloc(size_t num, size_t size) { return ptr; } -// Free memory (not implemented in bump allocator) -void kfree(void* ptr) { +void HeapAllocator::free(void* ptr) { (void)ptr; // TODO: Implement proper free with a real allocator // For now, bump allocator doesn't support freeing } // Memory utility functions - void* memset(void* dest, int val, size_t count) { uint8_t* d = (uint8_t*)dest; uint8_t v = (uint8_t)val; @@ -167,3 +159,48 @@ int memcmp(const void* s1, const void* s2, size_t count) { return 0; } + +// Global instances +static PhysicalMemoryManager globalPMM; +static HeapAllocator globalHeap; + +// C-compatible wrapper functions +extern "C" { + +void pmm_init(BootInfo* boot_info) { + globalPMM.init(boot_info); +} + +void* pmm_alloc_page(void) { + return globalPMM.allocPage(); +} + +void pmm_free_page(void* page) { + globalPMM.freePage(page); +} + +uint64_t pmm_get_total_memory(void) { + return globalPMM.getTotalMemory(); +} + +uint64_t pmm_get_free_memory(void) { + return globalPMM.getFreeMemory(); +} + +void heap_init(void* start, size_t size) { + globalHeap.init(start, size); +} + +void* kmalloc(size_t size) { + return globalHeap.alloc(size); +} + +void* kcalloc(size_t num, size_t size) { + return globalHeap.calloc(num, size); +} + +void kfree(void* ptr) { + globalHeap.free(ptr); +} + +} // extern "C" diff --git a/kernel/src/pci.c b/kernel/src/pci.cpp similarity index 52% rename from kernel/src/pci.c rename to kernel/src/pci.cpp index 41f6638..3f96bcc 100644 --- a/kernel/src/pci.c +++ b/kernel/src/pci.cpp @@ -19,13 +19,10 @@ static inline uint32_t inl(uint16_t port) { return value; } -// Maximum devices we'll track -#define MAX_PCI_DEVICES 256 -static pci_device_t pci_devices[MAX_PCI_DEVICES]; -static uint32_t pci_device_count = 0; +// PCIManager class implementation +PCIManager::PCIManager() : deviceCount(0) {} -// Read from PCI configuration space -uint32_t pci_read_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset) { +uint32_t PCIManager::readConfig(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset) { uint32_t address = (uint32_t)( ((uint32_t)bus << 16) | ((uint32_t)device << 11) | @@ -38,8 +35,7 @@ uint32_t pci_read_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t return inl(PCI_CONFIG_DATA); } -// Write to PCI configuration space -void pci_write_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value) { +void PCIManager::writeConfig(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value) { uint32_t address = (uint32_t)( ((uint32_t)bus << 16) | ((uint32_t)device << 11) | @@ -52,11 +48,10 @@ void pci_write_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t off outl(PCI_CONFIG_DATA, value); } -// Probe a PCI device -static void pci_probe_device(uint8_t bus, uint8_t device, uint8_t function) { - uint32_t vendor_device = pci_read_config(bus, device, function, 0x00); - uint16_t vendor_id = vendor_device & 0xFFFF; - uint16_t device_id = (vendor_device >> 16) & 0xFFFF; +void PCIManager::probeDevice(uint8_t bus, uint8_t device, uint8_t function) { + uint32_t vendorDevice = readConfig(bus, device, function, 0x00); + uint16_t vendor_id = vendorDevice & 0xFFFF; + uint16_t device_id = (vendorDevice >> 16) & 0xFFFF; // Check if device exists if (vendor_id == 0xFFFF) { @@ -64,15 +59,15 @@ static void pci_probe_device(uint8_t bus, uint8_t device, uint8_t function) { } // Read class code - uint32_t class_rev = pci_read_config(bus, device, function, 0x08); - uint8_t class_code = (class_rev >> 24) & 0xFF; - uint8_t subclass = (class_rev >> 16) & 0xFF; - uint8_t prog_if = (class_rev >> 8) & 0xFF; - uint8_t revision_id = class_rev & 0xFF; + uint32_t classRev = readConfig(bus, device, function, 0x08); + uint8_t class_code = (classRev >> 24) & 0xFF; + uint8_t subclass = (classRev >> 16) & 0xFF; + uint8_t prog_if = (classRev >> 8) & 0xFF; + uint8_t revision_id = classRev & 0xFF; // Store device info - if (pci_device_count < MAX_PCI_DEVICES) { - pci_device_t* dev = &pci_devices[pci_device_count++]; + if (deviceCount < MAX_PCI_DEVICES) { + PCIDevice* dev = &devices[deviceCount++]; dev->bus = bus; dev->device = device; dev->function = function; @@ -85,68 +80,86 @@ static void pci_probe_device(uint8_t bus, uint8_t device, uint8_t function) { // Read BARs (Base Address Registers) for (int i = 0; i < 6; i++) { - dev->bar[i] = pci_read_config(bus, device, function, 0x10 + (i * 4)); + dev->bar[i] = readConfig(bus, device, function, 0x10 + (i * 4)); } } } -// Initialize PCI subsystem -void pci_init(void) { +void PCIManager::init() { + deviceCount = 0; + // Scan all buses, devices, and functions - // Note: On real hardware, could optimize by stopping after consecutive empty buses for (uint16_t bus = 0; bus < 256; bus++) { - uint8_t devices_found = 0; - for (uint8_t device = 0; device < 32; device++) { // Check if device exists (function 0) - uint32_t vendor_device = pci_read_config(bus, device, 0, 0x00); - if ((vendor_device & 0xFFFF) == 0xFFFF) { + uint32_t vendorDevice = readConfig(bus, device, 0, 0x00); + if ((vendorDevice & 0xFFFF) == 0xFFFF) { continue; // Device doesn't exist } - devices_found++; - pci_probe_device(bus, device, 0); + probeDevice(bus, device, 0); // Check if multi-function device - uint32_t header_type = pci_read_config(bus, device, 0, 0x0C); - if (header_type & 0x00800000) { + uint32_t headerType = readConfig(bus, device, 0, 0x0C); + if (headerType & 0x00800000) { // Multi-function device, scan other functions for (uint8_t function = 1; function < 8; function++) { - vendor_device = pci_read_config(bus, device, function, 0x00); - if ((vendor_device & 0xFFFF) != 0xFFFF) { - pci_probe_device(bus, device, function); + vendorDevice = readConfig(bus, device, function, 0x00); + if ((vendorDevice & 0xFFFF) != 0xFFFF) { + probeDevice(bus, device, function); } } } } - - // Early termination: if no devices found in this bus and we're past bus 0, - // we can potentially stop (though some systems have gaps) - // For now, continue full scan for maximum compatibility } } -// Find a PCI device by vendor and device ID -pci_device_t* pci_find_device(uint16_t vendor_id, uint16_t device_id) { - for (uint32_t i = 0; i < pci_device_count; i++) { - if (pci_devices[i].vendor_id == vendor_id && - pci_devices[i].device_id == device_id) { - return &pci_devices[i]; +PCIDevice* PCIManager::findDevice(uint16_t vendor_id, uint16_t device_id) { + for (uint32_t i = 0; i < deviceCount; i++) { + if (devices[i].vendor_id == vendor_id && devices[i].device_id == device_id) { + return &devices[i]; } } - return NULL; + return nullptr; } -// Enable bus mastering for a device -void pci_enable_bus_mastering(pci_device_t* dev) { +void PCIManager::enableBusMastering(PCIDevice* dev) { if (!dev) return; // Read command register (offset 0x04) - uint32_t command = pci_read_config(dev->bus, dev->device, dev->function, 0x04); + uint32_t command = readConfig(dev->bus, dev->device, dev->function, 0x04); // Set bus master bit (bit 2) command |= 0x04; // Write back - pci_write_config(dev->bus, dev->device, dev->function, 0x04, command); + writeConfig(dev->bus, dev->device, dev->function, 0x04, command); } + +// Global PCI manager instance +static PCIManager globalPCI; + +// C-compatible wrapper functions +extern "C" { + +void pci_init(void) { + globalPCI.init(); +} + +uint32_t pci_read_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset) { + return globalPCI.readConfig(bus, device, function, offset); +} + +void pci_write_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value) { + globalPCI.writeConfig(bus, device, function, offset, value); +} + +pci_device_t* pci_find_device(uint16_t vendor_id, uint16_t device_id) { + return reinterpret_cast(globalPCI.findDevice(vendor_id, device_id)); +} + +void pci_enable_bus_mastering(pci_device_t* dev) { + globalPCI.enableBusMastering(reinterpret_cast(dev)); +} + +} // extern "C" diff --git a/kernel/src/smp.c b/kernel/src/smp.c deleted file mode 100644 index d0b9eb7..0000000 --- a/kernel/src/smp.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * MetalOS Kernel - SMP (Symmetric Multi-Processing) Support - * - * Basic multicore support for better performance - * Initializes Application Processors (APs) using SIPI protocol - */ - -#include "kernel/smp.h" -#include "kernel/apic.h" -#include "kernel/memory.h" - -// CPU information array -static cpu_info_t cpu_info[MAX_CPUS]; -static uint8_t cpu_count = 1; // Start with BSP -static bool smp_enabled = false; - -// Bootstrap CPU is always CPU 0 -#define BSP_CPU_ID 0 - -// Trampoline code location (must be in low memory for real mode) -#define AP_TRAMPOLINE_ADDR 0x8000 - -// AP startup code (will be copied to low memory) -extern void ap_trampoline_start(void); -extern void ap_trampoline_end(void); - -// Get current CPU ID from APIC -uint8_t smp_get_current_cpu(void) { - if (!smp_enabled) { - return BSP_CPU_ID; - } - - uint8_t apic_id = apic_get_id(); - - // Find CPU by APIC ID - for (uint8_t i = 0; i < cpu_count; i++) { - if (cpu_info[i].apic_id == apic_id) { - return cpu_info[i].cpu_id; - } - } - - return BSP_CPU_ID; -} - -// Initialize a CPU entry -static void smp_init_cpu(uint8_t cpu_id, uint8_t apic_id) { - if (cpu_id >= MAX_CPUS) return; - - cpu_info[cpu_id].cpu_id = cpu_id; - cpu_info[cpu_id].apic_id = apic_id; - cpu_info[cpu_id].online = false; - cpu_info[cpu_id].kernel_stack = 0; -} - -// Mark CPU as online -void smp_cpu_online(uint8_t cpu_id) { - if (cpu_id < MAX_CPUS) { - cpu_info[cpu_id].online = true; - } -} - -// Simple delay for AP startup -static void smp_delay(uint32_t microseconds) { - // Approximate delay (not precise) - for (volatile uint32_t i = 0; i < microseconds * 100; i++) { - __asm__ volatile("pause"); - } -} - -// Start an Application Processor -static bool smp_start_ap(uint8_t apic_id) { - // Send INIT IPI - apic_send_ipi(apic_id, 0, APIC_IPI_INIT); - smp_delay(10000); // 10ms - - // Send SIPI (Startup IPI) with trampoline address - uint8_t vector = AP_TRAMPOLINE_ADDR >> 12; // Page number - apic_send_ipi(apic_id, vector, APIC_IPI_STARTUP); - smp_delay(200); // 200us - - // Send second SIPI (as per Intel spec) - apic_send_ipi(apic_id, vector, APIC_IPI_STARTUP); - smp_delay(200); // 200us - - // Wait for AP to come online (timeout after 1 second) - for (int i = 0; i < 100; i++) { - // Check if AP marked itself online - for (uint8_t cpu_id = 0; cpu_id < cpu_count; cpu_id++) { - if (cpu_info[cpu_id].apic_id == apic_id && cpu_info[cpu_id].online) { - return true; - } - } - smp_delay(10000); // 10ms - } - - return false; -} - -// Initialize SMP -void smp_init(void) { - // Check if APIC is available - if (!apic_is_available()) { - // Single core mode - smp_init_cpu(BSP_CPU_ID, 0); - cpu_info[BSP_CPU_ID].online = true; - cpu_count = 1; - smp_enabled = false; - return; - } - - // Initialize APIC - apic_init(); - - // Get BSP APIC ID - uint8_t bsp_apic_id = apic_get_id(); - smp_init_cpu(BSP_CPU_ID, bsp_apic_id); - cpu_info[BSP_CPU_ID].online = true; - - // Detect additional CPUs from APIC - // For simplicity, we'll try to start CPUs with sequential APIC IDs - // A real implementation would parse ACPI MADT table - - uint8_t max_cpus_to_try = 12; // Try up to 12 logical processors - - for (uint8_t apic_id = 0; apic_id < max_cpus_to_try && cpu_count < MAX_CPUS; apic_id++) { - // Skip BSP - if (apic_id == bsp_apic_id) { - continue; - } - - // Initialize CPU entry - smp_init_cpu(cpu_count, apic_id); - - // Try to start this AP - if (smp_start_ap(apic_id)) { - cpu_count++; - } - } - - smp_enabled = (cpu_count > 1); -} - -// Get number of CPUs -uint8_t smp_get_cpu_count(void) { - return cpu_count; -} - -// Check if SMP is enabled -bool smp_is_enabled(void) { - return smp_enabled; -} - -// Get CPU info -cpu_info_t* smp_get_cpu_info(uint8_t cpu_id) { - if (cpu_id >= MAX_CPUS) { - return NULL; - } - return &cpu_info[cpu_id]; -} diff --git a/kernel/src/smp.cpp b/kernel/src/smp.cpp new file mode 100644 index 0000000..f3e9e40 --- /dev/null +++ b/kernel/src/smp.cpp @@ -0,0 +1,185 @@ +/* + * MetalOS Kernel - SMP (Symmetric Multi-Processing) Support + * + * Basic multicore support for better performance + * Initializes Application Processors (APs) using SIPI protocol + */ + +#include "kernel/smp.h" +#include "kernel/apic.h" +#include "kernel/memory.h" + +// Bootstrap CPU is always CPU 0 +#define BSP_CPU_ID 0 + +// Trampoline code location (must be in low memory for real mode) +#define AP_TRAMPOLINE_ADDR 0x8000 + +// AP startup code (will be copied to low memory) +extern "C" { + void ap_trampoline_start(void); + void ap_trampoline_end(void); +} + +// SMPManager class implementation +SMPManager::SMPManager() : cpuCount(1), smpEnabled(false) { + // Initialize BSP + cpuInfo[0].cpu_id = BSP_CPU_ID; + cpuInfo[0].apic_id = 0; + cpuInfo[0].online = false; + cpuInfo[0].kernel_stack = 0; +} + +uint8_t SMPManager::getCurrentCPU() const { + if (!smpEnabled) { + return BSP_CPU_ID; + } + + uint8_t apicId = apic_get_id(); + + // Find CPU by APIC ID + for (uint8_t i = 0; i < cpuCount; i++) { + if (cpuInfo[i].apic_id == apicId) { + return cpuInfo[i].cpu_id; + } + } + + return BSP_CPU_ID; +} + +void SMPManager::initCPU(uint8_t cpuId, uint8_t apicId) { + if (cpuId >= MAX_CPUS) return; + + cpuInfo[cpuId].cpu_id = cpuId; + cpuInfo[cpuId].apic_id = apicId; + cpuInfo[cpuId].online = false; + cpuInfo[cpuId].kernel_stack = 0; +} + +void SMPManager::markCPUOnline(uint8_t cpuId) { + if (cpuId < MAX_CPUS) { + cpuInfo[cpuId].online = true; + } +} + +void SMPManager::delay(uint32_t microseconds) { + // Approximate delay (not precise) + for (volatile uint32_t i = 0; i < microseconds * 100; i++) { + __asm__ volatile("pause"); + } +} + +bool SMPManager::startAP(uint8_t apicId) { + // Send INIT IPI + apic_send_ipi(apicId, 0, APIC_IPI_INIT); + delay(10000); // 10ms + + // Send SIPI (Startup IPI) with trampoline address + uint8_t vector = AP_TRAMPOLINE_ADDR >> 12; // Page number + apic_send_ipi(apicId, vector, APIC_IPI_STARTUP); + delay(200); // 200us + + // Send second SIPI (as per Intel spec) + apic_send_ipi(apicId, vector, APIC_IPI_STARTUP); + delay(200); // 200us + + // Wait for AP to come online (timeout after 1 second) + for (int i = 0; i < 100; i++) { + // Check if AP marked itself online + for (uint8_t cpuId = 0; cpuId < cpuCount; cpuId++) { + if (cpuInfo[cpuId].apic_id == apicId && cpuInfo[cpuId].online) { + return true; + } + } + delay(10000); // 10ms + } + + return false; +} + +void SMPManager::init() { + // Check if APIC is available + if (!apic_is_available()) { + // Single core mode + initCPU(BSP_CPU_ID, 0); + cpuInfo[BSP_CPU_ID].online = true; + cpuCount = 1; + smpEnabled = false; + return; + } + + // Initialize APIC + apic_init(); + + // Get BSP APIC ID + uint8_t bspApicId = apic_get_id(); + initCPU(BSP_CPU_ID, bspApicId); + cpuInfo[BSP_CPU_ID].online = true; + + // Detect additional CPUs from APIC + uint8_t maxCPUsToTry = 12; // Try up to 12 logical processors + + for (uint8_t apicId = 0; apicId < maxCPUsToTry && cpuCount < MAX_CPUS; apicId++) { + // Skip BSP + if (apicId == bspApicId) { + continue; + } + + // Initialize CPU entry + initCPU(cpuCount, apicId); + + // Try to start this AP + if (startAP(apicId)) { + cpuCount++; + } + } + + smpEnabled = (cpuCount > 1); +} + +uint8_t SMPManager::getCPUCount() const { + return cpuCount; +} + +bool SMPManager::isEnabled() const { + return smpEnabled; +} + +cpu_info_t* SMPManager::getCPUInfo(uint8_t cpuId) { + if (cpuId >= MAX_CPUS) { + return nullptr; + } + return &cpuInfo[cpuId]; +} + +// Global SMP manager instance +static SMPManager globalSMP; + +// C-compatible wrapper functions +extern "C" { + +void smp_init(void) { + globalSMP.init(); +} + +uint8_t smp_get_cpu_count(void) { + return globalSMP.getCPUCount(); +} + +uint8_t smp_get_current_cpu(void) { + return globalSMP.getCurrentCPU(); +} + +bool smp_is_enabled(void) { + return globalSMP.isEnabled(); +} + +cpu_info_t* smp_get_cpu_info(uint8_t cpu_id) { + return globalSMP.getCPUInfo(cpu_id); +} + +void smp_cpu_online(uint8_t cpu_id) { + globalSMP.markCPUOnline(cpu_id); +} + +} // extern "C" diff --git a/kernel/src/spinlock.c b/kernel/src/spinlock.cpp similarity index 58% rename from kernel/src/spinlock.c rename to kernel/src/spinlock.cpp index 40086b2..da8817a 100644 --- a/kernel/src/spinlock.c +++ b/kernel/src/spinlock.cpp @@ -7,19 +7,21 @@ #include "kernel/spinlock.h" -// Initialize spinlock -void spinlock_init(spinlock_t* lock) { - lock->lock = 0; +// Spinlock class implementation + +Spinlock::Spinlock() : lock(0) {} + +void Spinlock::init() { + lock = 0; } -// Acquire spinlock (blocking) -void spinlock_acquire(spinlock_t* lock) { +void Spinlock::acquire() { while (1) { // Try to acquire lock using atomic exchange uint32_t old_value; __asm__ volatile( "xchgl %0, %1" - : "=r"(old_value), "+m"(lock->lock) + : "=r"(old_value), "+m"(lock) : "0"(1) : "memory" ); @@ -34,7 +36,55 @@ void spinlock_acquire(spinlock_t* lock) { } } -// Try to acquire spinlock (non-blocking) +bool Spinlock::tryAcquire() { + uint32_t old_value; + __asm__ volatile( + "xchgl %0, %1" + : "=r"(old_value), "+m"(lock) + : "0"(1) + : "memory" + ); + + return (old_value == 0); +} + +void Spinlock::release() { + // Memory barrier to ensure all previous stores are visible + __asm__ volatile("" ::: "memory"); + + // Release the lock + lock = 0; +} + +bool Spinlock::isLocked() const { + return lock != 0; +} + +// C-compatible wrapper functions for backward compatibility +extern "C" { + +void spinlock_init(spinlock_t* lock) { + lock->lock = 0; +} + +void spinlock_acquire(spinlock_t* lock) { + while (1) { + uint32_t old_value; + __asm__ volatile( + "xchgl %0, %1" + : "=r"(old_value), "+m"(lock->lock) + : "0"(1) + : "memory" + ); + + if (old_value == 0) { + return; + } + + __asm__ volatile("pause" ::: "memory"); + } +} + bool spinlock_try_acquire(spinlock_t* lock) { uint32_t old_value; __asm__ volatile( @@ -47,16 +97,13 @@ bool spinlock_try_acquire(spinlock_t* lock) { return (old_value == 0); } -// Release spinlock void spinlock_release(spinlock_t* lock) { - // Memory barrier to ensure all previous stores are visible __asm__ volatile("" ::: "memory"); - - // Release the lock lock->lock = 0; } -// Check if locked bool spinlock_is_locked(spinlock_t* lock) { return lock->lock != 0; } + +} // extern "C" diff --git a/kernel/src/timer.c b/kernel/src/timer.cpp similarity index 68% rename from kernel/src/timer.c rename to kernel/src/timer.cpp index ecd89e8..80d3596 100644 --- a/kernel/src/timer.c +++ b/kernel/src/timer.cpp @@ -15,9 +15,6 @@ // PIT constants #define PIT_BASE_FREQUENCY 1193182 // Hz -// Tick counter -static volatile uint64_t timer_ticks = 0; - // I/O port access functions static inline void outb(uint16_t port, uint8_t value) { __asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port)); @@ -29,8 +26,10 @@ static inline uint8_t inb(uint16_t port) { return value; } -// Initialize timer -void timer_init(uint32_t frequency) { +// Timer class implementation +Timer::Timer() : ticks(0) {} + +void Timer::init(uint32_t frequency) { // Calculate divisor uint32_t divisor = PIT_BASE_FREQUENCY / frequency; @@ -46,22 +45,45 @@ void timer_init(uint32_t frequency) { uint8_t mask = inb(PIC1_DATA); mask &= ~0x01; // Clear bit 0 (IRQ0) outb(PIC1_DATA, mask); + + ticks = 0; } -// Get current tick count -uint64_t timer_get_ticks(void) { - return timer_ticks; +uint64_t Timer::getTicks() const { + return ticks; } -// Wait for specified number of ticks -void timer_wait(uint32_t ticks) { - uint64_t target = timer_ticks + ticks; - while (timer_ticks < target) { +void Timer::wait(uint32_t waitTicks) const { + uint64_t target = ticks + waitTicks; + while (ticks < target) { __asm__ volatile("hlt"); } } -// Timer interrupt handler -void timer_handler(void) { - timer_ticks++; +void Timer::handleInterrupt() { + ticks++; } + +// Global timer instance +static Timer globalTimer; + +// C-compatible wrapper functions +extern "C" { + +void timer_init(uint32_t frequency) { + globalTimer.init(frequency); +} + +uint64_t timer_get_ticks(void) { + return globalTimer.getTicks(); +} + +void timer_wait(uint32_t ticks) { + globalTimer.wait(ticks); +} + +void timer_handler(void) { + globalTimer.handleInterrupt(); +} + +} // extern "C"