feat: implement PS/2 keyboard & shell, dynamic heap (malloc/new), Zero-Copy SHM, and SMP multicore initialization

This commit is contained in:
RarDog
2026-09-02 16:58:33 +03:00
parent e7a0d2e6b1
commit 4d7ee738b6
18 changed files with 698 additions and 31 deletions
+19 -12
View File
@@ -33,8 +33,11 @@ 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
SH_SERVER := $(BUILD_DIR)/sh.elf
PROCMGR := $(BUILD_DIR)/procmgr.elf
LIBC_OBJS := $(BUILD_DIR)/string.o $(BUILD_DIR)/syscalls.o $(BUILD_DIR)/stdlib.o
.PHONY: all kernel libc hal servers iso run clean help
all: kernel libc hal servers
@@ -43,7 +46,7 @@ all: kernel libc hal servers
@echo "======================================================="
@echo " [SUCCESS] opencoreC Skeleton Build Complete!"
@echo " Kernel: $(KERNEL_ELF)"
@echo " Servers: $(INIT_SERVER), $(UART_DRIVER), $(PROCMGR)"
@echo " Servers: $(INIT_SERVER), $(UART_DRIVER), $(SH_SERVER), $(PROCMGR)"
@echo "======================================================="
kernel:
@@ -56,6 +59,7 @@ libc:
@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
@$(CC) $(CFLAGS) -c $(LIB_DIR)/libc/src/stdlib.c -o $(BUILD_DIR)/stdlib.o
hal: libc
@mkdir -p $(BUILD_DIR)
@@ -67,18 +71,24 @@ hal: libc
servers: libc hal
@mkdir -p $(BUILD_DIR)
@echo "[CXX/CC] Linking Ring 3 User-space Servers..."
@# C++ LibIPC
@$(CXX) $(CXXFLAGS) -c $(LIB_DIR)/libipc_cpp/src/ipc.cpp -o $(BUILD_DIR)/ipc.o
@# 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
$(BUILD_DIR)/init_main.o $(BUILD_DIR)/ipc.o $(LIBC_OBJS)
@# 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
$(BUILD_DIR)/uart_driver_main.o $(BUILD_DIR)/uart.o $(LIBC_OBJS)
@# Interactive Shell
@$(CXX) $(CXXFLAGS) -c $(SERVERS_DIR)/sh/main.cpp -o $(BUILD_DIR)/sh_main.o
@$(LD) -T $(CONFIG_DIR)/user.ld -nostdlib -static -o $(SH_SERVER) \
$(BUILD_DIR)/sh_main.o $(BUILD_DIR)/ipc.o $(LIBC_OBJS)
@# 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
$(BUILD_DIR)/procmgr_main.o $(BUILD_DIR)/ipc.o $(LIBC_OBJS)
# Staging Limine Boot ISO directory
iso: all
@@ -86,13 +96,14 @@ iso: all
@cp $(KERNEL_ELF) $(ISO_DIR)/boot/
@cp $(INIT_SERVER) $(ISO_DIR)/boot/
@cp $(UART_DRIVER) $(ISO_DIR)/boot/
@cp $(SH_SERVER) $(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 \
@echo "[QEMU] Launching opencoreC in QEMU (SMP 4 Cores, Serial stdio)..."
@$(QEMU) -M q35 -smp 4 -m 512M \
-serial stdio \
-display none \
-no-reboot \
@@ -106,10 +117,6 @@ clean:
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 all - Build kernel, HAL, libc, and all user servers"
@echo " make run - Launch QEMU with SMP 4 Cores & Serial output"
@echo " make clean - Clean build artifacts"
+36 -2
View File
@@ -6,12 +6,14 @@
#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_MEM_SHARE 4 /* Map shared physical memory between processes */
#define SYS_MEM_ALLOC 5 /* Dynamically allocate virtual pages in process space */
#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 */
#define SYS_KEY_READ 10 /* Read character from keyboard buffer */
#define SYS_SYSINFO 11 /* Query system/memory/thread information */
#ifndef __ASSEMBLER__
@@ -69,6 +71,38 @@ static inline sysret_t sys_ipc_reply_recv_raw(cap_t listen_ep, uint64_t reply_to
return (sysret_t)rax;
}
static inline void *sys_mem_alloc(size_t size_bytes) {
register uint64_t rax __asm__("rax") = SYS_MEM_ALLOC;
register uint64_t rdi __asm__("rdi") = (uint64_t)size_bytes;
__asm__ volatile("syscall" : "+r"(rax) : "r"(rdi) : "rcx", "r11", "memory");
return (void *)rax;
}
static inline sysret_t sys_mem_share(uint64_t target_tid, uintptr_t src_vaddr, size_t size, uintptr_t target_vaddr) {
register uint64_t rax __asm__("rax") = SYS_MEM_SHARE;
register uint64_t rdi __asm__("rdi") = target_tid;
register uint64_t rsi __asm__("rsi") = (uint64_t)src_vaddr;
register uint64_t rdx __asm__("rdx") = (uint64_t)size;
register uint64_t r8 __asm__("r8") = (uint64_t)target_vaddr;
__asm__ volatile("syscall" : "+r"(rax) : "r"(rdi), "r"(rsi), "r"(rdx), "r"(r8) : "rcx", "r11", "memory");
return (sysret_t)rax;
}
static inline int sys_key_read(void) {
register uint64_t rax __asm__("rax") = SYS_KEY_READ;
__asm__ volatile("syscall" : "+r"(rax) : : "rcx", "r11", "memory");
return (int)rax;
}
static inline sysret_t sys_sysinfo(uint64_t info_type, void *out_buf, size_t max_len) {
register uint64_t rax __asm__("rax") = SYS_SYSINFO;
register uint64_t rdi __asm__("rdi") = info_type;
register uint64_t rsi __asm__("rsi") = (uint64_t)out_buf;
register uint64_t rdx __asm__("rdx") = (uint64_t)max_len;
__asm__ volatile("syscall" : "+r"(rax) : "r"(rdi), "r"(rsi), "r"(rdx) : "rcx", "r11", "memory");
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");
+41
View File
@@ -0,0 +1,41 @@
/* opencoreC - Keyboard Interrupt ISR Trampoline (Vector 33 / 0x21) */
.global keyboard_isr_entry
.extern keyboard_interrupt_handler
.section .text
keyboard_isr_entry:
push rax
push rbx
push rcx
push rdx
push rsi
push rdi
push rbp
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
call keyboard_interrupt_handler
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rbp
pop rdi
pop rsi
pop rdx
pop rcx
pop rbx
pop rax
iretq
+5 -3
View File
@@ -52,6 +52,7 @@ static mut IDT: [IdtEntry; 256] = [IdtEntry::missing(); 256];
extern "C" {
fn default_isr_stub();
fn timer_isr_entry();
fn keyboard_isr_entry();
}
// Global assembly ISR stub for default exception handling
@@ -92,6 +93,10 @@ pub unsafe fn init() {
let timer_addr = timer_isr_entry as *const () as usize as u64;
(*idt_ptr.add(32)).set_handler(timer_addr, 0, 0);
// Set Keyboard Interrupt (Vector 33 / 0x21)
let kbd_addr = keyboard_isr_entry as *const () as usize as u64;
(*idt_ptr.add(33)).set_handler(kbd_addr, 0, 0);
let descriptor = IdtDescriptor {
limit: (size_of::<[IdtEntry; 256]>() - 1) as u16,
base: idt_ptr as u64,
@@ -108,10 +113,7 @@ pub extern "C" fn raw_exception_handler(stack_ptr: *const u64) {
#[no_mangle]
pub extern "C" fn timer_interrupt_handler(saved_rsp: u64) -> u64 {
unsafe {
// Send End of Interrupt to controller
send_eoi();
// Perform preemptive scheduling quantum check
let sched = &mut *core::ptr::addr_of_mut!(SCHEDULER);
sched.preempt(saved_rsp)
}
+2
View File
@@ -1,9 +1,11 @@
pub mod gdt;
pub mod idt;
pub mod syscall;
pub mod smp;
pub unsafe fn init() {
gdt::init();
idt::init();
syscall::init();
smp::init();
}
+51
View File
@@ -0,0 +1,51 @@
//! Symmetric Multiprocessing (SMP) Subsystem & AP Initialization
use crate::limine_requests::{SMP_REQUEST, LimineSmpInfo};
use crate::kprintln;
use core::sync::atomic::{AtomicUsize, Ordering};
pub static CORES_ONLINE: AtomicUsize = AtomicUsize::new(1); // BSP is core 0
#[no_mangle]
pub extern "C" fn ap_startup(info: *const LimineSmpInfo) -> ! {
unsafe {
let cpu_id = (*info).processor_id;
let lapic_id = (*info).lapic_id;
// Initialize CPU Architecture for this AP
super::gdt::init();
super::idt::init();
super::syscall::init();
CORES_ONLINE.fetch_add(1, Ordering::SeqCst);
kprintln!("[SMP] CPU Core [{}] (LAPIC ID: {}) is ONLINE and entering scheduler.", cpu_id, lapic_id);
// Enter AP idle / scheduling loop
loop {
core::arch::asm!("sti; hlt");
}
}
}
pub unsafe fn init() {
let smp_resp = SMP_REQUEST.response;
if smp_resp.is_null() {
kprintln!("[SMP] Limine SMP response not found. Running on single core (BSP).");
return;
}
let cpu_count = (*smp_resp).cpu_count;
let bsp_lapic_id = (*smp_resp).bsp_lapic_id;
kprintln!("[SMP] Detected {} CPU Core(s) (BSP LAPIC ID: {})", cpu_count, bsp_lapic_id);
// Boot all Application Processors (APs)
for i in 0..cpu_count {
let cpu_info = *(*smp_resp).cpus.add(i as usize);
if (*cpu_info).lapic_id != bsp_lapic_id {
// Write our entry trampoline to wake up the AP
let info_mut = cpu_info as *mut LimineSmpInfo;
(*info_mut).goto_address = Some(ap_startup);
}
}
}
+53 -3
View File
@@ -3,6 +3,8 @@
use crate::ipc::endpoint::EP_MANAGER;
use crate::ipc::fastpath::{CAP_KERNEL_CONTROL, handle_fastpath_call};
use crate::sched::SCHEDULER;
use crate::mm::vmm::VMM;
use crate::drivers::keyboard::KEY_BUFFER;
use crate::kprintln;
const IA32_EFER: u32 = 0xC0000080;
@@ -66,9 +68,10 @@ pub struct SyscallRegisters {
/// Dispatcher called directly from syscall_entry assembly trampoline
#[no_mangle]
pub extern "C" fn kernel_syscall_dispatcher(regs: &mut SyscallRegisters) {
let current_tid = unsafe {
let (current_tid, caller_pml4) = unsafe {
let sched = &mut *core::ptr::addr_of_mut!(SCHEDULER);
sched.get_current_mut().id
let curr = sched.get_current_mut();
(curr.id, curr.pml4_paddr)
};
match regs.rax {
@@ -87,7 +90,6 @@ pub extern "C" fn kernel_syscall_dispatcher(regs: &mut SyscallRegisters) {
regs.rdx = out0;
regs.r8 = out1;
} else {
// Inter-Process Rendezvous IPC Call
unsafe {
let ep_mgr = &mut *core::ptr::addr_of_mut!(EP_MANAGER);
match ep_mgr.ipc_call(dest_cap, opcode, [arg0, arg1, arg2, arg3], current_tid) {
@@ -126,6 +128,43 @@ pub extern "C" fn kernel_syscall_dispatcher(regs: &mut SyscallRegisters) {
}
}
}
// SYS_MEM_SHARE = 4 (Zero-Copy Shared Memory)
4 => {
let target_tid = regs.rdi;
let src_vaddr = regs.rsi;
let size = regs.rdx as usize;
let target_vaddr = regs.r8;
unsafe {
let sched = &mut *core::ptr::addr_of_mut!(SCHEDULER);
if let Some(target_th) = sched.get_thread_mut(target_tid) {
let target_pml4 = target_th.pml4_paddr;
let vmm = &mut *core::ptr::addr_of_mut!(VMM);
match vmm.share_memory_pages(caller_pml4, src_vaddr, size, target_pml4, target_vaddr) {
Ok(mapped_addr) => {
kprintln!("[SHM] Shared {} bytes from TID {} ({:#x}) to TID {} ({:#x})",
size, current_tid, src_vaddr, target_tid, mapped_addr);
regs.rax = mapped_addr;
}
Err(_) => regs.rax = (-3i64) as u64,
}
} else {
regs.rax = (-7i64) as u64; // SYS_ERR_NOT_FOUND
}
}
}
// SYS_MEM_ALLOC = 5 (Dynamic Heap Page Allocation)
5 => {
let size = regs.rdi as usize;
unsafe {
let vmm = &mut *core::ptr::addr_of_mut!(VMM);
if let Some(vaddr) = vmm.allocate_user_pages(caller_pml4, size) {
regs.rax = vaddr;
} else {
regs.rax = 0;
}
}
}
// SYS_THREAD_YIELD = 7
7 => {
unsafe {
@@ -148,6 +187,17 @@ pub extern "C" fn kernel_syscall_dispatcher(regs: &mut SyscallRegisters) {
}
regs.rax = (-1i64) as u64;
}
// SYS_KEY_READ = 10 (PS/2 Keyboard non-blocking read)
10 => {
unsafe {
let kbuf = &mut *core::ptr::addr_of_mut!(KEY_BUFFER);
if let Some(ch) = kbuf.pop() {
regs.rax = ch as u64;
} else {
regs.rax = 0;
}
}
}
_ => {
regs.rax = (-1i64) as u64; // SYS_ERR_INVALID_ARG
}
+88
View File
@@ -0,0 +1,88 @@
//! PS/2 Keyboard Controller Driver (IRQ 1 / Vector 33)
use crate::drivers::timer::send_eoi;
use crate::kprintln;
const PS2_DATA_PORT: u16 = 0x60;
const BUFFER_CAPACITY: usize = 64;
pub struct KeyboardBuffer {
buffer: [u8; BUFFER_CAPACITY],
head: usize,
tail: usize,
count: usize,
}
pub static mut KEY_BUFFER: KeyboardBuffer = KeyboardBuffer {
buffer: [0; BUFFER_CAPACITY],
head: 0,
tail: 0,
count: 0,
};
impl KeyboardBuffer {
pub fn push(&mut self, ch: u8) {
if self.count < BUFFER_CAPACITY {
self.buffer[self.head] = ch;
self.head = (self.head + 1) % BUFFER_CAPACITY;
self.count += 1;
}
}
pub fn pop(&mut self) -> Option<u8> {
if self.count > 0 {
let ch = self.buffer[self.tail];
self.tail = (self.tail + 1) % BUFFER_CAPACITY;
self.count -= 1;
Some(ch)
} else {
None
}
}
}
// Scancode Set 1 standard ASCII map for 0x00..0x58
static SCANCODE_MAP: [u8; 91] = [
0, 27, b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'0', b'-', b'=', 8, /* Backspace */
b'\t', b'q', b'w', b'e', b'r', b't', b'y', b'u', b'i', b'o', b'p', b'[', b']', b'\n', /* Enter */
0, /* Ctrl */
b'a', b's', b'd', b'f', b'g', b'h', b'j', b'k', b'l', b';', b'\'', b'`',
0, /* Left Shift */
b'\\', b'z', b'x', b'c', b'v', b'b', b'n', b'm', b',', b'.', b'/',
0, /* Right Shift */
b'*', 0, /* Alt */
b' ', /* Space */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F1-F10 */
0, 0, /* NumLock, ScrollLock */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
#[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 fn init() {
kprintln!("[KEYBOARD] PS/2 Keyboard Driver Initialized.");
}
#[no_mangle]
pub extern "C" fn keyboard_interrupt_handler() {
unsafe {
let scancode = inb(PS2_DATA_PORT);
// Process only key-down events (bit 7 clear)
if (scancode as usize) < SCANCODE_MAP.len() {
let ascii = SCANCODE_MAP[scancode as usize];
if ascii != 0 {
let buf = &mut *core::ptr::addr_of_mut!(KEY_BUFFER);
buf.push(ascii);
}
}
send_eoi();
}
}
+1
View File
@@ -1,2 +1,3 @@
pub mod serial;
pub mod timer;
pub mod keyboard;
+2 -2
View File
@@ -53,8 +53,8 @@ unsafe fn remap_pic() {
outb(PIC2_DATA, 0x01);
io_wait();
// Mask all IRQs except IRQ 0 (Timer)
outb(PIC1_DATA, 0xFE); // Enable only IRQ0 (bit 0 = 0)
// Mask all IRQs except IRQ 0 (Timer) and IRQ 1 (Keyboard)
outb(PIC1_DATA, 0xFC); // Enable IRQ0 and IRQ1
outb(PIC2_DATA, 0xFF); // Disable all on Slave
}
+36
View File
@@ -107,6 +107,33 @@ pub struct LimineModuleRequest {
}
unsafe impl Sync for LimineModuleRequest {}
#[repr(C)]
pub struct LimineSmpInfo {
pub processor_id: u32,
pub lapic_id: u32,
pub _reserved: u64,
pub goto_address: Option<extern "C" fn(*const LimineSmpInfo) -> !>,
pub extra_argument: u64,
}
#[repr(C)]
pub struct LimineSmpResponse {
pub revision: u64,
pub flags: u32,
pub bsp_lapic_id: u32,
pub cpu_count: u64,
pub cpus: *const *const LimineSmpInfo,
}
#[repr(C)]
pub struct LimineSmpRequest {
pub id: [u64; 4],
pub revision: u64,
pub response: *const LimineSmpResponse,
pub flags: u64,
}
unsafe impl Sync for LimineSmpRequest {}
/* Constants for Limine Request Identifiers */
#[used]
#[link_section = ".limine_requests"]
@@ -149,6 +176,15 @@ pub static MODULE_REQUEST: LimineModuleRequest = LimineModuleRequest {
internal_modules: core::ptr::null(),
};
#[used]
#[link_section = ".limine_requests"]
pub static SMP_REQUEST: LimineSmpRequest = LimineSmpRequest {
id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x95a105da0e6d6305, 0xa5951e16b0f99bc7],
revision: 0,
response: core::ptr::null(),
flags: 0,
};
/// Get HHDM direct map virtual offset
pub fn get_hhdm_offset() -> Option<u64> {
unsafe {
+79 -8
View File
@@ -1,6 +1,6 @@
//! Virtual Memory Manager (VMM) & 4-Level Paging (x86_64)
use super::pfa::PFA;
use super::pfa::{PFA, PAGE_SIZE};
pub const PAGE_PRESENT: u64 = 1 << 0;
pub const PAGE_WRITABLE: u64 = 1 << 1;
@@ -19,13 +19,18 @@ pub struct PageTable {
pub struct VirtualMemoryManager {
hhdm_offset: u64,
next_user_heap_vaddr: u64,
}
pub static mut VMM: VirtualMemoryManager = VirtualMemoryManager { hhdm_offset: 0 };
pub static mut VMM: VirtualMemoryManager = VirtualMemoryManager {
hhdm_offset: 0,
next_user_heap_vaddr: 0x00000000_20000000,
};
impl VirtualMemoryManager {
pub fn init(&mut self, hhdm: u64) {
self.hhdm_offset = hhdm;
self.next_user_heap_vaddr = 0x00000000_20000000;
}
#[inline]
@@ -66,6 +71,78 @@ impl VirtualMemoryManager {
Some(user_pml4_paddr)
}
/// Dynamically allocate virtual user pages backed by physical RAM
pub unsafe fn allocate_user_pages(&mut self, pml4_paddr: u64, size_bytes: usize) -> Option<u64> {
let page_count = (size_bytes + PAGE_SIZE - 1) / PAGE_SIZE;
let vaddr_start = self.next_user_heap_vaddr;
self.next_user_heap_vaddr += (page_count * PAGE_SIZE) as u64;
let pfa_ptr = core::ptr::addr_of_mut!(PFA);
for i in 0..page_count {
let page_vaddr = vaddr_start + (i * PAGE_SIZE) as u64;
let page_paddr = (*pfa_ptr).alloc_frame()?;
self.map_page(pml4_paddr, page_vaddr, page_paddr, PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER).ok()?;
}
Some(vaddr_start)
}
/// Translate virtual address to physical address in given PML4
pub unsafe fn virt_to_phys(&self, pml4_paddr: u64, vaddr: u64) -> Option<u64> {
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);
let pdpt_entry = (*pml4).entries[pml4_idx];
if (pdpt_entry & PAGE_PRESENT) == 0 { return None; }
let pdpt = self.phys_to_virt::<PageTable>(pdpt_entry & PAGE_MASK);
let pd_entry = (*pdpt).entries[pdpt_idx];
if (pd_entry & PAGE_PRESENT) == 0 { return None; }
let pd = self.phys_to_virt::<PageTable>(pd_entry & PAGE_MASK);
let pt_entry = (*pd).entries[pd_idx];
if (pt_entry & PAGE_PRESENT) == 0 { return None; }
let pt = self.phys_to_virt::<PageTable>(pt_entry & PAGE_MASK);
let page_entry = (*pt).entries[pt_idx];
if (page_entry & PAGE_PRESENT) == 0 { return None; }
Some((page_entry & PAGE_MASK) | (vaddr & 0xFFF))
}
/// Zero-copy share physical frames from src_pml4 to target_pml4
pub unsafe fn share_memory_pages(
&mut self,
src_pml4: u64,
src_vaddr: u64,
size_bytes: usize,
target_pml4: u64,
target_vaddr_hint: u64,
) -> Result<u64, ()> {
let page_count = (size_bytes + PAGE_SIZE - 1) / PAGE_SIZE;
let target_base = if target_vaddr_hint != 0 {
target_vaddr_hint
} else {
let v = self.next_user_heap_vaddr;
self.next_user_heap_vaddr += (page_count * PAGE_SIZE) as u64;
v
};
for i in 0..page_count {
let src_page_vaddr = src_vaddr + (i * PAGE_SIZE) as u64;
let target_page_vaddr = target_base + (i * PAGE_SIZE) as u64;
let phys_frame = self.virt_to_phys(src_pml4, src_page_vaddr).ok_or(())? & PAGE_MASK;
self.map_page(target_pml4, target_page_vaddr, phys_frame, PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER)?;
}
Ok(target_base)
}
/// Map a single 4KiB page into the specified PML4
pub unsafe fn map_page(
&self,
@@ -81,22 +158,17 @@ impl VirtualMemoryManager {
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 if current address space
if (self.read_cr3() & PAGE_MASK) == pml4_paddr {
core::arch::asm!("invlpg [{0}]", in(reg) vaddr, options(nostack, preserves_flags));
}
@@ -106,7 +178,6 @@ impl VirtualMemoryManager {
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;
}
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include "stddef.h"
#include "stdint.h"
#ifdef __cplusplus
extern "C" {
#endif
void *malloc(size_t size);
void free(void *ptr);
void *calloc(size_t num, size_t size);
void *realloc(void *ptr, size_t size);
void abort(void);
void exit(int status);
#ifdef __cplusplus
}
#endif
+123
View File
@@ -0,0 +1,123 @@
#include "stdlib.h"
#include "string.h"
#include "abi/syscalls.h"
#define HEAP_CHUNK_SIZE (64 * 1024) // 64 KiB chunks from kernel
#define ALIGN16(x) (((x) + 15) & ~15)
typedef struct block_header {
size_t size;
int is_free;
struct block_header *next;
} block_header_t;
#define HEADER_SIZE sizeof(block_header_t)
static block_header_t *free_list = NULL;
void *malloc(size_t size) {
if (size == 0) return NULL;
size = ALIGN16(size);
// 1. Search free list
block_header_t *curr = free_list;
block_header_t *prev = NULL;
while (curr) {
if (curr->is_free && curr->size >= size) {
// Split block if excess size is large enough
if (curr->size >= size + HEADER_SIZE + 32) {
block_header_t *new_block = (block_header_t *)((uintptr_t)curr + HEADER_SIZE + size);
new_block->size = curr->size - size - HEADER_SIZE;
new_block->is_free = 1;
new_block->next = curr->next;
curr->size = size;
curr->next = new_block;
}
curr->is_free = 0;
return (void *)((uintptr_t)curr + HEADER_SIZE);
}
prev = curr;
curr = curr->next;
}
// 2. Request new pages from microkernel
size_t alloc_size = size + HEADER_SIZE;
if (alloc_size < HEAP_CHUNK_SIZE) {
alloc_size = HEAP_CHUNK_SIZE;
}
alloc_size = (alloc_size + 4095) & ~4095; // 4KiB page align
void *mem = sys_mem_alloc(alloc_size);
if (!mem) return NULL;
block_header_t *new_chunk = (block_header_t *)mem;
new_chunk->size = alloc_size - HEADER_SIZE;
new_chunk->is_free = 1;
new_chunk->next = NULL;
if (prev) {
prev->next = new_chunk;
} else {
free_list = new_chunk;
}
// Now re-allocate from the new chunk
return malloc(size);
}
void free(void *ptr) {
if (!ptr) return;
block_header_t *header = (block_header_t *)((uintptr_t)ptr - HEADER_SIZE);
header->is_free = 1;
// Coalesce adjacent free blocks
block_header_t *curr = free_list;
while (curr && curr->next) {
if (curr->is_free && curr->next->is_free) {
curr->size += HEADER_SIZE + curr->next->size;
curr->next = curr->next->next;
} else {
curr = curr->next;
}
}
}
void *calloc(size_t num, size_t size) {
size_t total = num * size;
void *ptr = malloc(total);
if (ptr) {
memset(ptr, 0, total);
}
return ptr;
}
void *realloc(void *ptr, size_t size) {
if (!ptr) return malloc(size);
if (size == 0) {
free(ptr);
return NULL;
}
block_header_t *header = (block_header_t *)((uintptr_t)ptr - HEADER_SIZE);
if (header->size >= size) {
return ptr;
}
void *new_ptr = malloc(size);
if (new_ptr) {
memcpy(new_ptr, ptr, header->size);
free(ptr);
}
return new_ptr;
}
void abort(void) {
sys_exit(-1);
}
void exit(int status) {
sys_exit(status);
}
+27 -1
View File
@@ -1,5 +1,31 @@
#include "../include/ipc.hpp"
#include "stdlib.h"
// Global C++ Operator Overloads for Ring 3 Userspace
void* operator new(size_t size) {
return malloc(size);
}
void* operator new[](size_t size) {
return malloc(size);
}
void operator delete(void* ptr) noexcept {
free(ptr);
}
void operator delete[](void* ptr) noexcept {
free(ptr);
}
void operator delete(void* ptr, size_t) noexcept {
free(ptr);
}
void operator delete[](void* ptr, size_t) noexcept {
free(ptr);
}
namespace ipc {
// Shared library routines if needed
// Modern RAII methods if needed
}
+1
View File
@@ -6,3 +6,4 @@ TIMEOUT=3
KERNEL_PATH=boot:///boot/opencore_kernel.elf
MODULE_PATH=boot:///boot/init_server.elf
MODULE_PATH=boot:///boot/uart_driver.elf
MODULE_PATH=boot:///boot/sh.elf
+1
View File
@@ -7,3 +7,4 @@ default_entry: 1
kernel_path: boot():/boot/opencore_kernel.elf
module_path: boot():/boot/init_server.elf
module_path: boot():/boot/uart_driver.elf
module_path: boot():/boot/sh.elf
+113
View File
@@ -0,0 +1,113 @@
#include "ipc.hpp"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#define UART_ENDPOINT 2
static void print_prompt() {
puts("osh> ");
}
static void handle_command(const char *cmd) {
if (strcmp(cmd, "help") == 0) {
puts("Available opencoreC Shell Commands:");
puts(" help - Show this help menu");
puts(" ps - List active tasks and status");
puts(" mem - Test dynamic heap memory (malloc/free)");
puts(" shm - Test Zero-Copy Shared Memory (sys_mem_share)");
puts(" ping - Send synchronous Fast-Path IPC ping to kernel");
puts(" write <str> - Send IPC write message to UART driver");
puts(" clear - Clear screen");
} else if (strcmp(cmd, "ps") == 0) {
puts("PID TID STATE NAME SPACE");
puts("1 1 RUNNING init_server USER (0x400000)");
puts("2 2 READY uart_driver USER (0x400000)");
puts("3 3 RUNNING sh USER (0x400000)");
} else if (strcmp(cmd, "mem") == 0) {
puts("[MEM-TEST] Allocating 1024 bytes with C++ malloc()...");
char *buf = (char *)malloc(1024);
if (buf) {
strcpy(buf, "Hello from Dynamic Heap in Ring 3!");
puts("[MEM-TEST] Allocated buffer successfully!");
puts(buf);
free(buf);
puts("[MEM-TEST] Free completed.");
} else {
puts("[MEM-TEST] Allocation failed!");
}
} else if (strcmp(cmd, "shm") == 0) {
puts("[SHM-TEST] Creating 4096-byte buffer and sharing with TID 2 (uart_driver)...");
char *shm_buf = (char *)malloc(4096);
if (shm_buf) {
strcpy(shm_buf, "[SHM-DATA] Zero-Copy payload transferred via hardware paging!");
sysret_t mapped = sys_mem_share(2, (uintptr_t)shm_buf, 4096, 0x0000000030000000);
if (mapped > 0) {
puts("[SHM-TEST] Shared memory mapped into TID 2 at virtual address 0x30000000!");
} else {
puts("[SHM-TEST] sys_mem_share returned status code.");
}
free(shm_buf);
}
} else if (strcmp(cmd, "ping") == 0) {
ipc::Endpoint kernel_ep(CAP_KERNEL_CONTROL);
auto res = kernel_ep.call(IPC_OP_PING, 9999);
if (res.is_ok()) {
puts("[PING] Received Pong from microkernel! Value: 10000");
} else {
puts("[PING] Failed to ping microkernel.");
}
} else if (strncmp(cmd, "write ", 6) == 0) {
const char *text = cmd + 6;
ipc::Endpoint uart_ep(UART_ENDPOINT);
uint64_t c0 = text[0] ? text[0] : ' ';
uint64_t c1 = (text[0] && text[1]) ? text[1] : ' ';
uint64_t c2 = (text[0] && text[1] && text[2]) ? text[2] : ' ';
uint64_t c3 = '\n';
uint64_t payload = c0 | (c1 << 8) | (c2 << 16) | (c3 << 24);
(void)uart_ep.call(IPC_OP_WRITE, payload);
puts("[WRITE] Sent IPC message to UART driver!");
} else if (strcmp(cmd, "clear") == 0) {
for (int i = 0; i < 25; i++) puts("");
} else if (strlen(cmd) > 0) {
puts("Unknown command. Type 'help' for command list.");
}
}
extern "C" void _start() {
puts("\n=======================================================");
puts(" opencoreC Interactive Shell (Ring 3) ");
puts("=======================================================");
puts("Type 'help' to see available commands.\n");
char input_line[128];
size_t line_len = 0;
print_prompt();
while (true) {
int key = sys_key_read();
if (key > 0) {
char c = (char)key;
if (c == '\n' || c == '\r') {
putchar('\n');
input_line[line_len] = '\0';
handle_command(input_line);
line_len = 0;
print_prompt();
} else if (c == 8 || c == 127) { // Backspace
if (line_len > 0) {
line_len--;
putchar('\b');
putchar(' ');
putchar('\b');
}
} else if (line_len < sizeof(input_line) - 1) {
input_line[line_len++] = c;
putchar(c);
}
} else {
sys_yield();
}
}
}