Add CMake, Ninja, and Conan support with kernel modules (GDT, interrupts)

Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-12-28 20:03:46 +00:00
parent 025e58f391
commit 5c8cf64442
17 changed files with 1170 additions and 6 deletions

20
.gitignore vendored
View File

@@ -20,6 +20,26 @@ build/
bootloader/build/
kernel/build/
# CMake
CMakeCache.txt
CMakeFiles/
cmake-build-*/
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps/
# Ninja
.ninja_deps
.ninja_log
# Conan
conan.lock
conaninfo.txt
conanbuildinfo.*
graph_info.json
# Test binaries
tests/unit/test_*
!tests/unit/*.c

147
CMakeLists.txt Normal file
View File

@@ -0,0 +1,147 @@
cmake_minimum_required(VERSION 3.16)
project(MetalOS
VERSION 0.1.0
DESCRIPTION "Minimal OS for QT6 on AMD64 + RX 6600"
LANGUAGES C CXX ASM_NASM
)
# Enable assembly with NASM
enable_language(ASM_NASM)
# Set C standard
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
# Set C++ standard (for QT6 app later)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Build type
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
# Output directories
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
# Platform-specific flags
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
# Global compile options for bare metal
add_compile_options(
-Wall
-Wextra
-Werror
-ffreestanding
-fno-stack-protector
-mno-red-zone
)
# NASM flags for assembly files
set(CMAKE_ASM_NASM_OBJECT_FORMAT elf64)
set(CMAKE_ASM_NASM_COMPILE_OBJECT "<CMAKE_ASM_NASM_COMPILER> <INCLUDES> <FLAGS> -f ${CMAKE_ASM_NASM_OBJECT_FORMAT} -o <OBJECT> <SOURCE>")
# Add subdirectories
add_subdirectory(bootloader)
add_subdirectory(kernel)
add_subdirectory(userspace)
# Testing
enable_testing()
add_subdirectory(tests)
# Custom target to create bootable image
add_custom_target(image
COMMAND ${CMAKE_SOURCE_DIR}/scripts/create_image.sh
DEPENDS bootloader kernel
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Creating bootable disk image"
)
# QEMU targets for testing
set(QEMU_DISPLAY "none" CACHE STRING "QEMU display mode (none, gtk, sdl)")
# Find OVMF firmware
find_file(OVMF_FIRMWARE
NAMES OVMF_CODE.fd OVMF.fd ovmf-x86_64.bin
PATHS
/usr/share/OVMF
/usr/share/ovmf
/usr/share/edk2-ovmf/x64
/usr/share/qemu
DOC "UEFI firmware for QEMU"
)
if(OVMF_FIRMWARE)
message(STATUS "Found OVMF firmware: ${OVMF_FIRMWARE}")
# QEMU run target
add_custom_target(qemu
COMMAND qemu-system-x86_64
-drive if=pflash,format=raw,readonly=on,file=${OVMF_FIRMWARE}
-drive format=raw,file=${CMAKE_BINARY_DIR}/metalos.img
-m 512M
-serial stdio
-display ${QEMU_DISPLAY}
-net none
DEPENDS image
COMMENT "Running MetalOS in QEMU with UEFI"
)
# QEMU debug target
add_custom_target(qemu-debug
COMMAND qemu-system-x86_64
-drive if=pflash,format=raw,readonly=on,file=${OVMF_FIRMWARE}
-drive format=raw,file=${CMAKE_BINARY_DIR}/metalos.img
-m 512M
-serial stdio
-display ${QEMU_DISPLAY}
-net none
-d int,cpu_reset
DEPENDS image
COMMENT "Running MetalOS in QEMU with debug output"
)
# QEMU GDB target
add_custom_target(qemu-gdb
COMMAND qemu-system-x86_64
-drive if=pflash,format=raw,readonly=on,file=${OVMF_FIRMWARE}
-drive format=raw,file=${CMAKE_BINARY_DIR}/metalos.img
-m 512M
-serial stdio
-display ${QEMU_DISPLAY}
-net none
-s -S
DEPENDS image
COMMENT "Running MetalOS in QEMU with GDB server on port 1234"
)
# QEMU UEFI test (no OS image)
add_custom_target(qemu-uefi-test
COMMAND qemu-system-x86_64
-drive if=pflash,format=raw,readonly=on,file=${OVMF_FIRMWARE}
-m 512M
-nographic
-net none
COMMENT "Testing QEMU UEFI boot (no OS image)"
)
else()
message(WARNING "OVMF firmware not found. QEMU targets will not be available.")
message(WARNING "Install OVMF:")
message(WARNING " Ubuntu/Debian: sudo apt-get install ovmf")
message(WARNING " Arch Linux: sudo pacman -S edk2-ovmf")
message(WARNING " Fedora: sudo dnf install edk2-ovmf")
endif()
# Print build configuration
message(STATUS "MetalOS Build Configuration:")
message(STATUS " Version: ${PROJECT_VERSION}")
message(STATUS " Build Type: ${CMAKE_BUILD_TYPE}")
message(STATUS " C Compiler: ${CMAKE_C_COMPILER}")
message(STATUS " C++ Compiler: ${CMAKE_CXX_COMPILER}")
message(STATUS " NASM: ${CMAKE_ASM_NASM_COMPILER}")
message(STATUS " QEMU Display: ${QEMU_DISPLAY}")

67
bootloader/CMakeLists.txt Normal file
View File

@@ -0,0 +1,67 @@
# MetalOS Bootloader CMakeLists
# Bootloader-specific compiler flags
set(BOOTLOADER_CFLAGS
-fshort-wchar
-fno-stack-check
-DEFI_FUNCTION_WRAPPER
)
# Bootloader-specific linker flags
set(BOOTLOADER_LDFLAGS
-shared
-Bsymbolic
-nostdlib
-znocombreloc
)
# Source files
set(BOOTLOADER_SOURCES
src/main.c
)
# Include directories
include_directories(include)
# Create bootloader object files
add_library(bootloader_objs OBJECT
${BOOTLOADER_SOURCES}
)
target_compile_options(bootloader_objs PRIVATE ${BOOTLOADER_CFLAGS})
# Link bootloader as shared object first
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/bootx64.so
COMMAND ${CMAKE_LINKER}
${BOOTLOADER_LDFLAGS}
-T ${CMAKE_CURRENT_SOURCE_DIR}/uefi.lds
$<TARGET_OBJECTS:bootloader_objs>
-o ${CMAKE_CURRENT_BINARY_DIR}/bootx64.so
DEPENDS bootloader_objs ${CMAKE_CURRENT_SOURCE_DIR}/uefi.lds
COMMENT "Linking bootloader shared object"
VERBATIM
)
# Convert to EFI binary
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/bootx64.efi
COMMAND ${CMAKE_OBJCOPY}
-j .text -j .sdata -j .data -j .dynamic
-j .dynsym -j .rel -j .rela -j .reloc
--target=efi-app-x86_64
${CMAKE_CURRENT_BINARY_DIR}/bootx64.so
${CMAKE_CURRENT_BINARY_DIR}/bootx64.efi
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/bootx64.so
COMMENT "Creating EFI bootloader binary"
VERBATIM
)
add_custom_target(bootloader ALL
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/bootx64.efi
)
# Install bootloader binary
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/bootx64.efi
DESTINATION bin
)

13
conan_profile Normal file
View File

@@ -0,0 +1,13 @@
[settings]
os=Linux
arch=x86_64
compiler=gcc
compiler.version=13
compiler.libcxx=libstdc++11
build_type=Release
[options]
[build_requires]
[env]

62
conanfile.py Normal file
View File

@@ -0,0 +1,62 @@
from conan import ConanFile
from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout
class MetalOSConan(ConanFile):
name = "metalos"
version = "0.1.0"
# Project metadata
license = "MIT"
author = "MetalOS Contributors"
url = "https://github.com/johndoe6345789/MetalOS"
description = "Minimal OS for QT6 on AMD64 + RX 6600"
topics = ("os", "kernel", "uefi", "qt6", "minimal")
# Build settings
settings = "os", "compiler", "build_type", "arch"
# Build options
options = {
"with_tests": [True, False],
"qemu_display": ["none", "gtk", "sdl"]
}
default_options = {
"with_tests": True,
"qemu_display": "none"
}
# Sources are in the same repo
exports_sources = (
"CMakeLists.txt",
"bootloader/*",
"kernel/*",
"userspace/*",
"tests/*",
"scripts/*",
"docs/*"
)
def layout(self):
cmake_layout(self)
def generate(self):
tc = CMakeToolchain(self)
tc.variables["BUILD_TESTING"] = self.options.with_tests
tc.variables["QEMU_DISPLAY"] = str(self.options.qemu_display)
tc.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
if self.options.with_tests:
cmake.test()
def package(self):
cmake = CMake(self)
cmake.install()
def package_info(self):
self.cpp_info.libs = ["metalos"]

307
docs/BUILD_SYSTEMS.md Normal file
View File

@@ -0,0 +1,307 @@
# MetalOS Build Instructions
This document describes how to build MetalOS using various build systems.
## Prerequisites
### Required Tools
- **Compiler**: GCC 11+ or Clang 13+
- **Assembler**: NASM 2.14+
- **CMake**: 3.16+
- **Ninja** (optional): 1.10+
- **Conan** (optional): 2.0+
- **QEMU**: For testing (with OVMF firmware)
### Install on Ubuntu/Debian
```bash
# Basic build tools
sudo apt-get update
sudo apt-get install -y build-essential cmake nasm
# Ninja build system (optional but faster)
sudo apt-get install -y ninja-build
# QEMU and UEFI firmware for testing
sudo apt-get install -y qemu-system-x86 ovmf
# Conan package manager (optional)
pip3 install conan
```
### Install on Arch Linux
```bash
sudo pacman -S base-devel cmake nasm ninja qemu edk2-ovmf
pip install conan
```
### Install on Fedora
```bash
sudo dnf install gcc gcc-c++ cmake nasm ninja-build qemu edk2-ovmf
pip install conan
```
## Build Methods
### Method 1: CMake with Make (Traditional)
```bash
# Configure
mkdir build && cd build
cmake ..
# Build
make -j$(nproc)
# Build specific targets
make bootloader
make kernel
# Test in QEMU
make image
make qemu
```
### Method 2: CMake with Ninja (Faster)
```bash
# Configure with Ninja
mkdir build && cd build
cmake -G Ninja ..
# Build (much faster than make)
ninja
# Build specific targets
ninja bootloader
ninja kernel
# Test in QEMU
ninja image
ninja qemu
```
### Method 3: Conan + CMake (Modern)
```bash
# Install dependencies (if any are added in future)
conan install . --build=missing
# Build with Conan
conan build .
# Or build with specific profile
conan create . --build=missing
```
### Method 4: Conan + Ninja (Recommended)
```bash
# Configure Conan to use Ninja
conan install . --build=missing -c tools.cmake.cmaketoolchain:generator=Ninja
# Build
conan build .
```
## Build Configuration
### CMake Options
```bash
# Debug build
cmake -DCMAKE_BUILD_TYPE=Debug ..
# Release build (default)
cmake -DCMAKE_BUILD_TYPE=Release ..
# Set QEMU display mode
cmake -DQEMU_DISPLAY=gtk ..
```
### Conan Options
```bash
# With tests (default)
conan build . -o with_tests=True
# Without tests
conan build . -o with_tests=False
# Set QEMU display
conan build . -o qemu_display=gtk
```
## Quick Start
### Fastest Build (Recommended)
```bash
# One-time setup
pip3 install conan
sudo apt-get install -y cmake ninja-build nasm
# Build and test
mkdir build && cd build
cmake -G Ninja ..
ninja
ninja qemu
```
### Simple Build (No extra tools)
```bash
# Just CMake and Make
mkdir build && cd build
cmake ..
make -j$(nproc)
make qemu
```
## Build Targets
### Main Targets
- **all**: Build bootloader and kernel (default)
- **bootloader**: Build UEFI bootloader only
- **kernel**: Build kernel only
- **image**: Create bootable disk image
- **clean**: Clean build artifacts
### Testing Targets
- **test**: Run unit tests
- **qemu**: Run in QEMU with UEFI
- **qemu-debug**: Run with debug output
- **qemu-gdb**: Run with GDB debugging
- **qemu-uefi-test**: Test UEFI boot without OS
## Build Output
```
build/
├── bootloader/
│ └── bootx64.efi # UEFI bootloader
├── kernel/
│ └── metalos.bin # Kernel binary
└── metalos.img # Bootable disk image
```
## Troubleshooting
### NASM not found
```bash
sudo apt-get install nasm
# Or download from https://www.nasm.us/
```
### OVMF firmware not found
```bash
# Ubuntu/Debian
sudo apt-get install ovmf
# Arch Linux
sudo pacman -S edk2-ovmf
# Fedora
sudo dnf install edk2-ovmf
```
### Ninja not found
```bash
sudo apt-get install ninja-build
# Or use make instead: cmake .. (without -G Ninja)
```
### Conan not found
```bash
pip3 install conan
# Or use system package manager
```
## Performance Tips
### Parallel Builds
```bash
# Make (use all CPU cores)
make -j$(nproc)
# Ninja (automatically uses all cores)
ninja
# Conan
conan build . -c tools.cmake.cmake_toolchain:jobs=$(nproc)
```
### Incremental Builds
- Ninja is faster for incremental builds
- Use `ccache` for faster recompilation:
```bash
sudo apt-get install ccache
export CC="ccache gcc"
export CXX="ccache g++"
cmake ..
```
### Clean Builds
```bash
# Full clean
rm -rf build/
mkdir build && cd build
cmake ..
# Or use target
make clean # Make
ninja clean # Ninja
```
## Cross-Compilation
MetalOS is built for x86_64 bare metal. If you need a cross-compiler:
```bash
# Install x86_64 bare metal toolchain
sudo apt-get install gcc-x86-64-linux-gnu
# Configure CMake to use it
cmake -DCMAKE_C_COMPILER=x86_64-linux-gnu-gcc ..
```
## CI/CD Integration
### GitHub Actions Example
```yaml
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y cmake ninja-build nasm ovmf qemu-system-x86
- name: Build with Ninja
run: |
mkdir build && cd build
cmake -G Ninja ..
ninja
- name: Run tests
run: |
cd build
ctest --output-on-failure
```
## Additional Resources
- [CMake Documentation](https://cmake.org/documentation/)
- [Ninja Build System](https://ninja-build.org/)
- [Conan Documentation](https://docs.conan.io/)
- [NASM Documentation](https://www.nasm.us/doc/)

79
kernel/CMakeLists.txt Normal file
View File

@@ -0,0 +1,79 @@
# MetalOS Kernel CMakeLists
# Include directories
include_directories(include)
# Source files
set(KERNEL_C_SOURCES
src/main.c
src/gdt.c
src/interrupts.c
)
set(KERNEL_ASM_SOURCES
src/gdt_flush.asm
src/interrupts_asm.asm
)
# Kernel-specific C compiler flags
set(KERNEL_CFLAGS
-mcmodel=large
-O2
)
# Create kernel C object files
add_library(kernel_c_objs OBJECT
${KERNEL_C_SOURCES}
)
target_compile_options(kernel_c_objs PRIVATE ${KERNEL_CFLAGS})
target_include_directories(kernel_c_objs PRIVATE include)
# Create kernel ASM object files separately
# We need to avoid CMake adding C flags to NASM
foreach(asm_file ${KERNEL_ASM_SOURCES})
get_filename_component(asm_name ${asm_file} NAME_WE)
set(asm_obj ${CMAKE_CURRENT_BINARY_DIR}/${asm_name}.o)
add_custom_command(
OUTPUT ${asm_obj}
COMMAND ${CMAKE_ASM_NASM_COMPILER}
-f elf64
-I ${CMAKE_CURRENT_SOURCE_DIR}/include
-o ${asm_obj}
${CMAKE_CURRENT_SOURCE_DIR}/${asm_file}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${asm_file}
COMMENT "Assembling ${asm_file}"
VERBATIM
)
list(APPEND KERNEL_ASM_OBJS ${asm_obj})
endforeach()
# Custom target for ASM objects
add_custom_target(kernel_asm_objs
DEPENDS ${KERNEL_ASM_OBJS}
)
# Link kernel binary
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/metalos.bin
COMMAND ${CMAKE_LINKER}
-nostdlib
-T ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld
$<TARGET_OBJECTS:kernel_c_objs>
${KERNEL_ASM_OBJS}
-o ${CMAKE_CURRENT_BINARY_DIR}/metalos.bin
DEPENDS kernel_c_objs kernel_asm_objs ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld
COMMENT "Linking kernel binary"
COMMAND_EXPAND_LISTS
)
add_custom_target(kernel ALL
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/metalos.bin
)
# Install kernel binary
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/metalos.bin
DESTINATION bin
)

View File

@@ -0,0 +1,25 @@
#ifndef METALOS_KERNEL_GDT_H
#define METALOS_KERNEL_GDT_H
#include <stdint.h>
// GDT Entry structure
typedef struct {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t access;
uint8_t granularity;
uint8_t base_high;
} __attribute__((packed)) gdt_entry_t;
// GDT Pointer structure
typedef struct {
uint16_t limit;
uint64_t base;
} __attribute__((packed)) gdt_ptr_t;
// Initialize the Global Descriptor Table
void gdt_init(void);
#endif // METALOS_KERNEL_GDT_H

View File

@@ -0,0 +1,37 @@
#ifndef METALOS_KERNEL_INTERRUPTS_H
#define METALOS_KERNEL_INTERRUPTS_H
#include <stdint.h>
// IDT Entry structure
typedef struct {
uint16_t offset_low; // Offset bits 0-15
uint16_t selector; // Code segment selector
uint8_t ist; // Interrupt Stack Table offset
uint8_t type_attr; // Type and attributes
uint16_t offset_mid; // Offset bits 16-31
uint32_t offset_high; // Offset bits 32-63
uint32_t zero; // Reserved
} __attribute__((packed)) idt_entry_t;
// IDT Pointer structure
typedef struct {
uint16_t limit;
uint64_t base;
} __attribute__((packed)) idt_ptr_t;
// CPU registers state (for interrupt handlers)
typedef struct {
uint64_t r15, r14, r13, r12, r11, r10, r9, r8;
uint64_t rbp, rdi, rsi, rdx, rcx, rbx, rax;
uint64_t int_no, err_code;
uint64_t rip, cs, rflags, rsp, ss;
} __attribute__((packed)) registers_t;
// Initialize Interrupt Descriptor Table
void idt_init(void);
// Generic interrupt handler (called from assembly)
void interrupt_handler(registers_t* regs);
#endif // METALOS_KERNEL_INTERRUPTS_H

56
kernel/src/gdt.c Normal file
View File

@@ -0,0 +1,56 @@
/*
* 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);
}

26
kernel/src/gdt_flush.asm Normal file
View File

@@ -0,0 +1,26 @@
; GDT flush assembly routine
; Load new GDT and update segment registers
global gdt_flush
extern gdt_ptr
section .text
bits 64
gdt_flush:
lgdt [rdi] ; Load GDT pointer (first argument in rdi)
; Reload segments
mov ax, 0x10 ; Kernel data segment offset
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; Far return to reload CS
pop rdi ; Get return address
mov rax, 0x08 ; Kernel code segment offset
push rax
push rdi
retfq ; Far return

160
kernel/src/interrupts.c Normal file
View File

@@ -0,0 +1,160 @@
/*
* MetalOS Kernel - Interrupt Handling
*
* Minimal IDT and interrupt handlers
* Only essential interrupts for QT6 app
*/
#include "kernel/interrupts.h"
// 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
__asm__ volatile("outb %0, $0x20" : : "a"((uint8_t)0x11)); // Master PIC
__asm__ volatile("outb %0, $0xA0" : : "a"((uint8_t)0x11)); // Slave PIC
// ICW2: Set interrupt vector offsets
__asm__ volatile("outb %0, $0x21" : : "a"((uint8_t)0x20)); // Master offset to 0x20
__asm__ volatile("outb %0, $0xA1" : : "a"((uint8_t)0x28)); // Slave offset to 0x28
// ICW3: Set up cascade
__asm__ volatile("outb %0, $0x21" : : "a"((uint8_t)0x04)); // Tell master about slave
__asm__ volatile("outb %0, $0xA1" : : "a"((uint8_t)0x02)); // Tell slave its cascade
// ICW4: Set mode
__asm__ volatile("outb %0, $0x21" : : "a"((uint8_t)0x01));
__asm__ volatile("outb %0, $0xA1" : : "a"((uint8_t)0x01));
// Mask all interrupts initially
__asm__ volatile("outb %0, $0x21" : : "a"((uint8_t)0xFF));
__asm__ volatile("outb %0, $0xA1" : : "a"((uint8_t)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 interrupt based on interrupt number
(void)regs; // Suppress unused warning for now
// TODO: Dispatch to specific handlers based on regs->int_no
// Send EOI (End of Interrupt) to PIC if this was an IRQ
if (regs->int_no >= 32 && regs->int_no < 48) {
if (regs->int_no >= 40) {
// Slave PIC
__asm__ volatile("outb %0, $0xA0" : : "a"((uint8_t)0x20));
}
// Master PIC
__asm__ volatile("outb %0, $0x20" : : "a"((uint8_t)0x20));
}
}

View File

@@ -0,0 +1,120 @@
; Interrupt Service Routines (ISRs) for x86_64
; Assembly stubs that save state and call C handler
global isr0, isr1, isr2, isr3, isr4, isr5, isr6, isr7
global isr8, isr9, isr10, isr11, isr12, isr13, isr14, isr15
global isr16, isr17, isr18, isr19, isr20, isr21, isr22, isr23
global isr24, isr25, isr26, isr27, isr28, isr29, isr30, isr31
global irq0, irq1
extern interrupt_handler
section .text
bits 64
; Macro for ISRs without error code
%macro ISR_NOERRCODE 1
isr%1:
push qword 0 ; Dummy error code
push qword %1 ; Interrupt number
jmp isr_common_stub
%endmacro
; Macro for ISRs with error code
%macro ISR_ERRCODE 1
isr%1:
push qword %1 ; Interrupt number
jmp isr_common_stub
%endmacro
; Macro for IRQs
%macro IRQ 2
irq%1:
push qword 0 ; Dummy error code
push qword %2 ; Interrupt number
jmp isr_common_stub
%endmacro
; CPU Exceptions (0-31)
ISR_NOERRCODE 0 ; Divide by zero
ISR_NOERRCODE 1 ; Debug
ISR_NOERRCODE 2 ; Non-maskable interrupt
ISR_NOERRCODE 3 ; Breakpoint
ISR_NOERRCODE 4 ; Overflow
ISR_NOERRCODE 5 ; Bound range exceeded
ISR_NOERRCODE 6 ; Invalid opcode
ISR_NOERRCODE 7 ; Device not available
ISR_ERRCODE 8 ; Double fault
ISR_NOERRCODE 9 ; Coprocessor segment overrun
ISR_ERRCODE 10 ; Invalid TSS
ISR_ERRCODE 11 ; Segment not present
ISR_ERRCODE 12 ; Stack-segment fault
ISR_ERRCODE 13 ; General protection fault
ISR_ERRCODE 14 ; Page fault
ISR_NOERRCODE 15 ; Reserved
ISR_NOERRCODE 16 ; x87 floating-point exception
ISR_ERRCODE 17 ; Alignment check
ISR_NOERRCODE 18 ; Machine check
ISR_NOERRCODE 19 ; SIMD floating-point exception
ISR_NOERRCODE 20 ; Virtualization exception
ISR_NOERRCODE 21 ; Reserved
ISR_NOERRCODE 22 ; Reserved
ISR_NOERRCODE 23 ; Reserved
ISR_NOERRCODE 24 ; Reserved
ISR_NOERRCODE 25 ; Reserved
ISR_NOERRCODE 26 ; Reserved
ISR_NOERRCODE 27 ; Reserved
ISR_NOERRCODE 28 ; Reserved
ISR_NOERRCODE 29 ; Reserved
ISR_ERRCODE 30 ; Security exception
ISR_NOERRCODE 31 ; Reserved
; IRQs (32-47)
IRQ 0, 32 ; Timer
IRQ 1, 33 ; Keyboard
; Common ISR stub - saves state and calls C handler
isr_common_stub:
; Save all general purpose registers
push rax
push rbx
push rcx
push rdx
push rsi
push rdi
push rbp
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
; Call C handler with pointer to register state
mov rdi, rsp
call interrupt_handler
; Restore all registers
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rbp
pop rdi
pop rsi
pop rdx
pop rcx
pop rbx
pop rax
; Clean up error code and interrupt number
add rsp, 16
; Return from interrupt
iretq

View File

@@ -7,6 +7,8 @@
*/
#include "kernel/kernel.h"
#include "kernel/gdt.h"
#include "kernel/interrupts.h"
/*
* Kernel main entry point
@@ -19,13 +21,13 @@ void kernel_main(BootInfo* boot_info) {
// Suppress unused parameter warning
(void)boot_info;
// TODO: Set up minimal page tables (identity mapped or simple offset)
// Initialize GDT (Global Descriptor Table)
gdt_init();
// TODO: Set up IDT with only interrupts we need:
// - Keyboard/mouse (USB or PS/2)
// - Timer (for QT event loop)
// - GPU (if needed)
// That's it! Maybe 5 interrupt handlers total.
// Initialize IDT (Interrupt Descriptor Table)
idt_init();
// TODO: Set up minimal page tables (identity mapped or simple offset)
// TODO: Simple memory allocator (bump allocator is fine)

10
tests/CMakeLists.txt Normal file
View File

@@ -0,0 +1,10 @@
# MetalOS Tests CMakeLists
# Include test framework
include_directories(include)
# Unit tests subdirectory
add_subdirectory(unit)
# Integration tests can be added here later
# add_subdirectory(integration)

23
tests/unit/CMakeLists.txt Normal file
View File

@@ -0,0 +1,23 @@
# MetalOS Unit Tests CMakeLists
# Test sources
set(TEST_SOURCES
test_bootloader.c
)
# Create test executable for each test file
foreach(test_src ${TEST_SOURCES})
get_filename_component(test_name ${test_src} NAME_WE)
add_executable(${test_name} ${test_src})
# Link with test framework (if needed)
target_include_directories(${test_name} PRIVATE
${CMAKE_SOURCE_DIR}/tests/include
${CMAKE_SOURCE_DIR}/bootloader/include
${CMAKE_SOURCE_DIR}/kernel/include
)
# Add as test
add_test(NAME ${test_name} COMMAND ${test_name})
endforeach()

10
userspace/CMakeLists.txt Normal file
View File

@@ -0,0 +1,10 @@
# MetalOS Userspace CMakeLists
# Placeholder for userspace components
# Will be expanded when implementing user applications
message(STATUS "Userspace build configuration (placeholder)")
# Add subdirectories when ready
# add_subdirectory(apps)
# add_subdirectory(libs)