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
+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;
}