feat: implement PS/2 keyboard & shell, dynamic heap (malloc/new), Zero-Copy SHM, and SMP multicore initialization
This commit is contained in:
@@ -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
@@ -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");
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,2 +1,3 @@
|
||||
pub mod serial;
|
||||
pub mod timer;
|
||||
pub mod keyboard;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user