From 9cffb01889e6eb371b65adfb941254207419f507 Mon Sep 17 00:00:00 2001 From: RarDog Date: Wed, 2 Sep 2026 16:47:37 +0300 Subject: [PATCH] feat: initial scalable x86_64 microkernel skeleton with Limine, Rust, C HAL, and C++20 IPC --- .gitignore | 21 +++++ CMakeLists.txt | 43 +++++++++ Cargo.toml | 16 ++++ Makefile | 115 +++++++++++++++++++++++ README.md | 149 +++++++++++++++++++++++++++++ config/linker.ld | 51 ++++++++++ config/user.ld | 36 +++++++ hal/include/apic.h | 29 ++++++ hal/include/pci.h | 34 +++++++ hal/include/uart.h | 36 +++++++ hal/src/apic.c | 31 ++++++ hal/src/pci.c | 79 ++++++++++++++++ hal/src/uart.c | 98 +++++++++++++++++++ include/abi/ipc.h | 49 ++++++++++ include/abi/syscalls.h | 73 ++++++++++++++ include/abi/types.h | 38 ++++++++ kernel/Cargo.toml | 10 ++ kernel/asm/context.S | 33 +++++++ kernel/asm/syscall_entry.S | 53 +++++++++++ kernel/src/arch/gdt.rs | 102 ++++++++++++++++++++ kernel/src/arch/idt.rs | 99 +++++++++++++++++++ kernel/src/arch/mod.rs | 9 ++ kernel/src/arch/syscall.rs | 113 ++++++++++++++++++++++ kernel/src/drivers/mod.rs | 1 + kernel/src/drivers/serial.rs | 83 ++++++++++++++++ kernel/src/ipc/fastpath.rs | 53 +++++++++++ kernel/src/ipc/mod.rs | 5 + kernel/src/limine_requests.rs | 162 ++++++++++++++++++++++++++++++++ kernel/src/main.rs | 120 +++++++++++++++++++++++ kernel/src/mm/mod.rs | 14 +++ kernel/src/mm/pfa.rs | 167 +++++++++++++++++++++++++++++++++ kernel/src/mm/vmm.rs | 90 ++++++++++++++++++ kernel/src/sched/mod.rs | 35 +++++++ lib/libc/include/stddef.h | 14 +++ lib/libc/include/stdint.h | 24 +++++ lib/libc/include/stdio.h | 15 +++ lib/libc/include/string.h | 23 +++++ lib/libc/src/string.c | 92 ++++++++++++++++++ lib/libc/src/syscalls.c | 17 ++++ lib/libipc_cpp/include/ipc.hpp | 116 +++++++++++++++++++++++ lib/libipc_cpp/src/ipc.cpp | 5 + limine.cfg | 8 ++ limine.conf | 9 ++ rust-toolchain.toml | 3 + servers/init/main.cpp | 32 +++++++ servers/procmgr/main.cpp | 10 ++ servers/uart_driver/main.c | 17 ++++ 47 files changed, 2432 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 Cargo.toml create mode 100644 Makefile create mode 100644 README.md create mode 100644 config/linker.ld create mode 100644 config/user.ld create mode 100644 hal/include/apic.h create mode 100644 hal/include/pci.h create mode 100644 hal/include/uart.h create mode 100644 hal/src/apic.c create mode 100644 hal/src/pci.c create mode 100644 hal/src/uart.c create mode 100644 include/abi/ipc.h create mode 100644 include/abi/syscalls.h create mode 100644 include/abi/types.h create mode 100644 kernel/Cargo.toml create mode 100644 kernel/asm/context.S create mode 100644 kernel/asm/syscall_entry.S create mode 100644 kernel/src/arch/gdt.rs create mode 100644 kernel/src/arch/idt.rs create mode 100644 kernel/src/arch/mod.rs create mode 100644 kernel/src/arch/syscall.rs create mode 100644 kernel/src/drivers/mod.rs create mode 100644 kernel/src/drivers/serial.rs create mode 100644 kernel/src/ipc/fastpath.rs create mode 100644 kernel/src/ipc/mod.rs create mode 100644 kernel/src/limine_requests.rs create mode 100644 kernel/src/main.rs create mode 100644 kernel/src/mm/mod.rs create mode 100644 kernel/src/mm/pfa.rs create mode 100644 kernel/src/mm/vmm.rs create mode 100644 kernel/src/sched/mod.rs create mode 100644 lib/libc/include/stddef.h create mode 100644 lib/libc/include/stdint.h create mode 100644 lib/libc/include/stdio.h create mode 100644 lib/libc/include/string.h create mode 100644 lib/libc/src/string.c create mode 100644 lib/libc/src/syscalls.c create mode 100644 lib/libipc_cpp/include/ipc.hpp create mode 100644 lib/libipc_cpp/src/ipc.cpp create mode 100644 limine.cfg create mode 100644 limine.conf create mode 100644 rust-toolchain.toml create mode 100644 servers/init/main.cpp create mode 100644 servers/procmgr/main.cpp create mode 100644 servers/uart_driver/main.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..62b4218 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# Build artifacts +/build/ +/target/ +*.o +*.a +*.elf +*.bin +*.iso +*.img + +# Cargo +Cargo.lock +**/*.rs.bk + +# Editor and OS files +.vscode/ +.idea/ +*.swp +*.swo +*~ +.DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c526ce1 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 3.20) +project(opencoreC C CXX ASM) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 20) + +# Freestanding flags +set(FREESTANDING_FLAGS "-ffreestanding -fno-stack-protector -fno-pie -no-pie -mno-red-zone -m64") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FREESTANDING_FLAGS} -Wall -Wextra -O2") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FREESTANDING_FLAGS} -Wall -Wextra -O2 -fno-exceptions -fno-rtti") + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/lib/libc/include + ${CMAKE_CURRENT_SOURCE_DIR}/lib/libipc_cpp/include + ${CMAKE_CURRENT_SOURCE_DIR}/hal/include +) + +# Bare-metal libc +add_library(freestanding_libc STATIC + lib/libc/src/string.c + lib/libc/src/syscalls.c +) + +# HAL Library +add_library(hal STATIC + hal/src/uart.c + hal/src/pci.c + hal/src/apic.c +) + +# Ring 3 User Servers +add_executable(init_server servers/init/main.cpp) +target_link_options(init_server PRIVATE -T${CMAKE_CURRENT_SOURCE_DIR}/config/user.ld -nostdlib -static) +target_link_libraries(init_server PRIVATE freestanding_libc) + +add_executable(uart_driver servers/uart_driver/main.c) +target_link_options(uart_driver PRIVATE -T${CMAKE_CURRENT_SOURCE_DIR}/config/user.ld -nostdlib -static) +target_link_libraries(uart_driver PRIVATE hal freestanding_libc) + +add_executable(procmgr servers/procmgr/main.cpp) +target_link_options(procmgr PRIVATE -T${CMAKE_CURRENT_SOURCE_DIR}/config/user.ld -nostdlib -static) +target_link_libraries(procmgr PRIVATE freestanding_libc) diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1e0a026 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[workspace] +members = [ + "kernel", +] +resolver = "2" + +[profile.dev] +panic = "abort" +opt-level = 1 + +[profile.release] +panic = "abort" +opt-level = 3 +lto = true +codegen-units = 1 +debug = false diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cfb1b78 --- /dev/null +++ b/Makefile @@ -0,0 +1,115 @@ +# opencoreC - Root Unified Makefile +# Target Architecture: x86_64 Long Mode (Microkernel OS) + +SHELL := /usr/bin/env bash + +# Toolchain definitions +CC := gcc +CXX := g++ +LD := ld +CARGO := cargo +QEMU := qemu-system-x86_64 + +# Directory layout +BUILD_DIR := build +CONFIG_DIR := config +INCLUDE_DIR := include +LIB_DIR := lib +HAL_DIR := hal +SERVERS_DIR := servers +KERNEL_DIR := kernel +ISO_DIR := $(BUILD_DIR)/iso_root + +# Common Freestanding Flags +COMMON_FLAGS := -Wall -Wextra -O2 -ffreestanding -fno-stack-protector \ + -fno-pie -no-pie -mno-red-zone -m64 \ + -I$(INCLUDE_DIR) -I$(LIB_DIR)/libc/include + +CFLAGS := $(COMMON_FLAGS) -std=c11 +CXXFLAGS := $(COMMON_FLAGS) -std=c++20 -fno-exceptions -fno-rtti -I$(LIB_DIR)/libipc_cpp/include + +# Outputs +KERNEL_BIN := target/x86_64-unknown-none/release/opencore_kernel +KERNEL_ELF := $(BUILD_DIR)/opencore_kernel.elf +INIT_SERVER := $(BUILD_DIR)/init_server.elf +UART_DRIVER := $(BUILD_DIR)/uart_driver.elf +PROCMGR := $(BUILD_DIR)/procmgr.elf + +.PHONY: all kernel libc hal servers iso run clean help + +all: kernel libc hal servers + @mkdir -p $(BUILD_DIR) + @cp $(KERNEL_BIN) $(KERNEL_ELF) + @echo "=======================================================" + @echo " [SUCCESS] opencoreC Skeleton Build Complete!" + @echo " Kernel: $(KERNEL_ELF)" + @echo " Servers: $(INIT_SERVER), $(UART_DRIVER), $(PROCMGR)" + @echo "=======================================================" + +kernel: + @echo "[CARGO] Compiling Rust Microkernel (x86_64-unknown-none)..." + @RUSTFLAGS="-C link-arg=-T$(CONFIG_DIR)/linker.ld -C code-model=kernel -C relocation-model=static" \ + $(CARGO) build --package opencore_kernel --target x86_64-unknown-none --release + +libc: + @mkdir -p $(BUILD_DIR) + @echo "[CC] Compiling bare-metal freestanding libc..." + @$(CC) $(CFLAGS) -c $(LIB_DIR)/libc/src/string.c -o $(BUILD_DIR)/string.o + @$(CC) $(CFLAGS) -c $(LIB_DIR)/libc/src/syscalls.c -o $(BUILD_DIR)/syscalls.o + +hal: libc + @mkdir -p $(BUILD_DIR) + @echo "[CC] Compiling HAL and Hardware Drivers..." + @$(CC) $(CFLAGS) -I$(HAL_DIR)/include -c $(HAL_DIR)/src/uart.c -o $(BUILD_DIR)/uart.o + @$(CC) $(CFLAGS) -I$(HAL_DIR)/include -c $(HAL_DIR)/src/pci.c -o $(BUILD_DIR)/pci.o + @$(CC) $(CFLAGS) -I$(HAL_DIR)/include -c $(HAL_DIR)/src/apic.c -o $(BUILD_DIR)/apic.o + +servers: libc hal + @mkdir -p $(BUILD_DIR) + @echo "[CXX/CC] Linking Ring 3 User-space Servers..." + @# Init Root Server + @$(CXX) $(CXXFLAGS) -c $(SERVERS_DIR)/init/main.cpp -o $(BUILD_DIR)/init_main.o + @$(LD) -T $(CONFIG_DIR)/user.ld -nostdlib -static -o $(INIT_SERVER) \ + $(BUILD_DIR)/init_main.o $(BUILD_DIR)/syscalls.o $(BUILD_DIR)/string.o + @# Standalone UART Driver Server + @$(CC) $(CFLAGS) -I$(HAL_DIR)/include -c $(SERVERS_DIR)/uart_driver/main.c -o $(BUILD_DIR)/uart_driver_main.o + @$(LD) -T $(CONFIG_DIR)/user.ld -nostdlib -static -o $(UART_DRIVER) \ + $(BUILD_DIR)/uart_driver_main.o $(BUILD_DIR)/uart.o $(BUILD_DIR)/syscalls.o $(BUILD_DIR)/string.o + @# Process Manager Daemon + @$(CXX) $(CXXFLAGS) -c $(SERVERS_DIR)/procmgr/main.cpp -o $(BUILD_DIR)/procmgr_main.o + @$(LD) -T $(CONFIG_DIR)/user.ld -nostdlib -static -o $(PROCMGR) \ + $(BUILD_DIR)/procmgr_main.o $(BUILD_DIR)/syscalls.o $(BUILD_DIR)/string.o + +# Staging Limine Boot ISO directory +iso: all + @mkdir -p $(ISO_DIR)/boot $(ISO_DIR)/EFI/BOOT + @cp $(KERNEL_ELF) $(ISO_DIR)/boot/ + @cp $(INIT_SERVER) $(ISO_DIR)/boot/ + @cp $(UART_DRIVER) $(ISO_DIR)/boot/ + @cp limine.conf $(ISO_DIR)/boot/limine.conf + @cp limine.cfg $(ISO_DIR)/boot/limine.cfg + @echo "[ISO] Staged boot files in $(ISO_DIR)/" + +run: all + @echo "[QEMU] Launching opencoreC in QEMU (Serial Output to Terminal)..." + @$(QEMU) -M q35 -m 512M \ + -serial stdio \ + -display none \ + -no-reboot \ + -no-shutdown \ + -kernel $(KERNEL_ELF) + +clean: + @echo "[CLEAN] Removing build directory and cargo target..." + @rm -rf $(BUILD_DIR) target + +help: + @echo "opencoreC Microkernel Build System" + @echo "Targets:" + @echo " make all - Build kernel, HAL, libc, and user servers" + @echo " make kernel - Build Rust microkernel only" + @echo " make hal - Build freestanding HAL drivers" + @echo " make servers - Build Ring 3 C++ servers" + @echo " make iso - Stage ISO root boot directory" + @echo " make run - Launch QEMU with serial stdio" + @echo " make clean - Clean build artifacts" diff --git a/README.md b/README.md new file mode 100644 index 0000000..24ba563 --- /dev/null +++ b/README.md @@ -0,0 +1,149 @@ +# opencoreC: High-Performance x86_64 Microkernel OS + +[![Architecture](https://img.shields.io/badge/Arch-x86__64%20(Long%20Mode)-blue.svg)]() +[![Kernel](https://img.shields.io/badge/Kernel-Rust%20%23!%5Bno__std%5D-orange.svg)]() +[![HAL](https://img.shields.io/badge/HAL%20%2F%20Drivers-C11%20Freestanding-green.svg)]() +[![Services](https://img.shields.io/badge/Services-C%2B%2B20%20RAII-purple.svg)]() +[![Bootloader](https://img.shields.io/badge/Boot-Limine%20v3-brightgreen.svg)]() + +**opencoreC** is a modern, modular, capability-based microkernel operating system written in **Rust (`#![no_std]`)**, **Freestanding C**, and **Modern C++20**. It is designed around principles of strict hardware isolation, capability security, and zero-copy / fast-path inter-process communication (IPC). + +--- + +## πŸ›οΈ Architecture Overview + +The system follows a pure microkernel design paradigm where only the essential primitives reside in **Ring 0**, while device drivers, file systems, and system servers operate in isolated **Ring 3** user spaces. + +```mermaid +graph TD + subgraph Ring 3 [User Space - Ring 3] + INIT["init_server (C++20 Root Server)"] + PROCMGR["procmgr (Process & Server Manager)"] + UART_DRV["uart_driver (16550 COM1 Driver)"] + FS["VFS / Storage Server (Future)"] + end + + subgraph Ring 0 [Microkernel - Ring 0] + IPC["Fast-Path IPC Dispatcher (Registers + SHM)"] + CAP["Capability Table & CNode Tracker"] + VMM["Virtual Memory Manager (PML4 4-Level Paging)"] + PFA["Physical Frame Allocator (Bitmap Allocator)"] + SCHED["Thread Scheduler & Context Switch (ASM)"] + end + + INIT -- "Fast-Path IPC (Syscall)" --> IPC + PROCMGR -- "Fast-Path IPC (Syscall)" --> IPC + UART_DRV -- "Fast-Path IPC (Syscall)" --> IPC + IPC --> CAP + IPC --> SCHED +``` + +### πŸ”€ Language & Subsystem Distribution + +| Component | Language | Privilege | Responsibilities | +| :--- | :--- | :--- | :--- | +| **Microkernel Core** | **Rust** (`#![no_std]`) | Ring 0 | PFA (Physical Memory), VMM (Paging), Capabilities, Fast IPC dispatcher, Scheduler. | +| **Low-Level Glue** | **x86_64 ASM** | Ring 0 / 3 | `_start`, `switch_to` context switch, `syscall_entry` / `sysretq`, IDT vectors. | +| **HAL & Hardware** | **C** (`-ffreestanding`) | Ring 3 / Ring 0 | Minimal bare-metal libc, 16550 UART COM driver, PCI bus scanning, APIC/IOAPIC. | +| **User Services** | **C++20** (`-nostdlib`) | Ring 3 | System servers, process management, RAII IPC endpoints (`ipc::Endpoint`, `ipc::SharedBuffer`). | + +--- + +## πŸ“ Repository Structure + +```text +opencoreC/ +β”œβ”€β”€ Cargo.toml # Root Rust workspace manifest +β”œβ”€β”€ rust-toolchain.toml # Fixed toolchain (x86_64-unknown-none) +β”œβ”€β”€ Makefile # Unified root build system +β”œβ”€β”€ CMakeLists.txt # Alternative CMake build for C/C++ components +β”œβ”€β”€ limine.conf # Limine bootloader configuration +β”œβ”€β”€ config/ +β”‚ β”œβ”€β”€ linker.ld # Higher-half kernel linker script (0xffffffff80000000) +β”‚ └── user.ld # Userspace ELF linker script (0x400000) +β”œβ”€β”€ include/ +β”‚ └── abi/ +β”‚ β”œβ”€β”€ types.h # ABI primitive types (cap_t, sysret_t, vaddr_t) +β”‚ β”œβ”€β”€ syscalls.h # Syscall numbers and inline assembly wrappers +β”‚ └── ipc.h # Fast-Path IPC register layout and SHM headers +β”œβ”€β”€ kernel/ # Microkernel crate (Rust) +β”‚ β”œβ”€β”€ Cargo.toml +β”‚ β”œβ”€β”€ asm/ +β”‚ β”‚ β”œβ”€β”€ context.S # Cooperative/Preemptive context switcher +β”‚ β”‚ └── syscall_entry.S # x86_64 fast syscall trampoline +β”‚ └── src/ +β”‚ β”œβ”€β”€ main.rs # Kernel entry point & self-tests +β”‚ β”œβ”€β”€ limine_requests.rs # Limine protocol responses (HHDM, Memmap) +β”‚ β”œβ”€β”€ arch/ # GDT, TSS, IDT, Syscall MSRs +β”‚ β”œβ”€β”€ mm/ # Physical Frame Allocator & 4-Level Paging +β”‚ β”œβ”€β”€ ipc/ # Register-based Fast-Path IPC +β”‚ └── sched/ # Thread control blocks & scheduler +β”œβ”€β”€ lib/ +β”‚ β”œβ”€β”€ libc/ # Minimal freestanding C library +β”‚ └── libipc_cpp/ # Modern C++ RAII IPC client library +β”œβ”€β”€ hal/ # Hardware Abstraction Layer (UART, PCI, APIC) +└── servers/ # Ring 3 User-space servers (init, procmgr, uart_driver) +``` + +--- + +## ⚑ Fast-Path IPC Specification + +Short messages (up to 32 bytes of arguments) are passed directly through CPU registers without memory allocations or deep kernel stack copies: + +* **`RAX`**: Syscall Number (`SYS_IPC_CALL = 1`, `SYS_IPC_REPLY_RECV = 2`, etc.) +* **`RDI`**: Target Capability Handle (`cap_t dest_cap`) +* **`RSI`**: IPC Opcode / Interface Method ID +* **`RDX`**: Argument 0 (`uint64_t`) +* **`R8`** : Argument 1 (`uint64_t`) +* **`R9`** : Argument 2 (`uint64_t`) +* **`R10`**: Argument 3 / SHM token (`uint64_t`) +* **Return Registers**: `RAX` (Status code), `RDX` (Result 0), `R8` (Result 1) + +--- + +## πŸš€ Building & Running + +### Prerequisites + +* `rustc` & `cargo` (with target `x86_64-unknown-none`) +* `gcc` & `g++` (supporting C11 and C++20) +* `qemu-system-x86_64` + +### Quick Start + +1. **Build all components:** + ```bash + make all + ``` + +2. **Run in QEMU with Serial Console Output:** + ```bash + make run + ``` + +3. **Clean build artifacts:** + ```bash + make clean + ``` + +--- + +## πŸ—ΊοΈ Roadmap + +- [x] Higher-Half Limine bootloader protocol initialization (HHDM). +- [x] Physical Frame Allocator (PFA) via bitmap. +- [x] 4-Level Paging (VMM) with dynamic page table allocation. +- [x] Fast-Path register IPC infrastructure (`syscall`/`sysretq`). +- [x] Freestanding bare-metal libc and 16550 UART driver. +- [x] Modern C++ RAII IPC wrappers and initial userspace servers. +- [ ] **Phase 1:** ELF Loader, User Address Space isolation, and `iretq` jump to Ring 3. +- [ ] **Phase 2:** Synchronous IPC Rendezvous (blocking call / reply loop). +- [ ] **Phase 3:** Preemptive Multi-Tasking via LAPIC Timer interrupts. +- [ ] **Phase 4:** Userspace device driver model & interrupt forwarding. + +--- + +## πŸ“œ License + +Distributed under the MIT / Apache-2.0 License. diff --git a/config/linker.ld b/config/linker.ld new file mode 100644 index 0000000..3927ab1 --- /dev/null +++ b/config/linker.ld @@ -0,0 +1,51 @@ +/* opencoreC - Higher-Half x86_64 Kernel Linker Script */ +OUTPUT_FORMAT(elf64-x86-64) +OUTPUT_ARCH(i386:x86-64) +ENTRY(_start) + +PHDRS +{ + text PT_LOAD FLAGS((1 << 0) | (1 << 2)); /* Execute | Read */ + rodata PT_LOAD FLAGS((1 << 2)); /* Read Only */ + data PT_LOAD FLAGS((1 << 1) | (1 << 2)); /* Write | Read */ +} + +SECTIONS +{ + /* Higher-half virtual base: 0xffffffff80000000 (-2 GiB) */ + . = 0xffffffff80000000; + + .text : { + *(.text .text.*) + } :text + + . = ALIGN(CONSTANT(MAXPAGESIZE)); + + .rodata : { + *(.rodata .rodata.*) + } :rodata + + /* Limine requests section */ + .limine_requests : { + KEEP(*(.limine_requests_start)) + KEEP(*(.limine_requests)) + KEEP(*(.limine_requests_end)) + } :rodata + + . = ALIGN(CONSTANT(MAXPAGESIZE)); + + .data : { + *(.data .data.*) + } :data + + .bss : { + *(.bss .bss.*) + *(COMMON) + } :data + + /DISCARD/ : { + *(.eh_frame) + *(.note .note.*) + *(.comment) + } +} diff --git a/config/user.ld b/config/user.ld new file mode 100644 index 0000000..84a0dd6 --- /dev/null +++ b/config/user.ld @@ -0,0 +1,36 @@ +/* opencoreC - Userspace Program Linker Script */ +OUTPUT_FORMAT(elf64-x86-64) +OUTPUT_ARCH(i386:x86-64) +ENTRY(_start) + +SECTIONS +{ + . = 0x400000; + + .text : { + *(.text .text.*) + } + + . = ALIGN(0x1000); + + .rodata : { + *(.rodata .rodata.*) + } + + . = ALIGN(0x1000); + + .data : { + *(.data .data.*) + } + + .bss : { + *(.bss .bss.*) + *(COMMON) + } + + /DISCARD/ : { + *(.eh_frame) + *(.note .note.*) + *(.comment) + } +} diff --git a/hal/include/apic.h b/hal/include/apic.h new file mode 100644 index 0000000..4c5ec82 --- /dev/null +++ b/hal/include/apic.h @@ -0,0 +1,29 @@ +#pragma once + +#include "stdint.h" + +#define LAPIC_ID_REG 0x020 +#define LAPIC_VERSION_REG 0x030 +#define LAPIC_TPR_REG 0x080 +#define LAPIC_EOI_REG 0x0B0 +#define LAPIC_SVR_REG 0x0F0 +#define LAPIC_ICR_LOW_REG 0x300 +#define LAPIC_ICR_HIGH_REG 0x310 +#define LAPIC_LVT_TIMER_REG 0x320 +#define LAPIC_TIMER_INIT_REG 0x380 +#define LAPIC_TIMER_CURR_REG 0x390 +#define LAPIC_TIMER_DIV_REG 0x3E0 + +#ifdef __cplusplus +extern "C" { +#endif + +void lapic_init(uintptr_t lapic_base_vaddr); +void lapic_eoi(void); +uint32_t lapic_read(uint32_t reg); +void lapic_write(uint32_t reg, uint32_t value); +uint32_t lapic_get_id(void); + +#ifdef __cplusplus +} +#endif diff --git a/hal/include/pci.h b/hal/include/pci.h new file mode 100644 index 0000000..1137a38 --- /dev/null +++ b/hal/include/pci.h @@ -0,0 +1,34 @@ +#pragma once + +#include "stdint.h" + +#define PCI_CONFIG_ADDRESS 0xCF8 +#define PCI_CONFIG_DATA 0xCFC + +typedef struct pci_device { + uint8_t bus; + uint8_t slot; + uint8_t func; + uint16_t vendor_id; + uint16_t device_id; + uint8_t class_code; + uint8_t subclass; + uint8_t prog_if; + uint8_t header_type; +} pci_device_t; + +#ifdef __cplusplus +extern "C" { +#endif + +uint32_t pci_read_config_dword(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset); +void pci_write_config_dword(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset, uint32_t val); +uint16_t pci_get_vendor_id(uint8_t bus, uint8_t slot, uint8_t func); +uint16_t pci_get_device_id(uint8_t bus, uint8_t slot, uint8_t func); + +typedef void (*pci_scan_callback_t)(const pci_device_t *dev); +void pci_scan_bus(pci_scan_callback_t callback); + +#ifdef __cplusplus +} +#endif diff --git a/hal/include/uart.h b/hal/include/uart.h new file mode 100644 index 0000000..57d1cae --- /dev/null +++ b/hal/include/uart.h @@ -0,0 +1,36 @@ +#pragma once + +#include "stdint.h" +#include "stddef.h" + +#define UART_COM1_PORT 0x3F8 +#define UART_COM2_PORT 0x2F8 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initialize 16550 UART port with target baud rate */ +int uart_init(uint16_t port, uint32_t baud); + +/* Check if transmit buffer is empty */ +int uart_is_tx_empty(uint16_t port); + +/* Transmit a single character */ +void uart_putc(uint16_t port, char c); + +/* Transmit a null-terminated string */ +void uart_puts(uint16_t port, const char *s); + +/* Transmit a buffer of given length */ +void uart_write(uint16_t port, const char *buf, size_t len); + +/* Check if data received */ +int uart_has_data(uint16_t port); + +/* Read a single character (blocking) */ +char uart_getc(uint16_t port); + +#ifdef __cplusplus +} +#endif diff --git a/hal/src/apic.c b/hal/src/apic.c new file mode 100644 index 0000000..303618d --- /dev/null +++ b/hal/src/apic.c @@ -0,0 +1,31 @@ +#include "../include/apic.h" + +static volatile uint32_t *lapic_base = (volatile uint32_t *)0; + +uint32_t lapic_read(uint32_t reg) { + if (!lapic_base) return 0; + return *(volatile uint32_t *)((uintptr_t)lapic_base + reg); +} + +void lapic_write(uint32_t reg, uint32_t value) { + if (!lapic_base) return; + *(volatile uint32_t *)((uintptr_t)lapic_base + reg) = value; +} + +void lapic_init(uintptr_t lapic_base_vaddr) { + lapic_base = (volatile uint32_t *)lapic_base_vaddr; + + // Enable LAPIC via Spurious Interrupt Vector Register (SVR) + Vector 0xFF + lapic_write(LAPIC_SVR_REG, lapic_read(LAPIC_SVR_REG) | 0x1FF); + + // Set Task Priority Register to 0 (accept all interrupts) + lapic_write(LAPIC_TPR_REG, 0x00); +} + +void lapic_eoi(void) { + lapic_write(LAPIC_EOI_REG, 0x00); +} + +uint32_t lapic_get_id(void) { + return (lapic_read(LAPIC_ID_REG) >> 24) & 0xFF; +} diff --git a/hal/src/pci.c b/hal/src/pci.c new file mode 100644 index 0000000..d3820c7 --- /dev/null +++ b/hal/src/pci.c @@ -0,0 +1,79 @@ +#include "../include/pci.h" + +static inline void outl(uint16_t port, uint32_t val) { + __asm__ volatile("outl %0, %1" : : "a"(val), "Nd"(port)); +} + +static inline uint32_t inl(uint16_t port) { + uint32_t ret; + __asm__ volatile("inl %1, %0" : "=a"(ret) : "Nd"(port)); + return ret; +} + +uint32_t pci_read_config_dword(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) { + uint32_t address = (uint32_t)((((uint32_t)bus) << 16) | + (((uint32_t)slot) << 11) | + (((uint32_t)func) << 8) | + (offset & 0xFC) | + ((uint32_t)0x80000000)); + outl(PCI_CONFIG_ADDRESS, address); + return inl(PCI_CONFIG_DATA); +} + +void pci_write_config_dword(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset, uint32_t val) { + uint32_t address = (uint32_t)((((uint32_t)bus) << 16) | + (((uint32_t)slot) << 11) | + (((uint32_t)func) << 8) | + (offset & 0xFC) | + ((uint32_t)0x80000000)); + outl(PCI_CONFIG_ADDRESS, address); + outl(PCI_CONFIG_DATA, val); +} + +uint16_t pci_get_vendor_id(uint8_t bus, uint8_t slot, uint8_t func) { + uint32_t dword = pci_read_config_dword(bus, slot, func, 0x00); + return (uint16_t)(dword & 0xFFFF); +} + +uint16_t pci_get_device_id(uint8_t bus, uint8_t slot, uint8_t func) { + uint32_t dword = pci_read_config_dword(bus, slot, func, 0x00); + return (uint16_t)((dword >> 16) & 0xFFFF); +} + +void pci_scan_bus(pci_scan_callback_t callback) { + for (uint16_t bus = 0; bus < 256; bus++) { + for (uint8_t slot = 0; slot < 32; slot++) { + uint16_t vendor = pci_get_vendor_id((uint8_t)bus, slot, 0); + if (vendor == 0xFFFF || vendor == 0x0000) { + continue; + } + + uint32_t hdr_dword = pci_read_config_dword((uint8_t)bus, slot, 0, 0x0C); + uint8_t header_type = (uint8_t)((hdr_dword >> 16) & 0xFF); + uint8_t num_funcs = (header_type & 0x80) ? 8 : 1; + + for (uint8_t func = 0; func < num_funcs; func++) { + uint16_t func_vendor = pci_get_vendor_id((uint8_t)bus, slot, func); + if (func_vendor == 0xFFFF || func_vendor == 0x0000) { + continue; + } + + uint32_t class_dword = pci_read_config_dword((uint8_t)bus, slot, func, 0x08); + pci_device_t dev; + dev.bus = (uint8_t)bus; + dev.slot = slot; + dev.func = func; + dev.vendor_id = func_vendor; + dev.device_id = pci_get_device_id((uint8_t)bus, slot, func); + dev.class_code = (uint8_t)((class_dword >> 24) & 0xFF); + dev.subclass = (uint8_t)((class_dword >> 16) & 0xFF); + dev.prog_if = (uint8_t)((class_dword >> 8) & 0xFF); + dev.header_type = header_type; + + if (callback) { + callback(&dev); + } + } + } + } +} diff --git a/hal/src/uart.c b/hal/src/uart.c new file mode 100644 index 0000000..2af547b --- /dev/null +++ b/hal/src/uart.c @@ -0,0 +1,98 @@ +#include "../include/uart.h" + +/* Port I/O Helpers */ +static inline void outb(uint16_t port, uint8_t val) { + __asm__ volatile("outb %0, %1" : : "a"(val), "Nd"(port)); +} + +static inline uint8_t inb(uint16_t port) { + uint8_t ret; + __asm__ volatile("inb %1, %0" : "=a"(ret) : "Nd"(port)); + return ret; +} + +/* 16550 Register Offsets */ +#define UART_DATA 0 +#define UART_IER 1 +#define UART_BAUD_LO 0 +#define UART_BAUD_HI 1 +#define UART_FCR 2 +#define UART_LCR 3 +#define UART_MCR 4 +#define UART_LSR 5 + +int uart_init(uint16_t port, uint32_t baud) { + if (baud == 0) baud = 115200; + uint16_t divisor = (uint16_t)(115200 / baud); + if (divisor == 0) divisor = 1; + + // 1. Disable all interrupts + outb(port + UART_IER, 0x00); + + // 2. Enable DLAB (Divisor Latch Access Bit) + outb(port + UART_LCR, 0x80); + + // 3. Set divisor (baud rate) + outb(port + UART_BAUD_LO, (uint8_t)(divisor & 0xFF)); + outb(port + UART_BAUD_HI, (uint8_t)((divisor >> 8) & 0xFF)); + + // 4. Set 8 bits, no parity, 1 stop bit (8N1) + outb(port + UART_LCR, 0x03); + + // 5. Enable FIFO, clear TX/RX, 14-byte threshold + outb(port + UART_FCR, 0xC7); + + // 6. Turn on DTR, RTS, and OUT2 (enables interrupts) + outb(port + UART_MCR, 0x0B); + + // 7. Test loopback + outb(port + UART_MCR, 0x1E); + outb(port + UART_DATA, 0xAE); + if (inb(port + UART_DATA) != 0xAE) { + return -1; // Faulty serial chip + } + + // 8. Normal operation mode (not loopback) + outb(port + UART_MCR, 0x0F); + return 0; +} + +int uart_is_tx_empty(uint16_t port) { + return (inb(port + UART_LSR) & 0x20); +} + +void uart_putc(uint16_t port, char c) { + while (!uart_is_tx_empty(port)) { + __asm__ volatile("pause"); + } + outb(port + UART_DATA, (uint8_t)c); +} + +void uart_puts(uint16_t port, const char *s) { + while (*s) { + if (*s == '\n') { + uart_putc(port, '\r'); + } + uart_putc(port, *s++); + } +} + +void uart_write(uint16_t port, const char *buf, size_t len) { + for (size_t i = 0; i < len; i++) { + if (buf[i] == '\n') { + uart_putc(port, '\r'); + } + uart_putc(port, buf[i]); + } +} + +int uart_has_data(uint16_t port) { + return (inb(port + UART_LSR) & 0x01); +} + +char uart_getc(uint16_t port) { + while (!uart_has_data(port)) { + __asm__ volatile("pause"); + } + return (char)inb(port + UART_DATA); +} diff --git a/include/abi/ipc.h b/include/abi/ipc.h new file mode 100644 index 0000000..e79612c --- /dev/null +++ b/include/abi/ipc.h @@ -0,0 +1,49 @@ +#pragma once + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* IPC Protocol Flags */ +#define IPC_FLAG_FAST_REGISTERS (1 << 0) /* Message fits entirely in registers */ +#define IPC_FLAG_SHM_ATTACHED (1 << 1) /* Attached shared memory buffer */ +#define IPC_FLAG_CAP_TRANSFER (1 << 2) /* Delegating capability in message */ +#define IPC_FLAG_NONBLOCKING (1 << 3) /* Return immediately if receiver not ready */ + +/* Shared Memory Descriptor for Zero-Copy Transfers */ +typedef struct ipc_shm_desc { + cap_t shm_cap; /* Capability to shared memory region */ + uint64_t offset; /* Offset within SHM buffer */ + uint64_t length; /* Length of data payload */ + uint32_t permissions; /* Read/Write flags */ + uint32_t _reserved; +} __attribute__((packed)) ipc_shm_desc_t; + +/* IPC Message Payload Representation */ +typedef struct ipc_msg { + uint64_t badge; /* Sender authentication badge (injected by kernel) */ + uint32_t opcode; /* Interface Method / Function ID */ + uint32_t flags; /* IPC_FLAG_* */ + + /* Fast-Path Register Payload (4 x 64-bit = 32 bytes directly passed) */ + uint64_t args[4]; + + /* Zero-Copy Extended Descriptor */ + ipc_shm_desc_t shm; +} __attribute__((aligned(16))) ipc_msg_t; + +/* Standard Common IPC Opcodes */ +#define IPC_OP_PING 0x0001 +#define IPC_OP_PONG 0x0002 +#define IPC_OP_REGISTER_SERVER 0x0010 +#define IPC_OP_LOOKUP_SERVER 0x0011 +#define IPC_OP_NOTIFY_EVENT 0x0020 +#define IPC_OP_READ 0x0100 +#define IPC_OP_WRITE 0x0101 +#define IPC_OP_IOCTL 0x0102 + +#ifdef __cplusplus +} +#endif diff --git a/include/abi/syscalls.h b/include/abi/syscalls.h new file mode 100644 index 0000000..aa7ea5d --- /dev/null +++ b/include/abi/syscalls.h @@ -0,0 +1,73 @@ +#pragma once + +#include "types.h" + +/* Syscall Numbers (passed in RAX) */ +#define SYS_IPC_CALL 1 /* Synchronous call: send message & wait for reply */ +#define SYS_IPC_REPLY_RECV 2 /* Server fast-path: reply to current and wait next */ +#define SYS_IPC_SEND 3 /* Asynchronous/non-blocking send */ +#define SYS_IPC_RECV 4 /* Block waiting for incoming message */ +#define SYS_MEM_MAP 5 /* Map memory / shared memory region */ +#define SYS_CAP_GRANT 6 /* Delegate capability to CNode */ +#define SYS_THREAD_YIELD 7 /* Yield CPU slice */ +#define SYS_THREAD_EXIT 8 /* Terminate current thread */ +#define SYS_LOG_DEBUG 9 /* Direct Ring 0 debug print */ + +#ifndef __ASSEMBLER__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Low-level inline syscall invocations adhering to System V AMD64 ABI + Fast-Path */ + +static inline sysret_t sys_ipc_call_raw(cap_t dest_cap, uint64_t msg_code, + uint64_t arg0, uint64_t arg1, + uint64_t arg2, uint64_t arg3, + uint64_t *out0, uint64_t *out1) { + register uint64_t rax __asm__("rax") = SYS_IPC_CALL; + register uint64_t rdi __asm__("rdi") = dest_cap; + register uint64_t rsi __asm__("rsi") = msg_code; + register uint64_t rdx __asm__("rdx") = arg0; + register uint64_t r8 __asm__("r8") = arg1; + register uint64_t r9 __asm__("r9") = arg2; + register uint64_t r10 __asm__("r10") = arg3; + + __asm__ volatile( + "syscall" + : "+r"(rax), "+r"(rdx), "+r"(r8) + : "r"(rdi), "r"(rsi), "r"(r9), "r"(r10) + : "rcx", "r11", "memory" + ); + + if (out0) *out0 = rdx; + if (out1) *out1 = r8; + return (sysret_t)rax; +} + +static inline sysret_t sys_yield(void) { + register uint64_t rax __asm__("rax") = SYS_THREAD_YIELD; + __asm__ volatile("syscall" : "+r"(rax) : : "rcx", "r11", "memory"); + return (sysret_t)rax; +} + +static inline void sys_exit(int code) { + register uint64_t rax __asm__("rax") = SYS_THREAD_EXIT; + register uint64_t rdi __asm__("rdi") = (uint64_t)code; + __asm__ volatile("syscall" : "+r"(rax) : "r"(rdi) : "rcx", "r11", "memory"); + __builtin_unreachable(); +} + +static inline sysret_t sys_log_debug(const char *msg, size_t len) { + register uint64_t rax __asm__("rax") = SYS_LOG_DEBUG; + register uint64_t rdi __asm__("rdi") = (uint64_t)msg; + register uint64_t rsi __asm__("rsi") = (uint64_t)len; + __asm__ volatile("syscall" : "+r"(rax) : "r"(rdi), "r"(rsi) : "rcx", "r11", "memory"); + return (sysret_t)rax; +} + +#ifdef __cplusplus +} +#endif + +#endif /* __ASSEMBLER__ */ diff --git a/include/abi/types.h b/include/abi/types.h new file mode 100644 index 0000000..322cf1b --- /dev/null +++ b/include/abi/types.h @@ -0,0 +1,38 @@ +#pragma once + +#ifndef __ASSEMBLER__ +#include +#include +#include +#else +#define UINT64_C(c) c +#endif + +/* Standard Type Definitions */ +typedef uint64_t cap_t; /* Capability handle */ +typedef uint64_t vaddr_t; /* Virtual Address */ +typedef uint64_t paddr_t; /* Physical Address */ +typedef int64_t sysret_t; /* Syscall Return Code */ +typedef uint32_t pid_t; /* Process ID */ +typedef uint32_t tid_t; /* Thread ID */ + +/* Standard Return / Error Codes */ +#define SYS_OK 0 +#define SYS_ERR_INVALID_ARG -1 +#define SYS_ERR_INVALID_CAP -2 +#define SYS_ERR_NO_MEMORY -3 +#define SYS_ERR_IPC_TIMEOUT -4 +#define SYS_ERR_IPC_DEADLOCK -5 +#define SYS_ERR_PERMISSION -6 +#define SYS_ERR_NOT_FOUND -7 +#define SYS_ERR_ALREADY_EXISTS -8 +#define SYS_ERR_BUSY -9 + +/* Standard Special Capabilities */ +#define CAP_NULL 0 +#define CAP_SELF_TASK 1 +#define CAP_SELF_THREAD 2 +#define CAP_SELF_VMSPACE 3 +#define CAP_ROOT_CSPACE 4 +#define CAP_ROOT_IRQ 5 +#define CAP_ROOT_IO 6 diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml new file mode 100644 index 0000000..87cd560 --- /dev/null +++ b/kernel/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "opencore_kernel" +version = "0.1.0" +edition = "2021" + +[[bin]] +name = "opencore_kernel" +path = "src/main.rs" +test = false +bench = false diff --git a/kernel/asm/context.S b/kernel/asm/context.S new file mode 100644 index 0000000..cc37547 --- /dev/null +++ b/kernel/asm/context.S @@ -0,0 +1,33 @@ +/* opencoreC - Thread Context Switch */ +.global switch_to + +.section .text +/* + * void switch_to(uint64_t **prev_rsp_ptr, uint64_t *next_rsp); + * RDI = pointer to current thread's stored RSP + * RSI = target thread's RSP + */ +switch_to: + /* Push callee-saved registers of outgoing thread */ + push rbp + push rbx + push r12 + push r13 + push r14 + push r15 + + /* Save current stack pointer */ + mov [rdi], rsp + + /* Load target stack pointer */ + mov rsp, rsi + + /* Pop callee-saved registers of incoming thread */ + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + pop rbp + + ret diff --git a/kernel/asm/syscall_entry.S b/kernel/asm/syscall_entry.S new file mode 100644 index 0000000..b19d067 --- /dev/null +++ b/kernel/asm/syscall_entry.S @@ -0,0 +1,53 @@ +/* opencoreC - Fast Syscall Entry Trampoline (x86_64) */ +.global syscall_entry +.extern kernel_syscall_dispatcher + +.section .bss +.align 16 +user_scratch_rsp: + .quad 0 +kernel_temp_stack: + .skip 4096 +kernel_temp_stack_top: + +.section .text +syscall_entry: + /* Save user stack pointer */ + mov [rip + user_scratch_rsp], rsp + lea rsp, [rip + kernel_temp_stack_top] + + /* Save user RIP (in RCX) and user RFLAGS (in R11) */ + push rcx + push r11 + + /* Save argument and volatile registers to construct SyscallRegisters */ + push rax + push rdi + push rsi + push rdx + push r8 + push r9 + push r10 + + /* Pass pointer to SyscallRegisters struct in RDI */ + mov rdi, rsp + call kernel_syscall_dispatcher + + /* Restore registers (rax, rdx, r8 may have return values from dispatcher) */ + pop r10 + pop r9 + pop r8 + pop rdx + pop rsi + pop rdi + pop rax + + /* Restore user RIP and RFLAGS */ + pop r11 + pop rcx + + /* Restore user stack */ + mov rsp, [rip + user_scratch_rsp] + + /* Return to Ring 3 */ + sysretq diff --git a/kernel/src/arch/gdt.rs b/kernel/src/arch/gdt.rs new file mode 100644 index 0000000..14cbc9e --- /dev/null +++ b/kernel/src/arch/gdt.rs @@ -0,0 +1,102 @@ +//! Global Descriptor Table (GDT) and Task State Segment (TSS) for x86_64 + +use core::mem::size_of; + +#[repr(C, packed)] +pub struct TaskStateSegment { + _reserved1: u32, + pub rsp0: u64, + pub rsp1: u64, + pub rsp2: u64, + _reserved2: u64, + pub ist: [u64; 7], + _reserved3: u64, + _reserved4: u16, + pub iomap_base: u16, +} + +impl TaskStateSegment { + pub const fn new() -> Self { + Self { + _reserved1: 0, + rsp0: 0, + rsp1: 0, + rsp2: 0, + _reserved2: 0, + ist: [0; 7], + _reserved3: 0, + _reserved4: 0, + iomap_base: size_of::() as u16, + } + } +} + +#[repr(C, packed)] +struct GdtDescriptor { + limit: u16, + base: u64, +} + +pub static mut TSS: TaskStateSegment = TaskStateSegment::new(); + +// 8 entries: Null, KCode, KData, UData, UCode, TSS Low, TSS High, Null +static mut GDT: [u64; 8] = [ + 0x0000000000000000, // 0x00: Null + 0x00af9a000000ffff, // 0x08: Kernel Code 64 (Ring 0) + 0x00cf92000000ffff, // 0x10: Kernel Data 64 (Ring 0) + 0x00cff2000000ffff, // 0x18: User Data 64 (Ring 3) + 0x00affa000000ffff, // 0x20: User Code 64 (Ring 3) + 0x0000000000000000, // 0x28: TSS Low (populated at init) + 0x0000000000000000, // 0x30: TSS High (populated at init) + 0x0000000000000000, // 0x38: Alignment / padding +]; + +pub unsafe fn init() { + let tss_addr = core::ptr::addr_of!(TSS) as u64; + let tss_size = (size_of::() - 1) as u64; + + // Build 64-bit TSS descriptor + let tss_low = (tss_size & 0xffff) + | ((tss_addr & 0xffff) << 16) + | (((tss_addr >> 16) & 0xff) << 32) + | (0x89u64 << 40) // Present, 64-bit TSS (Available) + | (((tss_size >> 16) & 0xf) << 48) + | (((tss_addr >> 24) & 0xff) << 56); + let tss_high = tss_addr >> 32; + + let gdt_ptr = core::ptr::addr_of_mut!(GDT) as *mut u64; + *gdt_ptr.add(5) = tss_low; + *gdt_ptr.add(6) = tss_high; + + let descriptor = GdtDescriptor { + limit: (size_of::<[u64; 8]>() - 1) as u16, + base: gdt_ptr as u64, + }; + + core::arch::asm!( + "lgdt [{0}]", + "push 0x08", + "lea {tmp}, [2f + rip]", + "push {tmp}", + "retfq", + "2:", + "mov ax, 0x10", + "mov ds, ax", + "mov es, ax", + "mov ss, ax", + "mov fs, ax", + "mov gs, ax", + "ltr {tss_sel:x}", + in(reg) &descriptor, + tmp = out(reg) _, + tss_sel = in(reg) (0x28u16), + options(preserves_flags) + ); +} + +pub fn set_kernel_stack(stack_top: u64) { + unsafe { + let tss_ptr = core::ptr::addr_of_mut!(TSS); + (*tss_ptr).rsp0 = stack_top; + } +} diff --git a/kernel/src/arch/idt.rs b/kernel/src/arch/idt.rs new file mode 100644 index 0000000..ad4405c --- /dev/null +++ b/kernel/src/arch/idt.rs @@ -0,0 +1,99 @@ +//! Interrupt Descriptor Table (IDT) and Exception Handling + +use crate::kprintln; +use core::mem::size_of; + +#[repr(C, packed)] +#[derive(Clone, Copy)] +pub struct IdtEntry { + offset_low: u16, + selector: u16, + ist: u8, + type_attr: u8, + offset_mid: u16, + offset_high: u32, + _reserved: u32, +} + +impl IdtEntry { + pub const fn missing() -> Self { + Self { + offset_low: 0, + selector: 0, + ist: 0, + type_attr: 0, + offset_mid: 0, + offset_high: 0, + _reserved: 0, + } + } + + pub fn set_handler(&mut self, handler: u64, ist: u8, dpl: u8) { + self.offset_low = (handler & 0xffff) as u16; + self.selector = 0x08; // Kernel code segment + self.ist = ist & 0x7; + self.type_attr = 0x80 | ((dpl & 0x3) << 5) | 0x0E; // 64-bit Interrupt Gate Present + self.offset_mid = ((handler >> 16) & 0xffff) as u16; + self.offset_high = ((handler >> 32) & 0xffffffff) as u32; + self._reserved = 0; + } +} + +#[repr(C, packed)] +struct IdtDescriptor { + limit: u16, + base: u64, +} + +static mut IDT: [IdtEntry; 256] = [IdtEntry::missing(); 256]; + +extern "C" { + fn default_isr_stub(); +} + +// Global assembly ISR stub for default exception handling +core::arch::global_asm!( + ".global default_isr_stub", + "default_isr_stub:", + "push rax", + "push rcx", + "push rdx", + "push rsi", + "push rdi", + "push r8", + "push r9", + "push r10", + "push r11", + "mov rdi, rsp", + "call raw_exception_handler", + "pop r11", + "pop r10", + "pop r9", + "pop r8", + "pop rdi", + "pop rsi", + "pop rdx", + "pop rcx", + "pop rax", + "iretq" +); + +pub unsafe fn init() { + let stub_addr = default_isr_stub as *const () as usize as u64; + let idt_ptr = core::ptr::addr_of_mut!(IDT) as *mut IdtEntry; + for i in 0..256 { + (*idt_ptr.add(i)).set_handler(stub_addr, 0, 0); + } + + let descriptor = IdtDescriptor { + limit: (size_of::<[IdtEntry; 256]>() - 1) as u16, + base: idt_ptr as u64, + }; + + core::arch::asm!("lidt [{0}]", in(reg) &descriptor, options(preserves_flags)); +} + +#[no_mangle] +pub extern "C" fn raw_exception_handler(stack_ptr: *const u64) { + kprintln!("[CPU EXCEPTION] Trap triggered at stack frame: {:p}", stack_ptr); +} diff --git a/kernel/src/arch/mod.rs b/kernel/src/arch/mod.rs new file mode 100644 index 0000000..0bbc818 --- /dev/null +++ b/kernel/src/arch/mod.rs @@ -0,0 +1,9 @@ +pub mod gdt; +pub mod idt; +pub mod syscall; + +pub unsafe fn init() { + gdt::init(); + idt::init(); + syscall::init(); +} diff --git a/kernel/src/arch/syscall.rs b/kernel/src/arch/syscall.rs new file mode 100644 index 0000000..8f3d111 --- /dev/null +++ b/kernel/src/arch/syscall.rs @@ -0,0 +1,113 @@ +//! x86_64 Fast Syscall / Sysret Setup and Fast-Path Dispatch + +use crate::kprintln; + +const IA32_EFER: u32 = 0xC0000080; +const IA32_STAR: u32 = 0xC0000081; +const IA32_LSTAR: u32 = 0xC0000082; +const IA32_FMASK: u32 = 0xC0000084; + +#[inline] +unsafe fn wrmsr(msr: u32, value: u64) { + let low = value as u32; + let high = (value >> 32) as u32; + core::arch::asm!( + "wrmsr", + in("ecx") msr, + in("eax") low, + in("edx") high, + options(nostack, preserves_flags) + ); +} + +#[inline] +unsafe fn rdmsr(msr: u32) -> u64 { + let low: u32; + let high: u32; + core::arch::asm!( + "rdmsr", + in("ecx") msr, + out("eax") low, + out("edx") high, + options(nostack, preserves_flags) + ); + ((high as u64) << 32) | (low as u64) +} + +extern "C" { + fn syscall_entry(); +} + +pub unsafe fn init() { + // 1. Enable System Call Extension (SCE) in IA32_EFER + let efer = rdmsr(IA32_EFER); + wrmsr(IA32_EFER, efer | 1); + + // 2. Set STAR MSR: + // Bits [47:32] = Kernel CS/SS selectors (0x0008) + // Bits [63:48] = User CS/SS selectors base (0x0010 -> SS=0x18, CS=0x20) + let star = (0x0010u64 << 48) | (0x0008u64 << 32); + wrmsr(IA32_STAR, star); + + // 3. Set LSTAR to our assembly entry trampoline + wrmsr(IA32_LSTAR, syscall_entry as *const () as usize as u64); + + // 4. Set FMASK to clear IF (bit 9, 0x200), DF (bit 10), TF (bit 8) + wrmsr(IA32_FMASK, 0x00000200); +} + +#[repr(C)] +pub struct SyscallRegisters { + pub r10: u64, + pub r9: u64, + pub r8: u64, + pub rdx: u64, + pub rsi: u64, + pub rdi: u64, + pub rax: u64, // Syscall number in, return code out +} + +/// Dispatcher called directly from syscall_entry assembly trampoline +#[no_mangle] +pub extern "C" fn kernel_syscall_dispatcher(regs: &mut SyscallRegisters) { + match regs.rax { + // SYS_IPC_CALL = 1 + 1 => { + let dest_cap = regs.rdi; + let opcode = regs.rsi; + let arg0 = regs.rdx; + let arg1 = regs.r8; + let arg2 = regs.r9; + let arg3 = regs.r10; + + let (ret, out0, out1) = crate::ipc::fastpath::handle_fastpath_call( + dest_cap, opcode, arg0, arg1, arg2, arg3, + ); + + regs.rax = ret as u64; + regs.rdx = out0; + regs.r8 = out1; + } + // SYS_THREAD_YIELD = 7 + 7 => { + regs.rax = 0; + } + // SYS_LOG_DEBUG = 9 + 9 => { + let msg_ptr = regs.rdi as *const u8; + let len = regs.rsi as usize; + if !msg_ptr.is_null() && len > 0 && len < 4096 { + let slice = unsafe { core::slice::from_raw_parts(msg_ptr, len) }; + if let Ok(s) = core::str::from_utf8(slice) { + kprintln!("[USER LOG] {}", s); + regs.rax = 0; + return; + } + } + regs.rax = (-1i64) as u64; + } + _ => { + regs.rax = (-1i64) as u64; // SYS_ERR_INVALID_ARG + } + } +} diff --git a/kernel/src/drivers/mod.rs b/kernel/src/drivers/mod.rs new file mode 100644 index 0000000..b1fc0cf --- /dev/null +++ b/kernel/src/drivers/mod.rs @@ -0,0 +1 @@ +pub mod serial; diff --git a/kernel/src/drivers/serial.rs b/kernel/src/drivers/serial.rs new file mode 100644 index 0000000..4675583 --- /dev/null +++ b/kernel/src/drivers/serial.rs @@ -0,0 +1,83 @@ +//! Early Serial Driver (16550 UART on COM1 0x3F8) + +use core::fmt::{self, Write}; +use core::sync::atomic::{AtomicBool, Ordering}; + +const COM1: u16 = 0x3F8; + +static SERIAL_INITIALIZED: AtomicBool = AtomicBool::new(false); + +#[inline] +unsafe fn outb(port: u16, val: u8) { + core::arch::asm!("out dx, al", in("dx") port, in("al") val, options(nomem, nostack, preserves_flags)); +} + +#[inline] +unsafe fn inb(port: u16) -> u8 { + let mut val: u8; + core::arch::asm!("in al, dx", in("dx") port, out("al") val, options(nomem, nostack, preserves_flags)); + val +} + +pub struct SerialPort; + +impl SerialPort { + pub fn init() { + unsafe { + outb(COM1 + 1, 0x00); // Disable all interrupts + outb(COM1 + 3, 0x80); // Enable DLAB (set baud rate divisor) + outb(COM1 + 0, 0x01); // Set divisor to 1 (lo byte) 115200 baud + outb(COM1 + 1, 0x00); // (hi byte) + outb(COM1 + 3, 0x03); // 8 bits, no parity, one stop bit + outb(COM1 + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold + outb(COM1 + 4, 0x0B); // IRQs enabled, RTS/DSR set + } + SERIAL_INITIALIZED.store(true, Ordering::Release); + } + + pub fn write_byte(&self, byte: u8) { + unsafe { + // Wait for transmit buffer empty + while (inb(COM1 + 5) & 0x20) == 0 { + core::hint::spin_loop(); + } + outb(COM1, byte); + } + } + + pub fn write_str(&self, s: &str) { + for b in s.bytes() { + if b == b'\n' { + self.write_byte(b'\r'); + } + self.write_byte(b); + } + } +} + +impl Write for SerialPort { + fn write_str(&mut self, s: &str) -> fmt::Result { + SerialPort.write_str(s); + Ok(()) + } +} + +pub fn _kprint(args: fmt::Arguments) { + let mut port = SerialPort; + let _ = port.write_fmt(args); +} + +#[macro_export] +macro_rules! kprint { + ($($arg:tt)*) => { + $crate::drivers::serial::_kprint(format_args!($($arg)*)) + }; +} + +#[macro_export] +macro_rules! kprintln { + () => ($crate::kprint!("\n")); + ($($arg:tt)*) => ({ + $crate::kprint!("{}\n", format_args!($($arg)*)); + }); +} diff --git a/kernel/src/ipc/fastpath.rs b/kernel/src/ipc/fastpath.rs new file mode 100644 index 0000000..b4b792a --- /dev/null +++ b/kernel/src/ipc/fastpath.rs @@ -0,0 +1,53 @@ +//! High-Performance Fast-Path IPC Subsystem + +use crate::kprintln; + +pub const IPC_OP_PING: u64 = 0x0001; +pub const IPC_OP_PONG: u64 = 0x0002; +pub const IPC_OP_REGISTER_SERVER: u64 = 0x0010; +pub const IPC_OP_LOOKUP_SERVER: u64 = 0x0011; + +pub const CAP_KERNEL_CONTROL: u64 = 1; +pub const CAP_ROOT_SERVER: u64 = 2; + +#[derive(Clone, Copy)] +pub struct FastPathMessage { + pub dest_cap: u64, + pub opcode: u64, + pub args: [u64; 4], +} + +/// Dispatches a fast-path register message directly in Ring 0 +pub fn handle_fastpath_call( + dest_cap: u64, + opcode: u64, + arg0: u64, + _arg1: u64, + _arg2: u64, + _arg3: u64, +) -> (i64, u64, u64) { + match dest_cap { + CAP_KERNEL_CONTROL => { + match opcode { + IPC_OP_PING => { + // Fast ping-pong check (arg0 is sequence, return arg0 + 1) + (0, IPC_OP_PONG, arg0.wrapping_add(1)) + } + IPC_OP_REGISTER_SERVER => { + kprintln!("[IPC-FAST] Server registered with token: {:#x}", arg0); + (0, 0, 0) + } + IPC_OP_LOOKUP_SERVER => { + // Return capability id for requested server hash + (0, CAP_ROOT_SERVER, 0) + } + _ => (-1, 0, 0), // Invalid opcode + } + } + CAP_ROOT_SERVER => { + kprintln!("[IPC-FAST] Forwarding call to Root Server (opcode: {:#x}, arg0: {:#x})", opcode, arg0); + (0, 0xCAFE_BABE, 0) + } + _ => (-2, 0, 0), // Invalid capability + } +} diff --git a/kernel/src/ipc/mod.rs b/kernel/src/ipc/mod.rs new file mode 100644 index 0000000..91f7b16 --- /dev/null +++ b/kernel/src/ipc/mod.rs @@ -0,0 +1,5 @@ +pub mod fastpath; + +pub fn init() { + crate::kprintln!("[IPC] Capability-based Fast-Path IPC Ready."); +} diff --git a/kernel/src/limine_requests.rs b/kernel/src/limine_requests.rs new file mode 100644 index 0000000..b430263 --- /dev/null +++ b/kernel/src/limine_requests.rs @@ -0,0 +1,162 @@ +//! Limine Bootloader Protocol Requests & Types for opencoreC Microkernel +//! Implements Base Revision 3 specification. + +#[repr(C)] +pub struct LimineBaseRevision { + pub id: [u64; 4], + pub revision: u64, +} +unsafe impl Sync for LimineBaseRevision {} + +#[repr(C)] +pub struct LimineHhdmResponse { + pub revision: u64, + pub offset: u64, +} + +#[repr(C)] +pub struct LimineHhdmRequest { + pub id: [u64; 4], + pub revision: u64, + pub response: *const LimineHhdmResponse, +} +unsafe impl Sync for LimineHhdmRequest {} + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LimineMemoryType { + Usable = 0, + Reserved = 1, + AcpiReclaimable = 2, + AcpiNvs = 3, + BadMemory = 4, + BootloaderReclaimable = 5, + KernelAndModules = 6, + Framebuffer = 7, +} + +#[repr(C)] +pub struct LimineMemmapEntry { + pub base: u64, + pub length: u64, + pub typ: u64, +} + +#[repr(C)] +pub struct LimineMemmapResponse { + pub revision: u64, + pub entry_count: u64, + pub entries: *const *const LimineMemmapEntry, +} + +#[repr(C)] +pub struct LimineMemmapRequest { + pub id: [u64; 4], + pub revision: u64, + pub response: *const LimineMemmapResponse, +} +unsafe impl Sync for LimineMemmapRequest {} + +#[repr(C)] +pub struct LimineKernelAddressResponse { + pub revision: u64, + pub physical_base: u64, + pub virtual_base: u64, +} + +#[repr(C)] +pub struct LimineKernelAddressRequest { + pub id: [u64; 4], + pub revision: u64, + pub response: *const LimineKernelAddressResponse, +} +unsafe impl Sync for LimineKernelAddressRequest {} + +#[repr(C)] +pub struct LimineFile { + pub revision: u64, + pub address: *const u8, + pub size: u64, + pub path: *const u8, + pub cmdline: *const u8, + pub media_type: u32, + pub unused: u32, + pub tftp_ip: u32, + pub tftp_port: u32, + pub partition_index: u32, + pub mbr_disk_id: u32, + pub gpt_disk_uuid: [u8; 16], + pub gpt_part_uuid: [u8; 16], + pub part_uuid: [u8; 16], +} + +#[repr(C)] +pub struct LimineModuleResponse { + pub revision: u64, + pub module_count: u64, + pub modules: *const *const LimineFile, +} + +#[repr(C)] +pub struct LimineModuleRequest { + pub id: [u64; 4], + pub revision: u64, + pub response: *const LimineModuleResponse, + pub internal_module_count: u64, + pub internal_modules: *const *const LimineFile, +} +unsafe impl Sync for LimineModuleRequest {} + +/* Constants for Limine Request Identifiers */ +#[used] +#[link_section = ".limine_requests"] +pub static BASE_REVISION: LimineBaseRevision = LimineBaseRevision { + id: [0xf9562b2d5c95a6c8, 0x6a59638edd43477b, 0, 3], + revision: 3, +}; + +#[used] +#[link_section = ".limine_requests"] +pub static HHDM_REQUEST: LimineHhdmRequest = LimineHhdmRequest { + id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x48d150213051ecda, 0x21e2d0708170e71e], + revision: 0, + response: core::ptr::null(), +}; + +#[used] +#[link_section = ".limine_requests"] +pub static MEMMAP_REQUEST: LimineMemmapRequest = LimineMemmapRequest { + id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x67cf3d9d378a14e6, 0xe310ac092f613308], + revision: 0, + response: core::ptr::null(), +}; + +#[used] +#[link_section = ".limine_requests"] +pub static KERNEL_ADDRESS_REQUEST: LimineKernelAddressRequest = LimineKernelAddressRequest { + id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x71ba76863cc55f63, 0xb2644a48c516a487], + revision: 0, + response: core::ptr::null(), +}; + +#[used] +#[link_section = ".limine_requests"] +pub static MODULE_REQUEST: LimineModuleRequest = LimineModuleRequest { + id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x3e7e2797022b3699, 0xa69dc94525d978b1], + revision: 0, + response: core::ptr::null(), + internal_module_count: 0, + internal_modules: core::ptr::null(), +}; + +/// Get HHDM direct map virtual offset +pub fn get_hhdm_offset() -> Option { + unsafe { + let resp = HHDM_REQUEST.response; + if !resp.is_null() { + Some((*resp).offset) + } else { + None + } + } +} diff --git a/kernel/src/main.rs b/kernel/src/main.rs new file mode 100644 index 0000000..3986ad4 --- /dev/null +++ b/kernel/src/main.rs @@ -0,0 +1,120 @@ +#![no_std] +#![no_main] + +pub mod limine_requests; +pub mod drivers; +pub mod arch; +pub mod mm; +pub mod ipc; +pub mod sched; + +use core::panic::PanicInfo; +use limine_requests::*; + +// Include assembly trampolines directly into the binary +core::arch::global_asm!(include_str!("../asm/context.S")); +core::arch::global_asm!(include_str!("../asm/syscall_entry.S")); + +#[no_mangle] +pub extern "C" fn _start() -> ! { + // 1. Validate Limine Base Revision + if BASE_REVISION.revision != 3 { + // Revision mismatch + loop { core::hint::spin_loop(); } + } + + // 2. Early Serial Initialization + drivers::serial::SerialPort::init(); + + kprintln!("\n======================================================="); + kprintln!(" opencoreC Microkernel v0.1.0 (x86_64)"); + kprintln!("======================================================="); + + // 3. Obtain Limine responses + let hhdm_offset = get_hhdm_offset().expect("Limine HHDM response missing"); + let memmap_resp = MEMMAP_REQUEST.response; + let kernel_addr_resp = KERNEL_ADDRESS_REQUEST.response; + + kprintln!("[BOOT] Limine HHDM Base Virtual Address: {:#018x}", hhdm_offset); + if !kernel_addr_resp.is_null() { + unsafe { + kprintln!("[BOOT] Kernel Physical Base: {:#018x}, Virtual Base: {:#018x}", + (*kernel_addr_resp).physical_base, + (*kernel_addr_resp).virtual_base + ); + } + } + + // 4. Initialize Architecture (GDT, IDT, Syscall MSRs) + kprintln!("[BOOT] Initializing GDT, IDT, and Fast-Path Syscall MSRs..."); + unsafe { + arch::init(); + } + kprintln!("[BOOT] CPU Subsystem Configured."); + + // 5. Initialize Memory Subsystem (Physical Frame Allocator + Virtual Memory Manager) + kprintln!("[BOOT] Initializing Physical Frame Allocator & Paging..."); + unsafe { + mm::init(memmap_resp); + } + + // 6. Initialize IPC & Capability System + ipc::init(); + + // 7. Initialize Scheduler + sched::init(); + + // 8. Inspect Boot Modules (Initial Userspace Servers) + unsafe { + let mod_resp = MODULE_REQUEST.response; + if !mod_resp.is_null() { + let count = (*mod_resp).module_count; + kprintln!("[BOOT] Found {} initial userspace module(s):", count); + for i in 0..count { + let mod_file = *(*mod_resp).modules.add(i as usize); + let path_cstr = (*mod_file).path; + let path_len = (0..256).find(|&j| *path_cstr.add(j) == 0).unwrap_or(0); + let path = core::str::from_utf8(core::slice::from_raw_parts(path_cstr, path_len)).unwrap_or("unknown"); + kprintln!(" - Module [{}]: {} (size: {} bytes, addr: {:p})", + i, path, (*mod_file).size, (*mod_file).address); + } + } else { + kprintln!("[BOOT] No external modules passed by bootloader."); + } + } + + // 9. Kernel Self-Test: Fast-Path IPC Ping + kprintln!("[TEST] Executing Kernel Fast-Path IPC Self-Test..."); + let (status, resp_op, val) = ipc::fastpath::handle_fastpath_call( + ipc::fastpath::CAP_KERNEL_CONTROL, + ipc::fastpath::IPC_OP_PING, + 42, 0, 0, 0 + ); + kprintln!("[TEST] IPC Fast-Path Ping Result: status={}, opcode={:#x}, val={}", status, resp_op, val); + + kprintln!("[KERNEL] Initialization complete. Entering kernel idle loop.\n"); + + loop { + unsafe { + core::arch::asm!("hlt"); + } + } +} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + kprintln!("\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + kprintln!(" KERNEL PANIC DETECTED "); + kprintln!("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + if let Some(location) = info.location() { + kprintln!("Location: {}:{}:{}", location.file(), location.line(), location.column()); + } + kprintln!("Message: {}", info.message()); + kprintln!("Halting CPU core."); + + loop { + unsafe { + core::arch::asm!("cli; hlt"); + } + } +} diff --git a/kernel/src/mm/mod.rs b/kernel/src/mm/mod.rs new file mode 100644 index 0000000..5eaf88e --- /dev/null +++ b/kernel/src/mm/mod.rs @@ -0,0 +1,14 @@ +pub mod pfa; +pub mod vmm; + +use crate::limine_requests::{LimineMemmapResponse, get_hhdm_offset}; +use crate::kprintln; + +pub unsafe fn init(memmap: *const LimineMemmapResponse) { + let hhdm = get_hhdm_offset().expect("Limine HHDM is required"); + let pfa_ptr = core::ptr::addr_of_mut!(pfa::PFA); + let vmm_ptr = core::ptr::addr_of_mut!(vmm::VMM); + (*pfa_ptr).init(memmap, hhdm); + (*vmm_ptr).init(hhdm); + kprintln!("[MM] Memory Management Subsystem Initialized."); +} diff --git a/kernel/src/mm/pfa.rs b/kernel/src/mm/pfa.rs new file mode 100644 index 0000000..9d731b6 --- /dev/null +++ b/kernel/src/mm/pfa.rs @@ -0,0 +1,167 @@ +//! Physical Frame Allocator (PFA) +//! Bitmap-based 4KiB page frame allocator. + +use crate::limine_requests::{LimineMemmapResponse, LimineMemoryType}; +use crate::kprintln; + +pub const PAGE_SIZE: usize = 4096; + +pub struct FrameAllocator { + bitmap: *mut u8, + bitmap_size_bytes: usize, + total_frames: usize, + free_frames: usize, + hhdm_offset: u64, +} + +unsafe impl Send for FrameAllocator {} +unsafe impl Sync for FrameAllocator {} + +pub static mut PFA: FrameAllocator = FrameAllocator { + bitmap: core::ptr::null_mut(), + bitmap_size_bytes: 0, + total_frames: 0, + free_frames: 0, + hhdm_offset: 0, +}; + +impl FrameAllocator { + pub unsafe fn init(&mut self, memmap: *const LimineMemmapResponse, hhdm: u64) { + self.hhdm_offset = hhdm; + if memmap.is_null() { + return; + } + + let entry_count = (*memmap).entry_count as usize; + let entries = (*memmap).entries; + + let mut max_paddr: u64 = 0; + for i in 0..entry_count { + let entry = *entries.add(i); + let end = (*entry).base + (*entry).length; + if end > max_paddr { + max_paddr = end; + } + } + + self.total_frames = (max_paddr as usize) / PAGE_SIZE; + self.bitmap_size_bytes = (self.total_frames + 7) / 8; + + // Find a usable region large enough to place the bitmap + let mut bitmap_paddr: u64 = 0; + for i in 0..entry_count { + let entry = *entries.add(i); + if (*entry).typ == LimineMemoryType::Usable as u64 + && (*entry).length >= self.bitmap_size_bytes as u64 + { + bitmap_paddr = (*entry).base; + break; + } + } + + if bitmap_paddr == 0 { + kprintln!("[FATAL] Could not find memory for PFA bitmap!"); + return; + } + + self.bitmap = (bitmap_paddr + hhdm) as *mut u8; + // Initially mark all frames as used (1) + core::ptr::write_bytes(self.bitmap, 0xFF, self.bitmap_size_bytes); + + // Mark only truly usable RAM regions as free (0) + self.free_frames = 0; + for i in 0..entry_count { + let entry = *entries.add(i); + if (*entry).typ == LimineMemoryType::Usable as u64 { + let start_frame = ((*entry).base as usize) / PAGE_SIZE; + let count = ((*entry).length as usize) / PAGE_SIZE; + for f in start_frame..(start_frame + count) { + self.clear_bit(f); + self.free_frames += 1; + } + } + } + + // Mark the bitmap memory itself as allocated + let bitmap_start_frame = (bitmap_paddr as usize) / PAGE_SIZE; + let bitmap_frame_count = (self.bitmap_size_bytes + PAGE_SIZE - 1) / PAGE_SIZE; + for f in bitmap_start_frame..(bitmap_start_frame + bitmap_frame_count) { + self.set_bit(f); + if self.free_frames > 0 { + self.free_frames -= 1; + } + } + + // Also protect low 1MiB + let low_1mb_frames = 0x100000 / PAGE_SIZE; + for f in 0..low_1mb_frames { + if !self.test_bit(f) { + self.set_bit(f); + if self.free_frames > 0 { + self.free_frames -= 1; + } + } + } + + kprintln!("[PFA] Initialized: {} MiB total, {} MiB free", + (self.total_frames * PAGE_SIZE) / (1024 * 1024), + (self.free_frames * PAGE_SIZE) / (1024 * 1024) + ); + } + + #[inline] + fn test_bit(&self, frame: usize) -> bool { + if frame >= self.total_frames { + return true; + } + unsafe { + let byte = *self.bitmap.add(frame / 8); + (byte & (1 << (frame % 8))) != 0 + } + } + + #[inline] + fn set_bit(&self, frame: usize) { + if frame < self.total_frames { + unsafe { + let ptr = self.bitmap.add(frame / 8); + *ptr |= 1 << (frame % 8); + } + } + } + + #[inline] + fn clear_bit(&self, frame: usize) { + if frame < self.total_frames { + unsafe { + let ptr = self.bitmap.add(frame / 8); + *ptr &= !(1 << (frame % 8)); + } + } + } + + pub fn alloc_frame(&mut self) -> Option { + for frame in 0..self.total_frames { + if !self.test_bit(frame) { + self.set_bit(frame); + self.free_frames -= 1; + let paddr = (frame * PAGE_SIZE) as u64; + // Zero out the frame + unsafe { + let vaddr = (paddr + self.hhdm_offset) as *mut u8; + core::ptr::write_bytes(vaddr, 0, PAGE_SIZE); + } + return Some(paddr); + } + } + None + } + + pub fn free_frame(&mut self, paddr: u64) { + let frame = (paddr as usize) / PAGE_SIZE; + if frame < self.total_frames && self.test_bit(frame) { + self.clear_bit(frame); + self.free_frames += 1; + } + } +} diff --git a/kernel/src/mm/vmm.rs b/kernel/src/mm/vmm.rs new file mode 100644 index 0000000..fc72e91 --- /dev/null +++ b/kernel/src/mm/vmm.rs @@ -0,0 +1,90 @@ +//! Virtual Memory Manager (VMM) & 4-Level Paging (x86_64) + +use super::pfa::PFA; + +pub const PAGE_PRESENT: u64 = 1 << 0; +pub const PAGE_WRITABLE: u64 = 1 << 1; +pub const PAGE_USER: u64 = 1 << 2; +pub const PAGE_WRITE_THROUGH: u64 = 1 << 3; +pub const PAGE_NO_CACHE: u64 = 1 << 4; +pub const PAGE_HUGE: u64 = 1 << 7; +pub const PAGE_NO_EXECUTE: u64 = 1 << 63; + +pub const PAGE_MASK: u64 = 0x000F_FFFF_FFFF_F000; + +#[repr(C, align(4096))] +pub struct PageTable { + pub entries: [u64; 512], +} + +pub struct VirtualMemoryManager { + hhdm_offset: u64, +} + +pub static mut VMM: VirtualMemoryManager = VirtualMemoryManager { hhdm_offset: 0 }; + +impl VirtualMemoryManager { + pub fn init(&mut self, hhdm: u64) { + self.hhdm_offset = hhdm; + } + + #[inline] + fn phys_to_virt(&self, paddr: u64) -> *mut T { + (paddr + self.hhdm_offset) as *mut T + } + + /// Map a single 4KiB page into the specified PML4 + pub unsafe fn map_page( + &self, + pml4_paddr: u64, + vaddr: u64, + paddr: u64, + flags: u64, + ) -> Result<(), ()> { + let pml4_idx = ((vaddr >> 39) & 0x1FF) as usize; + let pdpt_idx = ((vaddr >> 30) & 0x1FF) as usize; + let pd_idx = ((vaddr >> 21) & 0x1FF) as usize; + let pt_idx = ((vaddr >> 12) & 0x1FF) as usize; + + let pml4 = self.phys_to_virt::(pml4_paddr); + + // 1. Traverse / allocate PDPT + let pdpt_paddr = self.get_or_alloc_table(&mut (*pml4).entries[pml4_idx], flags)?; + let pdpt = self.phys_to_virt::(pdpt_paddr); + + // 2. Traverse / allocate PD + let pd_paddr = self.get_or_alloc_table(&mut (*pdpt).entries[pdpt_idx], flags)?; + let pd = self.phys_to_virt::(pd_paddr); + + // 3. Traverse / allocate PT + let pt_paddr = self.get_or_alloc_table(&mut (*pd).entries[pd_idx], flags)?; + let pt = self.phys_to_virt::(pt_paddr); + + // 4. Map the physical page in the PT + (*pt).entries[pt_idx] = (paddr & PAGE_MASK) | flags | PAGE_PRESENT; + + // Invalidate TLB for this virtual address + core::arch::asm!("invlpg [{0}]", in(reg) vaddr, options(nostack, preserves_flags)); + + Ok(()) + } + + unsafe fn get_or_alloc_table(&self, entry: &mut u64, inherit_flags: u64) -> Result { + if (*entry & PAGE_PRESENT) != 0 { + // Update permissions if user access required + if (inherit_flags & PAGE_USER) != 0 { + *entry |= PAGE_USER; + } + if (inherit_flags & PAGE_WRITABLE) != 0 { + *entry |= PAGE_WRITABLE; + } + Ok(*entry & PAGE_MASK) + } else { + let pfa_ptr = core::ptr::addr_of_mut!(PFA); + let new_frame = (*pfa_ptr).alloc_frame().ok_or(())?; + let flags = PAGE_PRESENT | PAGE_WRITABLE | (inherit_flags & PAGE_USER); + *entry = (new_frame & PAGE_MASK) | flags; + Ok(new_frame) + } + } +} diff --git a/kernel/src/sched/mod.rs b/kernel/src/sched/mod.rs new file mode 100644 index 0000000..a4114f4 --- /dev/null +++ b/kernel/src/sched/mod.rs @@ -0,0 +1,35 @@ +//! Minimal Thread & Task Scheduler Subsystem + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ThreadState { + Ready, + Running, + BlockedOnIpc, + Dead, +} + +#[repr(C)] +pub struct ContextFrame { + pub r15: u64, + pub r14: u64, + pub r13: u64, + pub r12: u64, + pub rbx: u64, + pub rbp: u64, + pub rsp: u64, + pub rip: u64, + pub rflags: u64, +} + +pub struct Thread { + pub id: u64, + pub state: ThreadState, + pub rsp: u64, + pub cr3: u64, + pub kernel_stack_top: u64, +} + +pub fn init() { + crate::kprintln!("[SCHED] Scheduler initialized (Round-Robin preemptive stub)."); +} diff --git a/lib/libc/include/stddef.h b/lib/libc/include/stddef.h new file mode 100644 index 0000000..4e3107b --- /dev/null +++ b/lib/libc/include/stddef.h @@ -0,0 +1,14 @@ +#pragma once + +typedef __SIZE_TYPE__ size_t; +typedef __PTRDIFF_TYPE__ ptrdiff_t; +typedef __INTPTR_TYPE__ intptr_t; +typedef __UINTPTR_TYPE__ uintptr_t; + +#ifndef NULL +#define NULL ((void*)0) +#endif + +#ifndef offsetof +#define offsetof(type, member) __builtin_offsetof(type, member) +#endif diff --git a/lib/libc/include/stdint.h b/lib/libc/include/stdint.h new file mode 100644 index 0000000..1048071 --- /dev/null +++ b/lib/libc/include/stdint.h @@ -0,0 +1,24 @@ +#pragma once + +typedef __INT8_TYPE__ int8_t; +typedef __INT16_TYPE__ int16_t; +typedef __INT32_TYPE__ int32_t; +typedef __INT64_TYPE__ int64_t; + +typedef __UINT8_TYPE__ uint8_t; +typedef __UINT16_TYPE__ uint16_t; +typedef __UINT32_TYPE__ uint32_t; +typedef __UINT64_TYPE__ uint64_t; + +typedef __INTPTR_TYPE__ intptr_t; +typedef __UINTPTR_TYPE__ uintptr_t; + +#define INT8_MAX 127 +#define INT16_MAX 32767 +#define INT32_MAX 2147483647 +#define INT64_MAX 9223372036854775807LL + +#define UINT8_MAX 255 +#define UINT16_MAX 65535 +#define UINT32_MAX 4294967295U +#define UINT64_MAX 18446744073709551615ULL diff --git a/lib/libc/include/stdio.h b/lib/libc/include/stdio.h new file mode 100644 index 0000000..4bba307 --- /dev/null +++ b/lib/libc/include/stdio.h @@ -0,0 +1,15 @@ +#pragma once + +#include "stddef.h" +#include "stdint.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int puts(const char *s); +int putchar(int c); + +#ifdef __cplusplus +} +#endif diff --git a/lib/libc/include/string.h b/lib/libc/include/string.h new file mode 100644 index 0000000..e06fbaf --- /dev/null +++ b/lib/libc/include/string.h @@ -0,0 +1,23 @@ +#pragma once + +#include "stddef.h" +#include "stdint.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void *memset(void *dest, int c, size_t n); +void *memcpy(void *dest, const void *src, size_t n); +void *memmove(void *dest, const void *src, size_t n); +int memcmp(const void *s1, const void *s2, size_t n); + +size_t strlen(const char *s); +int strcmp(const char *s1, const char *s2); +int strncmp(const char *s1, const char *s2, size_t n); +char *strcpy(char *dest, const char *src); +char *strncpy(char *dest, const char *src, size_t n); + +#ifdef __cplusplus +} +#endif diff --git a/lib/libc/src/string.c b/lib/libc/src/string.c new file mode 100644 index 0000000..d45348f --- /dev/null +++ b/lib/libc/src/string.c @@ -0,0 +1,92 @@ +#include "../include/string.h" + +void *memset(void *dest, int c, size_t n) { + uint8_t *p = (uint8_t *)dest; + uint8_t val = (uint8_t)c; + while (n--) { + *p++ = val; + } + return dest; +} + +void *memcpy(void *dest, const void *src, size_t n) { + uint8_t *d = (uint8_t *)dest; + const uint8_t *s = (const uint8_t *)src; + while (n--) { + *d++ = *s++; + } + return dest; +} + +void *memmove(void *dest, const void *src, size_t n) { + uint8_t *d = (uint8_t *)dest; + const uint8_t *s = (const uint8_t *)src; + if (d < s) { + while (n--) { + *d++ = *s++; + } + } else { + d += n; + s += n; + while (n--) { + *--d = *--s; + } + } + return dest; +} + +int memcmp(const void *s1, const void *s2, size_t n) { + const uint8_t *p1 = (const uint8_t *)s1; + const uint8_t *p2 = (const uint8_t *)s2; + while (n--) { + if (*p1 != *p2) { + return *p1 - *p2; + } + p1++; + p2++; + } + return 0; +} + +size_t strlen(const char *s) { + size_t len = 0; + while (s[len]) { + len++; + } + return len; +} + +int strcmp(const char *s1, const char *s2) { + while (*s1 && (*s1 == *s2)) { + s1++; + s2++; + } + return *(const unsigned char *)s1 - *(const unsigned char *)s2; +} + +int strncmp(const char *s1, const char *s2, size_t n) { + while (n && *s1 && (*s1 == *s2)) { + s1++; + s2++; + n--; + } + if (n == 0) return 0; + return *(const unsigned char *)s1 - *(const unsigned char *)s2; +} + +char *strcpy(char *dest, const char *src) { + char *ret = dest; + while ((*dest++ = *src++)); + return ret; +} + +char *strncpy(char *dest, const char *src, size_t n) { + char *ret = dest; + while (n && (*dest++ = *src++)) { + n--; + } + while (n--) { + *dest++ = '\0'; + } + return ret; +} diff --git a/lib/libc/src/syscalls.c b/lib/libc/src/syscalls.c new file mode 100644 index 0000000..50bfbc6 --- /dev/null +++ b/lib/libc/src/syscalls.c @@ -0,0 +1,17 @@ +#include "stdio.h" +#include "abi/syscalls.h" +#include "abi/types.h" + +int puts(const char *s) { + size_t len = 0; + while (s[len]) len++; + sys_log_debug(s, len); + sys_log_debug("\n", 1); + return (int)len; +} + +int putchar(int c) { + char ch = (char)c; + sys_log_debug(&ch, 1); + return c; +} diff --git a/lib/libipc_cpp/include/ipc.hpp b/lib/libipc_cpp/include/ipc.hpp new file mode 100644 index 0000000..eb9a40c --- /dev/null +++ b/lib/libipc_cpp/include/ipc.hpp @@ -0,0 +1,116 @@ +#pragma once + +#include "abi/types.h" +#include "abi/syscalls.h" +#include "abi/ipc.h" +#include "stddef.h" +#include "stdint.h" + +namespace ipc { + +// Strongly typed result wrapper (no exceptions) +template +struct Result { + sysret_t status; + T value; + + constexpr bool is_ok() const noexcept { return status == SYS_OK; } + constexpr bool is_err() const noexcept { return status != SYS_OK; } + constexpr sysret_t error() const noexcept { return status; } +}; + +struct FastResponse { + uint64_t out0; + uint64_t out1; +}; + +// RAII Capability Handle +class Capability { +private: + cap_t handle_; + +public: + constexpr Capability() noexcept : handle_(CAP_NULL) {} + constexpr explicit Capability(cap_t handle) noexcept : handle_(handle) {} + + ~Capability() { + // Future: syscall to drop capability reference + } + + Capability(const Capability&) = delete; + Capability& operator=(const Capability&) = delete; + + constexpr Capability(Capability&& other) noexcept : handle_(other.handle_) { + other.handle_ = CAP_NULL; + } + + constexpr Capability& operator=(Capability&& other) noexcept { + if (this != &other) { + handle_ = other.handle_; + other.handle_ = CAP_NULL; + } + return *this; + } + + [[nodiscard]] constexpr cap_t get() const noexcept { return handle_; } + [[nodiscard]] constexpr bool is_valid() const noexcept { return handle_ != CAP_NULL; } +}; + +// RAII IPC Endpoint Wrapper +class Endpoint { +private: + Capability cap_; + +public: + explicit Endpoint(Capability&& cap) noexcept : cap_(static_cast(cap)) {} + explicit Endpoint(cap_t cap) noexcept : cap_(cap) {} + + // Fast-path synchronous register call (zero allocations) + [[nodiscard]] Result call( + uint64_t opcode, + uint64_t arg0 = 0, + uint64_t arg1 = 0, + uint64_t arg2 = 0, + uint64_t arg3 = 0 + ) const noexcept { + uint64_t out0 = 0; + uint64_t out1 = 0; + sysret_t ret = sys_ipc_call_raw(cap_.get(), opcode, arg0, arg1, arg2, arg3, &out0, &out1); + return Result{ .status = ret, .value = { out0, out1 } }; + } + + [[nodiscard]] cap_t raw_handle() const noexcept { return cap_.get(); } +}; + +// RAII Shared Memory Buffer +class SharedBuffer { +private: + Capability shm_cap_; + void* base_addr_; + size_t size_; + +public: + SharedBuffer(Capability&& cap, void* addr, size_t sz) noexcept + : shm_cap_(static_cast(cap)), base_addr_(addr), size_(sz) {} + + ~SharedBuffer() { + // Future: sys_unmap(base_addr_, size_) + } + + SharedBuffer(const SharedBuffer&) = delete; + SharedBuffer& operator=(const SharedBuffer&) = delete; + + SharedBuffer(SharedBuffer&& other) noexcept + : shm_cap_(static_cast(other.shm_cap_)), + base_addr_(other.base_addr_), + size_(other.size_) { + other.base_addr_ = nullptr; + other.size_ = 0; + } + + [[nodiscard]] void* data() const noexcept { return base_addr_; } + [[nodiscard]] size_t size() const noexcept { return size_; } + [[nodiscard]] cap_t handle() const noexcept { return shm_cap_.get(); } +}; + +} // namespace ipc diff --git a/lib/libipc_cpp/src/ipc.cpp b/lib/libipc_cpp/src/ipc.cpp new file mode 100644 index 0000000..8696d59 --- /dev/null +++ b/lib/libipc_cpp/src/ipc.cpp @@ -0,0 +1,5 @@ +#include "../include/ipc.hpp" + +namespace ipc { + // Shared library routines if needed +} diff --git a/limine.cfg b/limine.cfg new file mode 100644 index 0000000..76195e6 --- /dev/null +++ b/limine.cfg @@ -0,0 +1,8 @@ +# Limine Bootloader Configuration (Legacy Alias) +TIMEOUT=3 + +:opencoreC Microkernel (x86_64) + PROTOCOL=limine + KERNEL_PATH=boot:///boot/opencore_kernel.elf + MODULE_PATH=boot:///boot/init_server.elf + MODULE_PATH=boot:///boot/uart_driver.elf diff --git a/limine.conf b/limine.conf new file mode 100644 index 0000000..9753e18 --- /dev/null +++ b/limine.conf @@ -0,0 +1,9 @@ +# Limine Bootloader Configuration for opencoreC Microkernel +timeout: 3 +default_entry: 1 + +/opencoreC Microkernel (x86_64) + protocol: limine + kernel_path: boot():/boot/opencore_kernel.elf + module_path: boot():/boot/init_server.elf + module_path: boot():/boot/uart_driver.elf diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..9f0a955 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +targets = ["x86_64-unknown-none"] +profile = "minimal" diff --git a/servers/init/main.cpp b/servers/init/main.cpp new file mode 100644 index 0000000..29fdecc --- /dev/null +++ b/servers/init/main.cpp @@ -0,0 +1,32 @@ +#include "ipc.hpp" +#include "stdio.h" +#include "string.h" + +extern "C" void _start() { + puts("[INIT-SERVER] Userspace Root Init Server Started (Ring 3)!"); + + // 1. Instantiate RAII IPC Endpoint for Kernel Control + ipc::Endpoint kernel_ctrl(CAP_SELF_TASK); + + puts("[INIT-SERVER] Sending Fast-Path IPC Ping to Microkernel..."); + auto ping_res = kernel_ctrl.call(IPC_OP_PING, 100); + + if (ping_res.is_ok()) { + puts("[INIT-SERVER] Received IPC Ping Response from Microkernel!"); + } else { + puts("[INIT-SERVER] IPC Call Failed!"); + } + + // 2. Register Server Endpoint + puts("[INIT-SERVER] Registering Root Server Capability..."); + auto reg_res = kernel_ctrl.call(IPC_OP_REGISTER_SERVER, 0x1337BEEF); + if (reg_res.is_ok()) { + puts("[INIT-SERVER] Server Registered Successfully!"); + } + + puts("[INIT-SERVER] Root Init Server ready. Entering event loop."); + + while (true) { + sys_yield(); + } +} diff --git a/servers/procmgr/main.cpp b/servers/procmgr/main.cpp new file mode 100644 index 0000000..c9cc030 --- /dev/null +++ b/servers/procmgr/main.cpp @@ -0,0 +1,10 @@ +#include "ipc.hpp" +#include "stdio.h" + +extern "C" void _start() { + puts("[PROCMGR] Process & Server Manager Daemon Initialized."); + + while (true) { + sys_yield(); + } +} diff --git a/servers/uart_driver/main.c b/servers/uart_driver/main.c new file mode 100644 index 0000000..973797f --- /dev/null +++ b/servers/uart_driver/main.c @@ -0,0 +1,17 @@ +#include "uart.h" +#include "stdio.h" +#include "abi/syscalls.h" + +extern void _start(void) { + uart_init(UART_COM1_PORT, 115200); + uart_puts(UART_COM1_PORT, "[RING3-UART-DRIVER] 16550 UART COM1 initialized from userspace!\n"); + + while (1) { + if (uart_has_data(UART_COM1_PORT)) { + char c = uart_getc(UART_COM1_PORT); + // Echo back + uart_putc(UART_COM1_PORT, c); + } + sys_yield(); + } +}