mirror of
https://github.com/johndoe6345789/MetalOS.git
synced 2026-04-24 13:45:02 +00:00
Merge pull request #18 from johndoe6345789/copilot/add-root-level-functions
Convert MetalOS kernel from C to C++ with class-based architecture and Clang as default compiler
This commit is contained in:
3
.github/workflows/qemu-test.yml
vendored
3
.github/workflows/qemu-test.yml
vendored
@@ -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: |
|
||||
|
||||
3
.github/workflows/release.yml
vendored
3
.github/workflows/release.yml
vendored
@@ -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: |
|
||||
|
||||
4
.github/workflows/unit-tests.yml
vendored
4
.github/workflows/unit-tests.yml
vendored
@@ -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: |
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"]
|
||||
|
||||
20
README.md
20
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
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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 $<TARGET_OBJECTS:kernel_obj> ${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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,24 +4,39 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
@@ -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);
|
||||
}
|
||||
64
kernel/src/gdt.cpp
Normal file
64
kernel/src/gdt.cpp
Normal file
@@ -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"
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
166
kernel/src/interrupts.cpp
Normal file
166
kernel/src/interrupts.cpp
Normal file
@@ -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"
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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"
|
||||
@@ -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<pci_device_t*>(globalPCI.findDevice(vendor_id, device_id));
|
||||
}
|
||||
|
||||
void pci_enable_bus_mastering(pci_device_t* dev) {
|
||||
globalPCI.enableBusMastering(reinterpret_cast<PCIDevice*>(dev));
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
159
kernel/src/smp.c
159
kernel/src/smp.c
@@ -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];
|
||||
}
|
||||
185
kernel/src/smp.cpp
Normal file
185
kernel/src/smp.cpp
Normal file
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
Reference in New Issue
Block a user