fix: bugs
This commit is contained in:
@@ -7,25 +7,29 @@ pub static mut IDT: crate::cpu::idt::InterruptDescriptorTable = crate::cpu::idt:
|
||||
|
||||
pub const TLB_SHOOTDOWN_VECTOR: u8 = 0xFD;
|
||||
|
||||
global_asm!(
|
||||
".global page_fault_stub",
|
||||
"page_fault_stub:",
|
||||
"push rax",
|
||||
"push rcx",
|
||||
"push rdx",
|
||||
"push rbx",
|
||||
"push rbp",
|
||||
"push rsi",
|
||||
"push rdi",
|
||||
"push r8",
|
||||
"push r9",
|
||||
"push r10",
|
||||
"push r11",
|
||||
"push r12",
|
||||
"push r13",
|
||||
"push r14",
|
||||
"push r15",
|
||||
macro_rules! exception_stub {
|
||||
($name:ident, $handler:ident) => {
|
||||
concat!(
|
||||
".global ", stringify!($name), "\n",
|
||||
stringify!($name), ":\n",
|
||||
"push rax\npush rcx\npush rdx\npush rbx\npush rbp\n",
|
||||
"push rsi\npush rdi\npush r8\npush r9\npush r10\n",
|
||||
"push r11\npush r12\npush r13\npush r14\npush r15\n",
|
||||
"mov rdi, [rsp + 15*8]\n",
|
||||
"call ", stringify!($handler), "\n",
|
||||
"pop r15\npop r14\npop r13\npop r12\npop r11\n",
|
||||
"pop r10\npop r9\npop r8\npop rdi\npop rsi\n",
|
||||
"pop rbp\npop rbx\npop rdx\npop rcx\npop rax\n",
|
||||
"add rsp, 8\n",
|
||||
"iretq\n",
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
global_asm!(
|
||||
exception_stub!(page_fault_stub, rust_page_fault_handler),
|
||||
exception_stub!(gpf_stub, rust_gpf_handler),
|
||||
exception_stub!(double_fault_stub, rust_double_fault_handler),
|
||||
".global tlb_shootdown_stub",
|
||||
"tlb_shootdown_stub:",
|
||||
"push rax",
|
||||
@@ -43,12 +47,7 @@ global_asm!(
|
||||
"push r13",
|
||||
"push r14",
|
||||
"push r15",
|
||||
|
||||
"call rust_tlb_shootdown_handler",
|
||||
|
||||
"mov rdi, [rsp + 15*8]",
|
||||
"call rust_page_fault_handler",
|
||||
|
||||
"pop r15",
|
||||
"pop r14",
|
||||
"pop r13",
|
||||
@@ -64,36 +63,202 @@ global_asm!(
|
||||
"pop rdx",
|
||||
"pop rcx",
|
||||
"pop rax",
|
||||
|
||||
"add rsp, 8",
|
||||
"iretq"
|
||||
);
|
||||
|
||||
unsafe extern "C" {
|
||||
fn page_fault_stub();
|
||||
fn gpf_stub();
|
||||
fn double_fault_stub();
|
||||
fn tlb_shootdown_stub();
|
||||
}
|
||||
|
||||
pub fn init_idt() {
|
||||
pub fn init_early_exceptions() {
|
||||
unsafe {
|
||||
let idt_mut_ptr = core::ptr::addr_of_mut!(IDT);
|
||||
(*idt_mut_ptr).set_handler(14, page_fault_stub as u64);
|
||||
let idt = core::ptr::addr_of_mut!(IDT);
|
||||
let early: [u64; 32] = [
|
||||
early_handler_0 as u64, early_handler_1 as u64,
|
||||
early_handler_2 as u64, early_handler_3 as u64,
|
||||
early_handler_4 as u64, early_handler_5 as u64,
|
||||
early_handler_6 as u64, early_handler_7 as u64,
|
||||
early_handler_8 as u64, early_handler_9 as u64,
|
||||
early_handler_10 as u64, early_handler_11 as u64,
|
||||
early_handler_12 as u64, early_handler_13 as u64,
|
||||
early_handler_14 as u64, early_handler_15 as u64,
|
||||
early_handler_16 as u64, early_handler_17 as u64,
|
||||
early_handler_18 as u64, early_handler_19 as u64,
|
||||
early_handler_20 as u64, early_handler_21 as u64,
|
||||
early_handler_22 as u64, early_handler_23 as u64,
|
||||
early_handler_24 as u64, early_handler_25 as u64,
|
||||
early_handler_26 as u64, early_handler_27 as u64,
|
||||
early_handler_28 as u64, early_handler_29 as u64,
|
||||
early_handler_30 as u64, early_handler_31 as u64,
|
||||
];
|
||||
for (v, &handler) in early.iter().enumerate() {
|
||||
(*idt).set_handler(v as u8, handler);
|
||||
}
|
||||
// Override with proper handlers for vectors 8, 13, 14 and TLB IPI.
|
||||
(*idt).set_handler(8, double_fault_stub as u64);
|
||||
(*idt).set_handler(13, gpf_stub as u64);
|
||||
(*idt).set_handler(14, page_fault_stub as u64);
|
||||
(*idt).set_handler(TLB_SHOOTDOWN_VECTOR, tlb_shootdown_stub as u64);
|
||||
|
||||
(*idt_mut_ptr).set_handler(TLB_SHOOTDOWN_VECTOR, tlb_shootdown_stub as u64);
|
||||
|
||||
let idt_static_ref: &'static crate::cpu::idt::InterruptDescriptorTable = &*core::ptr::addr_of!(IDT);
|
||||
idt_static_ref.load();
|
||||
let ptr: &'static crate::cpu::idt::InterruptDescriptorTable = &*core::ptr::addr_of!(IDT);
|
||||
ptr.load();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn process_deferred_mmu_events() {
|
||||
let mut vmm_guard = KERNEL_SPACE.lock();
|
||||
if let Some(space) = vmm_guard.as_mut() {
|
||||
space.process_pending_revocations();
|
||||
pub fn init_idt() {
|
||||
// init_early_exceptions already loaded the IDT; this just overrides
|
||||
// vectors that the full kernel needs. Calling lidt again is harmless.
|
||||
unsafe {
|
||||
let idt = core::ptr::addr_of_mut!(IDT);
|
||||
(*idt).set_handler(14, page_fault_stub as u64);
|
||||
(*idt).set_handler(TLB_SHOOTDOWN_VECTOR, tlb_shootdown_stub as u64);
|
||||
let ptr: &'static crate::cpu::idt::InterruptDescriptorTable = &*core::ptr::addr_of!(IDT);
|
||||
ptr.load();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn rust_gpf_handler(error_code: u64) -> ! {
|
||||
crate::debug::serial::write_global(format_args!(
|
||||
"\n!!! GENERAL PROTECTION FAULT !!! error_code={:#x}\n\
|
||||
CPU halted.\n",
|
||||
error_code
|
||||
));
|
||||
loop {
|
||||
unsafe { asm!("cli; hlt", options(nomem, nostack, preserves_flags)); }
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn rust_double_fault_handler(error_code: u64) -> ! {
|
||||
crate::debug::serial::write_global(format_args!(
|
||||
"\n!!! DOUBLE FAULT !!! error_code={:#x}\n\
|
||||
CPU halted.\n",
|
||||
error_code
|
||||
));
|
||||
loop {
|
||||
unsafe { asm!("cli; hlt", options(nomem, nostack, preserves_flags)); }
|
||||
}
|
||||
}
|
||||
|
||||
global_asm!(r#"
|
||||
.altmacro
|
||||
|
||||
.macro early_stub vec
|
||||
.globl early_handler_\vec
|
||||
.balign 16
|
||||
early_handler_\vec:
|
||||
push 0 /* dummy error code */
|
||||
push \vec /* vector number */
|
||||
jmp early_common
|
||||
.endm
|
||||
|
||||
early_stub 0
|
||||
early_stub 1
|
||||
early_stub 2
|
||||
early_stub 3
|
||||
early_stub 4
|
||||
early_stub 5
|
||||
early_stub 6
|
||||
early_stub 7
|
||||
early_stub 8
|
||||
early_stub 9
|
||||
early_stub 10
|
||||
early_stub 11
|
||||
early_stub 12
|
||||
early_stub 13
|
||||
early_stub 14
|
||||
early_stub 15
|
||||
early_stub 16
|
||||
early_stub 17
|
||||
early_stub 18
|
||||
early_stub 19
|
||||
early_stub 20
|
||||
early_stub 21
|
||||
early_stub 22
|
||||
early_stub 23
|
||||
early_stub 24
|
||||
early_stub 25
|
||||
early_stub 26
|
||||
early_stub 27
|
||||
early_stub 28
|
||||
early_stub 29
|
||||
early_stub 30
|
||||
early_stub 31
|
||||
|
||||
early_common:
|
||||
push rax
|
||||
push rcx
|
||||
push rdx
|
||||
push rbx
|
||||
push rbp
|
||||
push rsi
|
||||
push rdi
|
||||
push r8
|
||||
push r9
|
||||
push r10
|
||||
push r11
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
push r15
|
||||
mov rdi, [rsp + 15*8] /* vector number */
|
||||
mov rsi, [rsp + 16*8] /* error code (or dummy 0) */
|
||||
call rust_early_exception_handler
|
||||
/* never returns */
|
||||
"#);
|
||||
|
||||
unsafe extern "C" {
|
||||
fn early_handler_0();
|
||||
fn early_handler_1();
|
||||
fn early_handler_2();
|
||||
fn early_handler_3();
|
||||
fn early_handler_4();
|
||||
fn early_handler_5();
|
||||
fn early_handler_6();
|
||||
fn early_handler_7();
|
||||
fn early_handler_8();
|
||||
fn early_handler_9();
|
||||
fn early_handler_10();
|
||||
fn early_handler_11();
|
||||
fn early_handler_12();
|
||||
fn early_handler_13();
|
||||
fn early_handler_14();
|
||||
fn early_handler_15();
|
||||
fn early_handler_16();
|
||||
fn early_handler_17();
|
||||
fn early_handler_18();
|
||||
fn early_handler_19();
|
||||
fn early_handler_20();
|
||||
fn early_handler_21();
|
||||
fn early_handler_22();
|
||||
fn early_handler_23();
|
||||
fn early_handler_24();
|
||||
fn early_handler_25();
|
||||
fn early_handler_26();
|
||||
fn early_handler_27();
|
||||
fn early_handler_28();
|
||||
fn early_handler_29();
|
||||
fn early_handler_30();
|
||||
fn early_handler_31();
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn rust_early_exception_handler(vector: u64, _error_code: u64) -> ! {
|
||||
crate::debug::serial::write_global(format_args!(
|
||||
"\n!!! EARLY EXCEPTION !!! vector={} error_code={:#x}\n\
|
||||
CPU halted.\n",
|
||||
vector, _error_code
|
||||
));
|
||||
loop {
|
||||
unsafe { asm!("cli; hlt", options(nomem, nostack, preserves_flags)); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn rust_page_fault_handler(error_code: u64) {
|
||||
let fault_addr: u64;
|
||||
@@ -105,13 +270,18 @@ pub extern "C" fn rust_page_fault_handler(error_code: u64) {
|
||||
let present = (error_code & 0x1) != 0;
|
||||
let virt_addr = VirtAddr(fault_addr);
|
||||
|
||||
process_deferred_mmu_events();
|
||||
|
||||
// Acquire KERNEL_SPACE lock once for both deferred events and fault handling.
|
||||
// TODO SMP: switch to RwLock so concurrent read-only faults are not
|
||||
// serialised. `handle_fault` is read-only for COW faults but mutates
|
||||
// the VMA tree on demand-paging — a lock-free VMA tree or per-region
|
||||
// locks would be ideal.
|
||||
let mut vmm_guard = KERNEL_SPACE.lock();
|
||||
|
||||
if let Some(space) = vmm_guard.as_mut() {
|
||||
space.process_pending_revocations();
|
||||
|
||||
match space.handle_fault(virt_addr, write) {
|
||||
Ok(_) => return,
|
||||
Ok(_) => {},
|
||||
Err(e) => {
|
||||
panic!(
|
||||
"KERNEL PANIC: Unprocessed failure of virtual memory (Page Fault)!\n\
|
||||
|
||||
@@ -36,15 +36,13 @@ macro_rules! log {
|
||||
let _ = writeln!($console, $($arg)*);
|
||||
|
||||
// Serial debug output
|
||||
let mut sp = unsafe { $crate::debug::serial::SerialPort::init() };
|
||||
let _ = writeln!(
|
||||
sp,
|
||||
"{}[{:>5}]\x1b[0m {:<8} | {}",
|
||||
$crate::debug::serial::write_global(format_args!(
|
||||
"{}[{:>5}]\x1b[0m {:<8} | {}\n",
|
||||
$level.serial_color_code(),
|
||||
"LOG",
|
||||
$module,
|
||||
format_args!($($arg)*)
|
||||
);
|
||||
));
|
||||
}};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use core::arch::asm;
|
||||
use core::fmt::Write;
|
||||
use crate::mem::allocator::Locked;
|
||||
|
||||
pub struct SerialPort(u16);
|
||||
|
||||
@@ -7,16 +9,14 @@ impl SerialPort {
|
||||
|
||||
pub unsafe fn init() -> Self {
|
||||
let port = Self::COM1;
|
||||
// В новых версиях Rust даже внутри unsafe fn
|
||||
// вызовы других unsafe функций требуют явного блока
|
||||
unsafe {
|
||||
outb(port + 1, 0x00); // Disable interrupts
|
||||
outb(port + 3, 0x80); // Enable DLAB
|
||||
outb(port + 0, 0x03); // Divisor 3 (38400 baud)
|
||||
outb(port + 1, 0x00);
|
||||
outb(port + 3, 0x03); // 8 bits, no parity, 1 stop bit
|
||||
outb(port + 2, 0xC7); // Enable FIFO
|
||||
outb(port + 4, 0x0B); // IRQs enabled, RTS/DSR set
|
||||
outb(port + 3, 0x80);
|
||||
outb(port + 0, 0x03);
|
||||
outb(port + 1, 0x00);
|
||||
outb(port + 3, 0x03);
|
||||
outb(port + 2, 0xC7);
|
||||
outb(port + 4, 0x0B);
|
||||
}
|
||||
SerialPort(port)
|
||||
}
|
||||
@@ -38,6 +38,20 @@ impl core::fmt::Write for SerialPort {
|
||||
}
|
||||
}
|
||||
|
||||
static SERIAL_PORT: Locked<Option<SerialPort>> = Locked::new(None);
|
||||
|
||||
pub fn init_global() {
|
||||
let mut guard = SERIAL_PORT.lock();
|
||||
*guard = Some(unsafe { SerialPort::init() });
|
||||
}
|
||||
|
||||
pub fn write_global(args: core::fmt::Arguments) {
|
||||
let mut guard = SERIAL_PORT.lock();
|
||||
if let Some(ref mut sp) = *guard {
|
||||
let _ = sp.write_fmt(args);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn outb(port: u16, val: u8) {
|
||||
unsafe {
|
||||
asm!("out dx, al", in("dx") port, in("al") val, options(nomem, nostack, preserves_flags));
|
||||
|
||||
@@ -180,18 +180,31 @@ unsafe extern "C" fn kmain() -> ! {
|
||||
let mut console = tty::Console::new(&fb, KERNEL_FONT);
|
||||
console.clear();
|
||||
|
||||
debug::serial::init_global();
|
||||
|
||||
cpu::interrupts::init_early_exceptions();
|
||||
info!(console, "BOOT", "LIS4 Kernel Starting...");
|
||||
|
||||
unsafe { mem::pmm::BitmapPMM::init(&mmap_res, hhdm_offset); }
|
||||
info!(console, "MEM", "Primary Physical Memory Manager (BitmapPMM) initialized.");
|
||||
|
||||
cpu::lapic::init(hhdm_offset);
|
||||
info!(console, "LAPIC", "Local APIC initialized.");
|
||||
|
||||
// debug: locate the free page
|
||||
info!(console, "BOOT", "alloc_frame...");
|
||||
let p4_phys = mem::pmm::alloc_frame().expect("OOM: Failed to allocate P4 table");
|
||||
let p4 = unsafe { &mut *p4_phys.to_virt(hhdm_offset).as_mut_ptr::<PageTable>() };
|
||||
info!(console, "BOOT", "alloc_frame ok: phys=0x{:x}", p4_phys.0);
|
||||
let virt = p4_phys.to_virt(hhdm_offset);
|
||||
info!(console, "BOOT", "virt=0x{:x}", virt.0);
|
||||
let p4 = unsafe { &mut *virt.as_mut_ptr::<PageTable>() };
|
||||
info!(console, "BOOT", "zeroing page...");
|
||||
unsafe { core::ptr::write_bytes(p4 as *mut _ as *mut u8, 0, 4096); }
|
||||
info!(console, "BOOT", "zero done");
|
||||
|
||||
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;
|
||||
|
||||
for entry in mmap_res.entries() {
|
||||
for (i, entry) in mmap_res.entries().iter().enumerate() {
|
||||
let phys = PhysAddr(entry.base);
|
||||
let virt_hhdm = phys.to_virt(hhdm_offset);
|
||||
p4.map_region(virt_hhdm, phys, entry.length, flags, hhdm_offset);
|
||||
@@ -379,6 +392,7 @@ unsafe extern "C" fn kmain() -> ! {
|
||||
}
|
||||
|
||||
fn hcf() -> ! {
|
||||
unsafe { asm!("cli", options(nomem, nostack, preserves_flags)); }
|
||||
loop {
|
||||
unsafe { asm!("hlt", options(nomem, nostack, preserves_flags)); }
|
||||
}
|
||||
@@ -386,7 +400,6 @@ fn hcf() -> ! {
|
||||
|
||||
#[panic_handler]
|
||||
fn rust_panic(info: &core::panic::PanicInfo) -> ! {
|
||||
let mut sp = unsafe { debug::serial::SerialPort::init() };
|
||||
let _ = writeln!(sp, "KERNEL PANIC: {:?}", info);
|
||||
debug::serial::write_global(format_args!("KERNEL PANIC: {:?}\n", info));
|
||||
hcf();
|
||||
}
|
||||
|
||||
@@ -55,8 +55,14 @@ struct ListNode {
|
||||
|
||||
const BLOCK_SIZES: &[usize] = &[8, 16, 32, 64, 128, 256, 512, 1024, 2048];
|
||||
|
||||
struct LargeBlockNode {
|
||||
size: usize,
|
||||
next: Option<&'static mut LargeBlockNode>,
|
||||
}
|
||||
|
||||
pub struct SlabAllocator {
|
||||
list_heads: [Option<&'static mut ListNode>; BLOCK_SIZES.len()],
|
||||
large_block_free: Option<&'static mut LargeBlockNode>,
|
||||
heap_start: usize,
|
||||
heap_end: usize,
|
||||
next_bump: usize,
|
||||
@@ -66,6 +72,7 @@ impl SlabAllocator {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
list_heads: [None, None, None, None, None, None, None, None, None],
|
||||
large_block_free: None,
|
||||
heap_start: 0,
|
||||
heap_end: 0,
|
||||
next_bump: 0,
|
||||
@@ -84,11 +91,28 @@ impl SlabAllocator {
|
||||
}
|
||||
|
||||
fn fallback_alloc(&mut self, layout: Layout) -> *mut u8 {
|
||||
let size = layout.size().max(layout.align());
|
||||
|
||||
if size > 2048 {
|
||||
let mut field: *mut Option<&'static mut LargeBlockNode> = &mut self.large_block_free;
|
||||
unsafe {
|
||||
while let Some(ref mut node) = *field {
|
||||
if node.size >= size {
|
||||
let node_ptr: *mut LargeBlockNode = *node;
|
||||
let next = node.next.take();
|
||||
*field = next;
|
||||
return node_ptr as *mut u8;
|
||||
}
|
||||
field = &mut node.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let alloc_start = (self.next_bump + layout.align() - 1) & !(layout.align() - 1);
|
||||
let alloc_end = alloc_start.checked_add(layout.size()).unwrap_or(self.heap_end + 1);
|
||||
|
||||
if alloc_end > self.heap_end {
|
||||
null_mut() // Out of memory
|
||||
null_mut()
|
||||
} else {
|
||||
self.next_bump = alloc_end;
|
||||
alloc_start as *mut u8
|
||||
@@ -139,6 +163,17 @@ unsafe impl GlobalAlloc for Locked<SlabAllocator> {
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// Large block: add to free list for reuse
|
||||
let size = layout.size().max(layout.align());
|
||||
let new_node = LargeBlockNode {
|
||||
size,
|
||||
next: allocator.large_block_free.take(),
|
||||
};
|
||||
let new_node_ptr = ptr as *mut LargeBlockNode;
|
||||
unsafe {
|
||||
new_node_ptr.write(new_node);
|
||||
allocator.large_block_free = Some(&mut *new_node_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,6 @@ pub enum PMRequest {
|
||||
size_pages: usize,
|
||||
channel_id: u16,
|
||||
},
|
||||
/// Sentinel — never pushed onto the queue; result of unpack(0).
|
||||
None,
|
||||
}
|
||||
|
||||
@@ -160,30 +159,18 @@ impl PMRequest {
|
||||
|
||||
// Response type
|
||||
|
||||
/// Result returned by `PMActor::process_messages()` for each completed request.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PMResponse {
|
||||
/// Channel to route this response to. `0` = discard.
|
||||
pub channel_id: u16,
|
||||
/// The actual outcome.
|
||||
pub result: PMResult,
|
||||
}
|
||||
|
||||
/// Outcome of a single PM operation.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum PMResult {
|
||||
/// Memory was allocated. `cap` is the strong capability to the region.
|
||||
/// `order` is the buddy order — **must** be passed back to `Free`.
|
||||
Allocated { cap: Capability, order: usize },
|
||||
|
||||
/// The actor had insufficient free pages.
|
||||
/// Future: ballooning subsystem intercepts this and retries.
|
||||
OutOfMemory { size_pages: usize },
|
||||
|
||||
/// A fixed sub-region was carved successfully.
|
||||
Carved { cap: Capability },
|
||||
|
||||
/// Free completed (no capability issued — memory returned to buddy pool).
|
||||
Freed { pages_returned: usize },
|
||||
}
|
||||
|
||||
@@ -194,7 +181,6 @@ pub enum PMResult {
|
||||
/// `pop` — exactly one consumer, no locking on the read side.
|
||||
pub struct PMActorQueue {
|
||||
buffer: [AtomicU64; QUEUE_SIZE],
|
||||
/// Pad to separate producer-written `tail` from consumer-read `head`.
|
||||
_pad0: [u8; 64],
|
||||
head: AtomicUsize,
|
||||
_pad1: [u8; 64],
|
||||
@@ -269,15 +255,10 @@ impl PMActorQueue {
|
||||
/// Only `process_messages()` ever mutates `buddy` and `queue.head`.
|
||||
/// This is enforced by taking `&mut self` on `process_messages`.
|
||||
pub struct PMActor {
|
||||
/// Unique identity within the actor federation.
|
||||
pub actor_id: u64,
|
||||
/// Root strong capability over the entire managed physical range.
|
||||
pub root_untyped: Capability,
|
||||
/// `(inclusive_start, exclusive_end)` physical addresses.
|
||||
pub managed_range: (PhysAddr, PhysAddr),
|
||||
/// Inbox — producers write here, actor reads here.
|
||||
queue: PMActorQueue,
|
||||
/// Local buddy allocator. Only ever touched in `process_messages`.
|
||||
buddy: BuddyAllocator,
|
||||
}
|
||||
|
||||
@@ -325,10 +306,16 @@ impl PMActor {
|
||||
/// `buddy` and `queue.head` are mutated — no other thread touches them.
|
||||
/// The only shared state is the queue's `tail`, which is written by producers
|
||||
/// via `AtomicUsize::compare_exchange_weak`, never by this path.
|
||||
/// Maximum requests to process in a single `process_messages` call.
|
||||
/// Prevents kernel starvation when the inbox is deep.
|
||||
const MAX_MESSAGES_PER_CALL: usize = 64;
|
||||
|
||||
pub fn process_messages(&mut self) -> Vec<PMResponse> {
|
||||
let mut responses = Vec::new();
|
||||
let mut remaining = Self::MAX_MESSAGES_PER_CALL;
|
||||
|
||||
while let Some(req) = self.queue.pop() {
|
||||
remaining -= 1;
|
||||
let resp = match req {
|
||||
PMRequest::Allocate { size_pages, token_sig, channel_id } => {
|
||||
self.handle_allocate(size_pages, token_sig, channel_id)
|
||||
@@ -342,12 +329,13 @@ impl PMActor {
|
||||
PMRequest::None => continue,
|
||||
};
|
||||
|
||||
// Only push responses that need routing.
|
||||
// Free responses (channel_id == 0) are still pushed so callers can
|
||||
// audit completion if needed; they may simply drop them.
|
||||
if let Some(r) = resp {
|
||||
responses.push(r);
|
||||
}
|
||||
|
||||
if remaining == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
responses
|
||||
@@ -434,9 +422,6 @@ impl PMActor {
|
||||
|
||||
self.buddy.free(local_frame_idx, order);
|
||||
|
||||
// Free never needs routing — return None to skip Vec push, or push with
|
||||
// channel_id=0 for audit purposes. We skip to avoid unnecessary allocation.
|
||||
// Callers that need a Free-complete signal should use a separate mechanism.
|
||||
None
|
||||
}
|
||||
|
||||
@@ -446,7 +431,6 @@ impl PMActor {
|
||||
size_pages: usize,
|
||||
channel_id: u16,
|
||||
) -> Option<PMResponse> {
|
||||
// Validate offset + size within range.
|
||||
let range_pages = self.buddy.total_pages();
|
||||
if offset_pages >= range_pages
|
||||
|| size_pages == 0
|
||||
|
||||
@@ -69,12 +69,21 @@ pub fn init() {
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_router() -> &'static PMRouter {
|
||||
// INVARIANT: `is_ready` is set once in `init()` and never cleared.
|
||||
// The Acquire load on `is_ready` synchronises-with the Release store in
|
||||
// `init()`, making the `Option::Some` write visible. Because nothing
|
||||
// ever writes `None` or clears `is_ready`, the TOCTOU window between
|
||||
// the load and the `unwrap_unchecked` is safe.
|
||||
if ROUTER.is_ready.load(Ordering::Acquire) {
|
||||
unsafe {
|
||||
(*ROUTER.inner.get()).as_ref().unwrap_unchecked()
|
||||
}
|
||||
} else {
|
||||
panic!("FATAL: PMRouter is accessed before initialization!")
|
||||
#[cold]
|
||||
fn not_initialized() -> ! {
|
||||
panic!("FATAL: PMRouter is accessed before initialization!")
|
||||
}
|
||||
not_initialized()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ pub struct BitmapPMM {
|
||||
ref_counts: &'static mut [u16],
|
||||
total_pages: usize,
|
||||
used_pages: usize,
|
||||
/// Byte index hint: next search starts here to amortise O(N) scans.
|
||||
last_byte: usize,
|
||||
}
|
||||
|
||||
@@ -59,6 +58,7 @@ impl BitmapPMM {
|
||||
last_byte: 0,
|
||||
};
|
||||
|
||||
// Free all USABLE pages (gap pages not covered by any entry stay locked).
|
||||
for entry in mmap.entries() {
|
||||
if entry.entry_type == limine::memory_map::EntryType::USABLE {
|
||||
for addr in (entry.base..entry.base + entry.length).step_by(PAGE_SIZE as usize) {
|
||||
@@ -67,6 +67,7 @@ impl BitmapPMM {
|
||||
}
|
||||
}
|
||||
|
||||
// Metadata pages sit inside a USABLE region — lock them back.
|
||||
let meta_end = (meta_phys + total_meta_size as u64 + PAGE_SIZE - 1) & !(PAGE_SIZE - 1);
|
||||
for addr in (meta_phys..meta_end).step_by(PAGE_SIZE as usize) {
|
||||
pmm.lock_frame(PhysAddr(addr));
|
||||
|
||||
@@ -56,7 +56,7 @@ fn local_tlb_flush_asid(asid: u16) {
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"invpcid {ty}, [{desc}]",
|
||||
ty = in(reg) 1u64, // type 1 = single-context flush
|
||||
ty = in(reg) 1u64,
|
||||
desc = in(reg) &desc,
|
||||
options(nostack, preserves_flags),
|
||||
);
|
||||
@@ -69,24 +69,14 @@ fn local_tlb_flush_asid(asid: u16) {
|
||||
// Error type
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum VmError {
|
||||
/// PMM returned `None` — no physical frames available.
|
||||
OutOfMemory,
|
||||
/// The requested virtual range overlaps an existing VMA.
|
||||
RegionOverlap,
|
||||
/// No VMA covers the given address.
|
||||
RegionNotFound,
|
||||
/// Address or size is not a multiple of 4096.
|
||||
InvalidAlignment,
|
||||
/// Size is zero, or `virt + size` would overflow.
|
||||
InvalidRange,
|
||||
/// Write fault on a read-only VMA, or exec fault on a NX VMA.
|
||||
PermissionDenied,
|
||||
/// Page-fault in a non-lazy (already-eager or fixed) region — hardware bug
|
||||
/// or an exploit attempt; the faulting task must be killed.
|
||||
UnexpectedFault,
|
||||
/// Address outside the x86-64 canonical range.
|
||||
NonCanonical,
|
||||
/// ASID pool exhausted (> 4094 simultaneous address spaces).
|
||||
AsidExhausted,
|
||||
}
|
||||
|
||||
@@ -143,23 +133,10 @@ impl VmaFlags {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum VmaBacking {
|
||||
/// Anonymous pages (stack, heap, BSS).
|
||||
/// Index `i` → frame for `virt_start + i * 4096`. `None` = not yet faulted in.
|
||||
Anonymous(Vec<Option<PhysAddr>>),
|
||||
|
||||
/// Fixed physical range. Frames are **not** freed on unmap.
|
||||
/// Used for MMIO, identity-mapped RAM, framebuffer, DMA buffers.
|
||||
Physical(PhysAddr),
|
||||
|
||||
/// Zero-copy borrow of another actor's frames.
|
||||
///
|
||||
/// `phys_base` is the physical address of the first page; the mapping covers
|
||||
/// exactly `(virt_end - virt_start) / 4096` pages.
|
||||
/// Frames are owned by `owner_cap` and **never** freed by this VMA.
|
||||
Shared {
|
||||
/// Token of the capability that owns the frames.
|
||||
owner_cap: u64,
|
||||
/// Physical base (first page of the shared region).
|
||||
phys_base: PhysAddr,
|
||||
},
|
||||
}
|
||||
@@ -251,13 +228,13 @@ impl AsidAllocator {
|
||||
}
|
||||
}
|
||||
}
|
||||
None // pool truly exhausted
|
||||
None
|
||||
}
|
||||
|
||||
/// Return an ASID to the pool.
|
||||
fn free(&mut self, asid: u16) {
|
||||
if asid == 0 || asid >= 4095 {
|
||||
return; // sentinel values — never freed
|
||||
return;
|
||||
}
|
||||
let word = asid as usize / 32;
|
||||
let bit = asid as usize % 32;
|
||||
@@ -498,15 +475,6 @@ impl AddressSpace {
|
||||
Self { asid, pml4_phys, regions: Vec::new(), hhdm }
|
||||
}
|
||||
|
||||
// ─── Anonymous / fixed mapping ─────────────────────────────────────────
|
||||
|
||||
/// Map `size` bytes of virtual space starting at `virt`.
|
||||
///
|
||||
/// | `phys` | `VmaFlags::LAZY` | Behaviour |
|
||||
/// |-----------|------------------|-----------------------------------------------|
|
||||
/// | `Some(p)` | any | Fixed physical (MMIO / identity / DMA) |
|
||||
/// | `None` | not set | Eager anonymous — allocate + zero + map now |
|
||||
/// | `None` | set | Lazy anonymous — map frames on first fault |
|
||||
pub fn map_region(
|
||||
&mut self,
|
||||
virt: VirtAddr,
|
||||
@@ -645,6 +613,8 @@ impl AddressSpace {
|
||||
let old_frame = frames[page_idx].expect("COW fault on unmapped page");
|
||||
let new_frame = pmm::alloc_frame().ok_or(VmError::OutOfMemory)?;
|
||||
|
||||
debug_assert!(old_frame != new_frame, "COW: old and new frame are the same!");
|
||||
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(
|
||||
old_frame.to_virt(hhdm).as_ptr::<u8>(),
|
||||
@@ -793,7 +763,15 @@ pub fn tlb_flush_asid(asid: u16) {
|
||||
|
||||
let active_cpus = ACTIVE_CPUS_MASK.load(Ordering::Acquire);
|
||||
let current_core = crate::cpu::lapic::current_core_id();
|
||||
let target_mask = active_cpus & !(1u64 << current_core);
|
||||
// u64 can only represent cores 0–63. APIC IDs may be >= 64 (CPUID
|
||||
// returns up to 255), so guard the shift to avoid UB (panic in debug,
|
||||
// wrap in release). When SMP with >64 cores is implemented, switch to
|
||||
// a wider mask or a dynamic list of active APIC IDs.
|
||||
let target_mask = if current_core < 64 {
|
||||
active_cpus & !(1u64 << current_core)
|
||||
} else {
|
||||
active_cpus
|
||||
};
|
||||
|
||||
if target_mask == 0 {
|
||||
return;
|
||||
@@ -830,7 +808,12 @@ pub fn handle_tlb_shootdown_ipi() {
|
||||
local_tlb_flush_asid(asid);
|
||||
|
||||
let current_core = crate::cpu::lapic::current_core_id();
|
||||
SHOOTDOWN_ACK.fetch_or(1u64 << current_core, Ordering::AcqRel);
|
||||
// Only cores < 64 can ACK in a u64 mask. Cores >= 64 are not
|
||||
// representable; their ACK would wrap and corrupt the mask.
|
||||
// TODO: widen to u128 or use per-core ACK slots for >64 core SMP.
|
||||
if current_core < 64 {
|
||||
SHOOTDOWN_ACK.fetch_or(1u64 << current_core, Ordering::AcqRel);
|
||||
}
|
||||
}
|
||||
|
||||
// Global kernel address space
|
||||
|
||||
Reference in New Issue
Block a user