From 64173db2ceb4b6836e23da0486265fce8940e213 Mon Sep 17 00:00:00 2001 From: zhangxuan2011 Date: Tue, 25 Aug 2026 12:46:30 +0800 Subject: [PATCH 1/4] feat(syscall): added syscall 2 for heap memory allocation --- Cargo.toml | 5 +-- src/process/driver.rs | 14 +++++++- src/process/mod.rs | 6 ++-- src/process/normal.rs | 14 +++++++- src/scheduler.rs | 6 ++-- src/syscall/allocate.rs | 72 +++++++++++++++++++++++++++++++++++++++++ src/syscall/mod.rs | 9 ++++++ 7 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 src/syscall/allocate.rs diff --git a/Cargo.toml b/Cargo.toml index cba0e71..9909857 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,8 +24,9 @@ x2apic = "0.5.0" x86_64 = "0.15.5" [profile.release] -opt-level = 0 +opt-level = 3 panic = "abort" debug = false codegen-units = 1 -incremental = false \ No newline at end of file +incremental = false + diff --git a/src/process/driver.rs b/src/process/driver.rs index c78b3ad..b0625c8 100644 --- a/src/process/driver.rs +++ b/src/process/driver.rs @@ -41,6 +41,15 @@ pub struct DriverProcess { /// The process context. pub context: Context, + /// The bottom of the stack. + pub stack_bottom: u64, + + /// Current heap bottom. + pub heap_bottom: u64, + + /// Current heap top. + pub heap_top: u64, + /// The process's page table. pub table_addr: u64, } @@ -48,11 +57,14 @@ pub struct DriverProcess { impl DriverProcess { /// Create a process. #[inline] - pub fn create(frame: u64) -> Result { + pub fn create(frame: u64, stack_size: u64) -> Result { Ok(Self { present: true, status: Status::Ready, context: Context::driver(), + stack_bottom: Context::driver().rsp - stack_size, + heap_top: 0x180000000, + heap_bottom: 0x180000000, table_addr: frame, }) } diff --git a/src/process/mod.rs b/src/process/mod.rs index 81c5638..c348325 100644 --- a/src/process/mod.rs +++ b/src/process/mod.rs @@ -319,7 +319,8 @@ pub unsafe fn create(data: &[u8], priority: u8) -> Result<(), Error> { /// Create a normal process. fn create_normal(frame: u64, priority: u8) -> Result<(), Error> { - let process = self::normal::NormalProcess::create(frame, priority)?; + // TODO: `proka-exec` support stack size + let process = self::normal::NormalProcess::create(frame, priority, 0x100000)?; // Check which process is usable let mut table = NORMAL_PROCESS.write(); @@ -341,7 +342,8 @@ fn create_normal(frame: u64, priority: u8) -> Result<(), Error> { /// Create a driver process. fn create_driver(frame: u64) -> Result<(), Error> { - let process = self::driver::DriverProcess::create(frame)?; + // TODO: `proka-exec` support stack size + let process = self::driver::DriverProcess::create(frame, 0x100000)?; // Check which process is usable let mut table = DRIVER_PROCESS.write(); diff --git a/src/process/normal.rs b/src/process/normal.rs index 33531e9..03ad71c 100644 --- a/src/process/normal.rs +++ b/src/process/normal.rs @@ -47,6 +47,15 @@ pub struct NormalProcess { /// The page table which is currently using. pub current_table: u64, + /// The stack bottom address. + pub stack_bottom: u64, + + /// The heap bottom address. + pub heap_bottom: u64, + + /// The heap top address. + pub heap_top: u64, + /// The process's page table. pub table_addr: u64, } @@ -54,13 +63,16 @@ pub struct NormalProcess { impl NormalProcess { /// Create a process. #[inline] - pub fn create(frame: u64, priority: u8) -> Result { + pub fn create(frame: u64, priority: u8, stack_size: u64) -> Result { Ok(Self { present: true, status: Status::Ready, priority, context: Context::normal(), current_table: frame, + stack_bottom: Context::normal().rsp - stack_size, + heap_top: 0x180000000, + heap_bottom: 0x180000000, table_addr: frame, }) } diff --git a/src/scheduler.rs b/src/scheduler.rs index da8c4c5..629afef 100644 --- a/src/scheduler.rs +++ b/src/scheduler.rs @@ -2,7 +2,10 @@ extern crate alloc; use crate::process::{Context, DRIVER_PROCESS, NORMAL_PROCESS}; use alloc::vec::Vec; -use core::{mem::offset_of, sync::atomic::{AtomicBool, AtomicUsize, Ordering}}; +use core::{ + mem::offset_of, + sync::atomic::{AtomicBool, AtomicUsize, Ordering}, +}; use spin::Mutex; use x86_64::structures::idt::InterruptStackFrame; @@ -249,7 +252,6 @@ pub extern "x86-interrupt" fn switch_task(stack: InterruptStackFrame) { in("rdi") &context.0, in("rax") context.1, ); - } } diff --git a/src/syscall/allocate.rs b/src/syscall/allocate.rs new file mode 100644 index 0000000..309f3e6 --- /dev/null +++ b/src/syscall/allocate.rs @@ -0,0 +1,72 @@ +//! Syscall to allocate memory. +use crate::{ + memory::{IdentityPageTableMapper, framealloc::FRAME_ALLOCATOR}, + process::NORMAL_PROCESS, +}; +use core::ops::Add; +use x86_64::{ + VirtAddr, + structures::paging::{ + MappedPageTable, Mapper, Page, PageSize, PageTable, PageTableFlags, Size4KiB, + }, +}; + +/// Entry of allocator. +/// +/// # Arguments +/// - size: The size you want to allocated to heap memory. +/// +/// # Returns +/// The size which was actually allocated. +/// +/// Only the size which in 1..u32::MAX is allowed. +pub extern "C" fn allocate(size: u64, _: u64, _: u64, _: u64, _: u64) -> i64 { + x86_64::instructions::interrupts::without_interrupts(|| { + // Get user table... + let user_table: u64; + unsafe { core::arch::asm!("nop", out("r15") user_table) } + + // Query the page table which is using by one user process. + let mut binding = NORMAL_PROCESS.write(); + let Some(process) = binding + .process + .iter_mut() + .find(|item| item.table_addr == user_table) + else { + return -1; + }; + + // And create a [`MappedPageTable`] instance + let mut mapper = unsafe { + let user_table_wrapped = &mut *(user_table as *mut PageTable); + MappedPageTable::new(user_table_wrapped, IdentityPageTableMapper) + }; + + // Calc the pages we needed and pre-allocate them. + let pages = size.div_ceil(Size4KiB::SIZE); + let Some(base_frame) = FRAME_ALLOCATOR.lock().allocate_contiguous(pages as usize) else { + return -2; + }; + + // Map them + let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::NO_EXECUTE; + for i in 0..pages { + let virt = Page::::containing_address(VirtAddr::new( + process.heap_top + i * Size4KiB::SIZE, + )); + let phys = base_frame.add(i); + unsafe { + let Ok(flusher) = mapper.map_to(virt, phys, flags, &mut *FRAME_ALLOCATOR.lock()) + else { + let allocated_size = virt.start_address().as_u64() - process.heap_top; + return allocated_size as i64; + }; + flusher.ignore(); + } + } + + // Increase the heap top and return the size which was allocated. + process.heap_top += pages * Size4KiB::SIZE; + size as i64 + }) +} diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs index 20b28fd..c5c96bc 100644 --- a/src/syscall/mod.rs +++ b/src/syscall/mod.rs @@ -1,5 +1,6 @@ //! The syscall module. extern crate alloc; +pub mod allocate; pub mod power; pub mod process; use crate::{handler::syscall_entry, tables::gdt::GDT}; @@ -73,5 +74,13 @@ pub fn init() { entry: power::power, }); + // For syscall 2 (memory allocation) + SYSCALL.write().push(SyscallEntry { + sysnum: 2, + page_table: 0x100000, + stack: 0xffff8000005ffff0, + entry: allocate::allocate, + }); + // TODO: Add more types of syscall (0-16) } From 28281ebac307710f99b9e68d996371ca2e3c404c Mon Sep 17 00:00:00 2001 From: zhangxuan2011 Date: Tue, 25 Aug 2026 15:08:54 +0800 Subject: [PATCH 2/4] feat(syscall): added size check of heap allocation --- src/syscall/allocate.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/syscall/allocate.rs b/src/syscall/allocate.rs index 309f3e6..b749113 100644 --- a/src/syscall/allocate.rs +++ b/src/syscall/allocate.rs @@ -26,6 +26,11 @@ pub extern "C" fn allocate(size: u64, _: u64, _: u64, _: u64, _: u64) -> i64 { let user_table: u64; unsafe { core::arch::asm!("nop", out("r15") user_table) } + // Check: Is specified size larger than u32::MAX or zeroed + if size > u32::MAX.into() || size == 0 { + return -1 + } + // Query the page table which is using by one user process. let mut binding = NORMAL_PROCESS.write(); let Some(process) = binding @@ -33,7 +38,7 @@ pub extern "C" fn allocate(size: u64, _: u64, _: u64, _: u64, _: u64) -> i64 { .iter_mut() .find(|item| item.table_addr == user_table) else { - return -1; + return -2; }; // And create a [`MappedPageTable`] instance @@ -45,7 +50,7 @@ pub extern "C" fn allocate(size: u64, _: u64, _: u64, _: u64, _: u64) -> i64 { // Calc the pages we needed and pre-allocate them. let pages = size.div_ceil(Size4KiB::SIZE); let Some(base_frame) = FRAME_ALLOCATOR.lock().allocate_contiguous(pages as usize) else { - return -2; + return -3; }; // Map them @@ -66,7 +71,8 @@ pub extern "C" fn allocate(size: u64, _: u64, _: u64, _: u64, _: u64) -> i64 { } // Increase the heap top and return the size which was allocated. - process.heap_top += pages * Size4KiB::SIZE; - size as i64 + let allocated_size = pages * Size4KiB::SIZE; + process.heap_top += allocated_size; + allocated_size as i64 }) } From 10e6799d7d02897872259c04a390e546049f9544 Mon Sep 17 00:00:00 2001 From: zhangxuan2011 Date: Wed, 26 Aug 2026 11:01:35 +0800 Subject: [PATCH 3/4] feat(syscall): implement the deallocate heap memory syscall --- Cargo.lock | 22 +++++ Cargo.toml | 1 + src/syscall/allocate.rs | 78 ------------------ src/syscall/memory.rs | 173 ++++++++++++++++++++++++++++++++++++++++ src/syscall/mod.rs | 4 +- src/syscall/power.rs | 24 ++---- src/syscall/process.rs | 28 +++---- 7 files changed, 216 insertions(+), 114 deletions(-) delete mode 100644 src/syscall/allocate.rs create mode 100644 src/syscall/memory.rs diff --git a/Cargo.lock b/Cargo.lock index f5c4333..79000be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -269,6 +269,27 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "parse-size" version = "1.1.0" @@ -337,6 +358,7 @@ dependencies = [ "glob", "hadris-fat", "log", + "num_enum", "pci_types", "proka-bootloader", "proka-exec", diff --git a/Cargo.toml b/Cargo.toml index 9909857..b7aebf7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ glob = "0.3.0" acpi = { git = "https://github.com/rust-osdev/acpi.git", branch = "main" } hadris-fat = { version = "1.2.0", default-features = false, features = ["alloc", "lfn", "read", "sync"] } log = "0.4.29" +num_enum = { version = "0.7.6", default-features = false } pci_types = "0.10.1" proka-bootloader = "0.5.4" proka-exec = "0.6.1" diff --git a/src/syscall/allocate.rs b/src/syscall/allocate.rs deleted file mode 100644 index b749113..0000000 --- a/src/syscall/allocate.rs +++ /dev/null @@ -1,78 +0,0 @@ -//! Syscall to allocate memory. -use crate::{ - memory::{IdentityPageTableMapper, framealloc::FRAME_ALLOCATOR}, - process::NORMAL_PROCESS, -}; -use core::ops::Add; -use x86_64::{ - VirtAddr, - structures::paging::{ - MappedPageTable, Mapper, Page, PageSize, PageTable, PageTableFlags, Size4KiB, - }, -}; - -/// Entry of allocator. -/// -/// # Arguments -/// - size: The size you want to allocated to heap memory. -/// -/// # Returns -/// The size which was actually allocated. -/// -/// Only the size which in 1..u32::MAX is allowed. -pub extern "C" fn allocate(size: u64, _: u64, _: u64, _: u64, _: u64) -> i64 { - x86_64::instructions::interrupts::without_interrupts(|| { - // Get user table... - let user_table: u64; - unsafe { core::arch::asm!("nop", out("r15") user_table) } - - // Check: Is specified size larger than u32::MAX or zeroed - if size > u32::MAX.into() || size == 0 { - return -1 - } - - // Query the page table which is using by one user process. - let mut binding = NORMAL_PROCESS.write(); - let Some(process) = binding - .process - .iter_mut() - .find(|item| item.table_addr == user_table) - else { - return -2; - }; - - // And create a [`MappedPageTable`] instance - let mut mapper = unsafe { - let user_table_wrapped = &mut *(user_table as *mut PageTable); - MappedPageTable::new(user_table_wrapped, IdentityPageTableMapper) - }; - - // Calc the pages we needed and pre-allocate them. - let pages = size.div_ceil(Size4KiB::SIZE); - let Some(base_frame) = FRAME_ALLOCATOR.lock().allocate_contiguous(pages as usize) else { - return -3; - }; - - // Map them - let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::NO_EXECUTE; - for i in 0..pages { - let virt = Page::::containing_address(VirtAddr::new( - process.heap_top + i * Size4KiB::SIZE, - )); - let phys = base_frame.add(i); - unsafe { - let Ok(flusher) = mapper.map_to(virt, phys, flags, &mut *FRAME_ALLOCATOR.lock()) - else { - let allocated_size = virt.start_address().as_u64() - process.heap_top; - return allocated_size as i64; - }; - flusher.ignore(); - } - } - - // Increase the heap top and return the size which was allocated. - let allocated_size = pages * Size4KiB::SIZE; - process.heap_top += allocated_size; - allocated_size as i64 - }) -} diff --git a/src/syscall/memory.rs b/src/syscall/memory.rs new file mode 100644 index 0000000..da3ac11 --- /dev/null +++ b/src/syscall/memory.rs @@ -0,0 +1,173 @@ +//! Syscall to allocate memory. +use crate::{ + memory::{IdentityPageTableMapper, framealloc::FRAME_ALLOCATOR}, + process::NORMAL_PROCESS, +}; +use core::ops::Add; +use num_enum::TryFromPrimitive; +use x86_64::{ + VirtAddr, + structures::paging::{ + FrameDeallocator, MappedPageTable, Mapper, Page, PageSize, PageTable, PageTableFlags, + Size4KiB, + }, +}; + +/// Types of this syscall. +#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive)] +#[repr(u64)] +enum MemorySyscallType { + /// Allocate heap memory. + Allocate = 0, + + /// Deallocate specified address memory. + Deallocate = 1, +} + +/// Main entry of this syscall 2. +pub extern "C" fn memory(typ: u64, size: u64, addr: u64, _: u64, _: u64) -> i64 { + let Ok(typ) = MemorySyscallType::try_from(typ) else { + return -2; + }; + + match typ { + MemorySyscallType::Allocate => allocate(size), + MemorySyscallType::Deallocate => deallocate(addr, size), + } +} + +/// Allocate heap memory for processes. +/// +/// # Arguments +/// - `size`: The size you want to allocated to heap memory. +/// +/// # Returns +/// - positive: the address of the heap base; +/// - negative: errors +/// +/// Only the size which is above 0 is allowed +fn allocate(size: u64) -> i64 { + x86_64::instructions::interrupts::without_interrupts(|| { + // Get user table... + let user_table: u64; + unsafe { core::arch::asm!("nop", out("r15") user_table) } + + // Check: Is specified size zeroed + if size == 0 { + return -16; + } + + // Query the page table which is using by one user process. + let mut binding = NORMAL_PROCESS.write(); + let Some(process) = binding + .process + .iter_mut() + .find(|item| item.table_addr == user_table) + else { + return -17; + }; + + // And create a [`MappedPageTable`] instance + let mut mapper = unsafe { + let user_table_wrapped = &mut *(user_table as *mut PageTable); + MappedPageTable::new(user_table_wrapped, IdentityPageTableMapper) + }; + + // Calc the pages we needed and pre-allocate them. + let pages = size.div_ceil(Size4KiB::SIZE); + let Some(base_frame) = FRAME_ALLOCATOR.lock().allocate_contiguous(pages as usize) else { + return -18; + }; + + // Map them + let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::NO_EXECUTE; + for i in 0..pages { + let virt = Page::::containing_address(VirtAddr::new( + process.heap_top + i * Size4KiB::SIZE, + )); + let phys = base_frame.add(i); + unsafe { + let Ok(flusher) = mapper.map_to(virt, phys, flags, &mut *FRAME_ALLOCATOR.lock()) + else { + let allocated_size = virt.start_address().as_u64() - process.heap_top; + return allocated_size as i64; + }; + flusher.ignore(); + } + } + + // Increase the heap top and return the addr which was allocated. + let addr = process.heap_top; + process.heap_top += pages * Size4KiB::SIZE; + addr as i64 // SAFETY: address is always low address + }) +} + +/// Deallocate heap memory. +/// +/// # Arguments +/// - `addr`: The virtual address of this process; +/// - `size`: The size which you want to deallocate. +/// +/// # Returns +/// - positive: succeed, 0..i64::MAX, commonly 0 +/// - negative: error +fn deallocate(addr: u64, size: u64) -> i64 { + x86_64::instructions::interrupts::without_interrupts(|| { + // Get user table + let user_table: u64; + unsafe { core::arch::asm!("nop", out("r15") user_table) } + + // Discover the process block + let mut binding = NORMAL_PROCESS.write(); + let Some(process) = binding + .process + .iter_mut() + .find(|item| item.table_addr == user_table) + else { + return -16; + }; + + // Check: Is the size we want to deallocated is larger than (top - bottom) + // SAFETY: `heap_top` is always larger than `heap_bottom`. + let available_dealloc_size = process.heap_top - process.heap_bottom; + if available_dealloc_size < size { + return -17; + } + + // Check: Is the deallocated memory range is invalid + // First assertion: check `addr` + if addr > process.heap_top || addr < process.heap_bottom { + return -18; + } + + // Second assertion: check is range overflow + let range_top = addr + size + 1; + if range_top > process.heap_top || range_top < process.heap_bottom { + return -19; + } + + // Create mapper + let mut mapper = unsafe { + let wrapped_mapper = &mut *(user_table as *mut PageTable); + MappedPageTable::new(wrapped_mapper, IdentityPageTableMapper) + }; + + let pages = size.div_ceil(Size4KiB::SIZE); + for i in 0..pages { + let page = + Page::::containing_address(VirtAddr::new(addr + i * Size4KiB::SIZE)); + unsafe { + let Ok((frame, flusher)) = mapper.unmap(page) else { + continue; + }; + FRAME_ALLOCATOR.lock().deallocate_frame(frame); + flusher.ignore(); + }; + } + + // Decrease the heap top and return + process.heap_top -= pages * Size4KiB::SIZE; + 0 + }) +} diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs index c5c96bc..639b9ed 100644 --- a/src/syscall/mod.rs +++ b/src/syscall/mod.rs @@ -1,6 +1,6 @@ //! The syscall module. extern crate alloc; -pub mod allocate; +pub mod memory; pub mod power; pub mod process; use crate::{handler::syscall_entry, tables::gdt::GDT}; @@ -79,7 +79,7 @@ pub fn init() { sysnum: 2, page_table: 0x100000, stack: 0xffff8000005ffff0, - entry: allocate::allocate, + entry: memory::memory, }); // TODO: Add more types of syscall (0-16) diff --git a/src/syscall/power.rs b/src/syscall/power.rs index 2ab98df..dbdf94f 100644 --- a/src/syscall/power.rs +++ b/src/syscall/power.rs @@ -1,37 +1,29 @@ //! The power action in syscall. //! //! Registered as syscall 1. +use num_enum::TryFromPrimitive; use crate::{ acpi::power::{poweroff, reboot}, scheduler::{DRIVER_QUEUE, NORMAL_QUEUE}, }; /// The power actions. -#[repr(C)] +#[derive(Debug, PartialEq, Eq, TryFromPrimitive)] +#[repr(u64)] pub enum PowerActions { /// The power action to poweroff the whole machine. - PowerOff, + PowerOff = 0, /// The power action which makes this machine reset (reboot). - Reboot, -} - -impl PowerActions { - /// Convert to this action from u64. - #[inline] - pub fn from_u64(action: u64) -> Self { - match action { - 0 => Self::PowerOff, - 1 => Self::Reboot, - _ => panic!("Invalid power action: {}", action), - } - } + Reboot = 1, } /// The power action syscall entry. pub extern "C" fn power(power_action: u64, _: u64, _: u64, _: u64, _: u64) -> i64 { unsafe { core::arch::asm!("cli") } // Avoid scheduler switch tasks - let action = PowerActions::from_u64(power_action); + let Ok(action) = PowerActions::try_from(power_action) else { + return -2; + }; // Kill all tasks... DRIVER_QUEUE.lock().clear(); diff --git a/src/syscall/process.rs b/src/syscall/process.rs index f333165..270309e 100644 --- a/src/syscall/process.rs +++ b/src/syscall/process.rs @@ -1,10 +1,12 @@ //! The task manager in syscall. //! //! Registered as syscall 0. - +use num_enum::TryFromPrimitive; use crate::process::ProcType; /// The process syscall request type. +#[derive(Debug, PartialEq, Eq, TryFromPrimitive)] +#[repr(u64)] pub enum ProcessSyscallRequest { /// Request to kill tasks. KillTasks, @@ -13,18 +15,6 @@ pub enum ProcessSyscallRequest { CreateTasks, } -impl ProcessSyscallRequest { - /// Convert to this type from u64. - #[inline] - pub fn from_u64(request: u64) -> Self { - match request { - 0 => Self::KillTasks, - 1 => Self::CreateTasks, - _ => panic!("Invalid process syscall request: {}", request), - } - } -} - /// The entry point of process syscall. // TODO: Write this function once the structure of memory got refactored. pub extern "C" fn process( @@ -44,14 +34,16 @@ pub extern "C" fn process( // Check: Is `None` was returned? if kernel_buf.is_none() { - return -1; + return -2; } // So we can safely unwrap it. let kernel_buf = kernel_buf.unwrap(); // Parse the request type. - let request = ProcessSyscallRequest::from_u64(request); + let Ok(request) = ProcessSyscallRequest::try_from(request) else { + return -3; + }; // Check the request type. match request { @@ -61,11 +53,11 @@ pub extern "C" fn process( let typ = match proctyp { 0 => ProcType::Normal, 1 => ProcType::Driver, - _ => return -1, + _ => return -4, }; if crate::process::remove(typ, id_or_priority as usize).is_err() { - return -1; + return -5; }; } ProcessSyscallRequest::CreateTasks => { @@ -73,7 +65,7 @@ pub extern "C" fn process( // If `id_or_priority` is larger than u8::MAX, it will cause truncation. unsafe { if crate::process::create(&kernel_buf, id_or_priority as u8).is_err() { - return -1; + return -6; } }; } From 9a9364133f4e2d476eb243f892319cbc45a5f936 Mon Sep 17 00:00:00 2001 From: zhangxuan2011 Date: Fri, 28 Aug 2026 10:28:55 +0800 Subject: [PATCH 4/4] feat(handler): implemented handler of #PF --- src/handler/exception.rs | 89 ++++++++++++++++++++++++++++++++++++---- src/syscall/power.rs | 2 +- src/syscall/process.rs | 2 +- 3 files changed, 82 insertions(+), 11 deletions(-) diff --git a/src/handler/exception.rs b/src/handler/exception.rs index d322705..85020ae 100644 --- a/src/handler/exception.rs +++ b/src/handler/exception.rs @@ -1,10 +1,16 @@ //! Exception handler. //! //! Originally by moyanj +use crate::memory::IdentityPageTableMapper; +use crate::memory::framealloc::FRAME_ALLOCATOR; use crate::println; +use crate::process::{DRIVER_PROCESS, NORMAL_PROCESS, ProcType}; use crate::scheduler::{CURRENT_ID, IS_DRIVER}; use core::arch::asm; use core::sync::atomic::Ordering; +use x86_64::structures::paging::{ + FrameAllocator, MappedPageTable, Mapper, Page, PageTable, PageTableFlags, Size4KiB, +}; use x86_64::{ VirtAddr, registers::control::Cr2, @@ -114,10 +120,9 @@ pub extern "x86-interrupt" fn double_fault(stack_frame: InterruptStackFrame, err } // #PF handler - pub extern "x86-interrupt" fn pagefault( - stack_frame: InterruptStackFrame, - error_code: PageFaultErrorCode, + _stack_frame: InterruptStackFrame, + _error_code: PageFaultErrorCode, ) { let pml4: u64; unsafe { @@ -135,12 +140,78 @@ pub extern "x86-interrupt" fn pagefault( Err(_) => VirtAddr::zero(), }; - println!( - "\x1b[31m[ERROR] EXCEPTION: PAGE FAULT in table 0x{:x} at {:#x}\nError Code: {:?}\nFrame: {:#?}\x1b[0m", - pml4, fault_address, error_code, stack_frame - ); - // TODO: Exception recovery logic - hlt_loop() + // Time to query the process... + if IS_DRIVER.load(Ordering::Relaxed) { + let binding = DRIVER_PROCESS.read(); + let (index, process) = binding + .process + .iter() + .enumerate() + .find(|item| pml4 == item.1.table_addr) + .expect("Process (driver) in the page table is mismatched..."); + + // Check: is the #PF place in stack range? + if (process.stack_bottom..0x7ffffffff000).contains(&fault_address.as_u64()) { + // We should map the missing place... + let mut mapper = unsafe { + let table_wrapped = &mut *(pml4 as *mut PageTable); + MappedPageTable::new(table_wrapped, IdentityPageTableMapper) + }; + + // Map 1 4KiB page... + let page = Page::::containing_address(fault_address); + let Some(frame) = FRAME_ALLOCATOR.lock().allocate_frame() else { + crate::process::remove(ProcType::Driver, index).unwrap(); + hlt_loop() + }; + let flags = + PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::NO_EXECUTE; + unsafe { + mapper + .map_to(page, frame, flags, &mut *FRAME_ALLOCATOR.lock()) + .expect("Failed to map stack in #PF") + .ignore() + } + } + + crate::process::remove(ProcType::Driver, index).unwrap(); + hlt_loop() + } else { + let binding = NORMAL_PROCESS.read(); + let (index, process) = binding + .process + .iter() + .enumerate() + .find(|item| pml4 == item.1.table_addr || pml4 == item.1.current_table) + .expect("Process (normal) om this page table is mismatched..."); + + // Check: is #PF in stack range + if (process.stack_bottom..0x7ffffffff000).contains(&fault_address.as_u64()) { + // Create mapper + let mut mapper = unsafe { + let table_wrapped = &mut *(pml4 as *mut PageTable); + MappedPageTable::new(table_wrapped, IdentityPageTableMapper) + }; + + // Map 1 4KiB page only... + let page = Page::::containing_address(fault_address); + let Some(frame) = FRAME_ALLOCATOR.lock().allocate_frame() else { + crate::process::remove(ProcType::Normal, index).unwrap(); + hlt_loop() + }; + let flags = + PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::NO_EXECUTE; + unsafe { + mapper + .map_to(page, frame, flags, &mut *FRAME_ALLOCATOR.lock()) + .expect("Failed to map stack in #PF") + .ignore() + } + } + + crate::process::remove(ProcType::Normal, index).unwrap(); + hlt_loop() + } } // Breakpoint handler diff --git a/src/syscall/power.rs b/src/syscall/power.rs index dbdf94f..3f8803c 100644 --- a/src/syscall/power.rs +++ b/src/syscall/power.rs @@ -1,11 +1,11 @@ //! The power action in syscall. //! //! Registered as syscall 1. -use num_enum::TryFromPrimitive; use crate::{ acpi::power::{poweroff, reboot}, scheduler::{DRIVER_QUEUE, NORMAL_QUEUE}, }; +use num_enum::TryFromPrimitive; /// The power actions. #[derive(Debug, PartialEq, Eq, TryFromPrimitive)] diff --git a/src/syscall/process.rs b/src/syscall/process.rs index 270309e..c358ecb 100644 --- a/src/syscall/process.rs +++ b/src/syscall/process.rs @@ -1,8 +1,8 @@ //! The task manager in syscall. //! //! Registered as syscall 0. -use num_enum::TryFromPrimitive; use crate::process::ProcType; +use num_enum::TryFromPrimitive; /// The process syscall request type. #[derive(Debug, PartialEq, Eq, TryFromPrimitive)]