feat: initial scalable x86_64 microkernel skeleton with Limine, Rust, C HAL, and C++20 IPC
This commit is contained in:
+21
@@ -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
|
||||
@@ -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)
|
||||
+16
@@ -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
|
||||
@@ -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"
|
||||
@@ -0,0 +1,149 @@
|
||||
# opencoreC: High-Performance x86_64 Microkernel OS
|
||||
|
||||
[-blue.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.
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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__ */
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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::<TaskStateSegment>() 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::<TaskStateSegment>() - 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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
pub mod gdt;
|
||||
pub mod idt;
|
||||
pub mod syscall;
|
||||
|
||||
pub unsafe fn init() {
|
||||
gdt::init();
|
||||
idt::init();
|
||||
syscall::init();
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod serial;
|
||||
@@ -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)*));
|
||||
});
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
pub mod fastpath;
|
||||
|
||||
pub fn init() {
|
||||
crate::kprintln!("[IPC] Capability-based Fast-Path IPC Ready.");
|
||||
}
|
||||
@@ -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<u64> {
|
||||
unsafe {
|
||||
let resp = HHDM_REQUEST.response;
|
||||
if !resp.is_null() {
|
||||
Some((*resp).offset)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.");
|
||||
}
|
||||
@@ -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<u64> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<T>(&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::<PageTable>(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::<PageTable>(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::<PageTable>(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::<PageTable>(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<u64, ()> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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).");
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 <typename T>
|
||||
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<Capability&&>(cap)) {}
|
||||
explicit Endpoint(cap_t cap) noexcept : cap_(cap) {}
|
||||
|
||||
// Fast-path synchronous register call (zero allocations)
|
||||
[[nodiscard]] Result<FastResponse> 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<FastResponse>{ .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<Capability&&>(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<Capability&&>(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
|
||||
@@ -0,0 +1,5 @@
|
||||
#include "../include/ipc.hpp"
|
||||
|
||||
namespace ipc {
|
||||
// Shared library routines if needed
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
[toolchain]
|
||||
targets = ["x86_64-unknown-none"]
|
||||
profile = "minimal"
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user