diff --git a/Cargo.lock b/Cargo.lock index ba46d04..2c2d4d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "ahash" @@ -16,9 +16,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" [[package]] name = "autocfg" @@ -52,9 +52,9 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bootloader_api" -version = "0.11.7" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35ba5100c2431e20b924c8103c2cf8adb919ed9880f625e8770c3cb9d1b06aa" +checksum = "99adba6d383d3b4b367b9ae41e8347ffe4d88efe247af76d00fd029e4fbb3076" [[package]] name = "buddy_system_allocator" @@ -174,9 +174,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "linked_list_allocator" @@ -229,12 +229,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.1" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" -dependencies = [ - "portable-atomic", -] +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "pic8259" @@ -245,17 +242,11 @@ dependencies = [ "x86_64", ] -[[package]] -name = "portable-atomic" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" - [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -286,9 +277,9 @@ checksum = "e3a8614ee435691de62bcffcf4a66d91b3594bf1428a5722e79103249a095690" [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "saturating_cast" @@ -348,9 +339,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.79" +version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ "proc-macro2", "quote", @@ -377,9 +368,9 @@ dependencies = [ [[package]] name = "uart_16550" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4922792855b1bce30997fbaa5418597902c278a92d20dfe348e6f062c3bd861d" +checksum = "e492212ac378a5e00da953718dafb1340d9fbaf4f27d6f3c5cab03d931d1c049" dependencies = [ "bitflags 2.6.0", "rustversion", @@ -388,9 +379,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unsigned-varint" @@ -457,5 +448,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.89", ] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 5d56faf..1dfc098 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "nightly" +channel = "nightly-2024-11-21" diff --git a/src/interrupts.rs b/src/interrupts.rs index f8fbbd6..73e3e54 100644 --- a/src/interrupts.rs +++ b/src/interrupts.rs @@ -1,8 +1,8 @@ use crate::{ bootinfo::BOOTINFO, print, println, - tasking::SleepReason, - virtual_memory::{ASpaceMutex, AddressSpace, ACTIVE_SPACE, KERNEL_SPACE}, + tasking::{InvalidPid, SleepReason}, + virtual_memory::{ASpaceMutex, AddressSpace, PagingError, ACTIVE_SPACE, KERNEL_SPACE}, TASKING, }; use alloc::{boxed::Box, vec::Vec}; @@ -64,43 +64,53 @@ extern "x86-interrupt" fn page_fault_handler( error_code: PageFaultErrorCode, ) { #[warn(clippy::expect_used, reason = "FIXME")] - let faulting_addr = - Cr2::read().expect("Cannot handle page faults caused by non-canonical addresses"); - if faulting_addr.p4_index() >= PageTableIndex::new(256) - && !ACTIVE_SPACE.lock().level_4_table()[faulting_addr.p4_index()] - .flags() - .contains(PageTableFlags::PRESENT) - && KERNEL_SPACE.lock().level_4_table()[faulting_addr.p4_index()] - .flags() - .contains(PageTableFlags::PRESENT) - { - ACTIVE_SPACE.lock().level_4_table_mut()[faulting_addr.p4_index()] = - KERNEL_SPACE.lock().level_4_table()[faulting_addr.p4_index()].clone(); - return; - } - if let Some(current_pid) = TASKING.current_pid() { - print!("PID {current_pid} "); + if let Ok(faulting_addr) = Cr2::read() { + if faulting_addr.p4_index() >= PageTableIndex::new(256) + && !ACTIVE_SPACE.lock().level_4_table()[faulting_addr.p4_index()] + .flags() + .contains(PageTableFlags::PRESENT) + && KERNEL_SPACE.lock().level_4_table()[faulting_addr.p4_index()] + .flags() + .contains(PageTableFlags::PRESENT) + { + ACTIVE_SPACE.lock().level_4_table_mut()[faulting_addr.p4_index()] = + KERNEL_SPACE.lock().level_4_table()[faulting_addr.p4_index()].clone(); + return; + } + if let Some(current_pid) = TASKING.current_pid() { + print!("PID {current_pid} "); + } else { + print!("Kernel init "); + } + if error_code.contains(PageFaultErrorCode::PROTECTION_VIOLATION) { + println!( + "page faulted {error_code:#?} at {:#x}\nEntry flags: {:#?}\n{stack_frame:#?}", + faulting_addr, + match ACTIVE_SPACE.lock().translate(faulting_addr) { + TranslateResult::Mapped { flags, .. } => flags, + _ => + { + #![allow( + clippy::panic, + reason = "A protection violation only happends on mapped addresses. If we get here, something has gone VERY wrong." + )] + panic!(); + } + }, + ); + } else { + println!("page faulted {error_code:#?} at {:#x}\n{stack_frame:#?}", faulting_addr); + } } else { - print!("Kernel init "); - } - if error_code.contains(PageFaultErrorCode::PROTECTION_VIOLATION) { + if let Some(current_pid) = TASKING.current_pid() { + print!("PID {current_pid} "); + } else { + print!("Kernel init "); + } println!( - "page faulted {error_code:#?} at {:#x}\nEntry flags: {:#?}\n{stack_frame:#?}", - faulting_addr, - match ACTIVE_SPACE.lock().translate(faulting_addr) { - TranslateResult::Mapped { flags, .. } => flags, - _ => - { - #![allow( - clippy::panic, - reason = "A protection violation only happends on mapped addresses. If we get here, something has gone VERY wrong." - )] - panic!(); - } - }, + "page faulted {error_code:#?} at non-caonical address {:#x}\n{stack_frame:#?}", + Cr2::read_raw() ); - } else { - println!("page faulted {error_code:#?} at {:#x}\n{stack_frame:#?}", faulting_addr); } TASKING.exit(254); } @@ -130,7 +140,30 @@ impl Drop for EoiGuard { } } -pub fn send_ipc_to(pid: usize, buffer: Box<[u8], &'static ASpaceMutex>, len: usize) { +#[derive(Copy, Clone, Debug)] +pub enum SendIpcError { + InvalidPid, + #[expect(unused, reason = "Useful in debug logs")] + PagingError(PagingError), +} + +impl From for SendIpcError { + fn from(_v: InvalidPid) -> Self { + Self::InvalidPid + } +} + +impl From for SendIpcError { + fn from(v: PagingError) -> Self { + Self::PagingError(v) + } +} + +pub fn send_ipc_to( + pid: usize, + buffer: Box<[u8], &'static ASpaceMutex>, + len: usize, +) -> Result<(), SendIpcError> { #[cfg(feature = "log-rpc")] { #[expect( @@ -172,43 +205,35 @@ pub fn send_ipc_to(pid: usize, buffer: Box<[u8], &'static ASpaceMutex>, len: usi ]); } assert!(len <= buffer.len()); - if TASKING.message_queue_mut(pid, |_| ()).is_ok() { + TASKING.process_mut(pid, |process| { let buf_num_pages = buffer.len() / 4096; let buffer = Box::into_raw(buffer); - let buf_start_page = - Page::from_start_address(VirtAddr::new(u64(buffer.expose_provenance()))).unwrap(); - let dest_buffer = TASKING - .address_space_mut(pid, |aspace| { - // This is None only if the destiniation is the current process. If so, - // no remapping is necessary so just return the old buffer. - let Some(aspace) = aspace else { - return buffer; - }; - let page = ACTIVE_SPACE - .lock() - .move_mappings_free(buf_start_page, buf_num_pages, aspace) - .unwrap(); - ptr::slice_from_raw_parts_mut::(page.start_address().as_mut_ptr(), buffer.len()) - }) - .unwrap(); #[expect( clippy::unwrap_used, - reason = "The PID is known valid due to using it in message_queue_mut in the if-let condition" + reason = "ASpaceMutex is guaranteed to only return page-aligned allocations, so the address passed to Page::from_start_address is always valid" )] - let new_buffer_key = TASKING.proc_data_buffers_mut(pid, |x| x.insert(dest_buffer)).unwrap(); - #[expect( - clippy::unwrap_used, - reason = "The option was already checked at the start of the if-let" - )] - TASKING.message_queue_mut(pid, |x| x.push((new_buffer_key, len))).unwrap(); - #[expect( - clippy::unwrap_used, - reason = "The PID is known valid due to using it in message_queue_mut in the if-let condition" - )] - TASKING.wake(pid, SleepReason::WaitingForIPC).unwrap(); - } else { - println!("irq 1 msg: Bad PID ({})", pid); - } + let buf_start_page = Page::from_start_address(VirtAddr::new(u64(buffer.expose_provenance()))).unwrap(); + // This is None only if the destiniation is the current process. If so, + // no remapping is necessary so just return the old buffer. + let dest_buffer = if let Some(aspace) = process.address_space_mut() { + let page = ACTIVE_SPACE.lock().move_mappings_free(buf_start_page, buf_num_pages, aspace)?; + ptr::slice_from_raw_parts_mut::( + page.start_address().as_mut_ptr(), + buffer.len(), + ) + } else { + buffer + }; + let new_buffer_key = process.data_buffers().lock().insert(dest_buffer); + process.message_queue().lock().push((new_buffer_key, len)); + Ok::<_, PagingError>(()) + })??; + #[expect( + clippy::unwrap_used, + reason = "The PID is known valid due to using it in process_mut above" + )] + TASKING.wake(pid, SleepReason::WaitingForIPC).unwrap(); + Ok(()) } fn irq_handler(_stack_frame: InterruptStackFrame, index: u8, _error_code: Option) { @@ -234,7 +259,19 @@ fn irq_handler(_stack_frame: InterruptStackFrame, index: u8, _error_code: Option buffer[0..8].copy_from_slice(&u64::MAX.to_le_bytes()); buffer[8..10].copy_from_slice(&2u16.to_le_bytes()); buffer[10] = irq_num; - send_ipc_to(pid, buffer, len); + match send_ipc_to(pid, buffer, len) { + Ok(()) => (), + Err(SendIpcError::InvalidPid) => { + IRQ_TASKS.write()[usize(irq_num)] = None; + } + #[expect( + clippy::panic, + reason = "If an IRQ message fails to deliver, driver code can easify malfunction, so treat this as fatal." + )] + Err(e) => { + panic!("Failed to send IRQ message: {e:?}"); + } + } } } @@ -305,8 +342,12 @@ extern "x86-interrupt" fn syscall_handler_header(stack_frame: InterruptStackFram } fn get_buffer(id: u64) -> Option> { - TASKING.data_buffers_mut(|x| { - x.try_remove(usize(id)).map(|buf| unsafe { Box::from_raw_in(buf, &*ACTIVE_SPACE) }) + TASKING.current_process(|process| { + process + .data_buffers() + .lock() + .try_remove(usize(id)) + .map(|buf| unsafe { Box::from_raw_in(buf, &*ACTIVE_SPACE) }) }) } @@ -314,7 +355,7 @@ pub static REGISTERD_PIDS: Lazy>> = Lazy::new(|| RwLock::new(HashMap::new())); static INITRD_BUF: Lazy<&'static [u8]> = Lazy::new(|| { - #[warn(clippy::expect_used, reason = "FIXME")] + #[expect(clippy::expect_used, reason = "Boot cannot happen without an initrd")] let ramdisk_start = BOOTINFO.ramdisk_addr.into_option().expect("initrd not present"); let ramdisk_len = BOOTINFO.ramdisk_len; let initrd = unsafe { @@ -326,6 +367,14 @@ static INITRD_BUF: Lazy<&'static [u8]> = Lazy::new(|| { let initrd_start_page = Page::containing_address(VirtAddr::new(ramdisk_start)); let initrd_num_pages = usize(ramdisk_len.div_ceil(4096)); unsafe { + #[expect( + clippy::unwrap_used, + reason = " + This can only fail if the initrd is not at the address the bootloder says or if it + uses huge pages. The first is a violatin of the contract between the bootloader and + the kernel, and the second can't happen because the bootloader does not use huge pages. + " + )] KERNEL_SPACE .lock() .update_flags( @@ -367,12 +416,13 @@ extern "C" fn syscall_handler() { .map_free(usize(regs.rdx), PageTableFlags::from_bits_truncate(regs.rsi)) .map_or(0, |x| u64(x.expose_provenance())) } else { - TASKING.address_spaces_mut(|x| { + TASKING.current_process(|process| { + let mut address_spaces = process.address_spaces().lock(); #[warn( clippy::arithmetic_side_effects, reason = "FIXME: The current address space should be usize::MAX as that is an invalid index, instead of 0." )] - if let Some(space) = x.get_mut(usize(regs.rcx - 1)) { + if let Some(space) = address_spaces.get_mut(usize(regs.rcx - 1)) { space .map_free(usize(regs.rdx), PageTableFlags::from_bits_truncate(regs.rsi)) .map_or(0, |x| u64(x.expose_provenance())) @@ -392,8 +442,10 @@ extern "C" fn syscall_handler() { clippy::arithmetic_side_effects, reason = "usize::MAX will never be returned as an index, and so incrementing can never overflow." )] - let address_space = u64(TASKING.address_spaces_mut(|x| { - x.insert(AddressSpace::new().expect("Failed to create address space")) + 1 + let address_space = u64(TASKING.current_process(|process| { + let mut address_spaces = process.address_spaces().lock(); + address_spaces.insert(AddressSpace::new().expect("Failed to create address space")) + + 1 })); retval = address_space; } @@ -402,7 +454,9 @@ extern "C" fn syscall_handler() { clippy::arithmetic_side_effects, reason = "FIXME: The current address space should be usize::MAX as that is an invalid index, instead of 0." )] - TASKING.address_spaces_mut(|x| x.remove(usize(regs.rcx - 1))); + TASKING.current_process(|process| { + process.address_spaces().lock().remove(usize(regs.rcx - 1)) + }); } 6 => 'call6: { let Ok(page) = Page::from_start_address(VirtAddr::new(regs.rdx)) else { @@ -414,12 +468,13 @@ extern "C" fn syscall_handler() { let failed = if regs.rcx == 0 { ACTIVE_SPACE.lock().map_assert_unused(page, num_pages, flags).is_err() } else { - TASKING.address_spaces_mut(|x| { + TASKING.current_process(|process| { + let mut address_spaces = process.address_spaces().lock(); #[warn( clippy::arithmetic_side_effects, reason = "FIXME: The current address space should be usize::MAX as that is an invalid index, instead of 0." )] - if let Some(space) = x.get_mut(usize(regs.rcx - 1)) { + if let Some(space) = address_spaces.get_mut(usize(regs.rcx - 1)) { space.map_assert_unused(page, num_pages, flags).is_err() } else { true @@ -432,35 +487,38 @@ extern "C" fn syscall_handler() { if let Some(buffer) = get_buffer(regs.rdx) { let len = usize(regs.rdi); assert!(len <= buffer.len()); - TASKING.address_spaces_mut(|x| { + let err = TASKING.current_process(|process| { + let mut address_spaces = process.address_spaces().lock(); #[warn( clippy::arithmetic_side_effects, reason = "FIXME: The current address space should be usize::MAX as that is an invalid index, instead of 0." )] - if let Some(space) = x.get_mut(usize(regs.rcx - 1)) { - let buffer_num_pages = buffer.len() / 4096; - let buffer_raw = Box::into_raw(buffer); - let page = ACTIVE_SPACE - .lock() - .move_mappings_free( - Page::from_start_address(VirtAddr::new(u64( - buffer_raw.expose_provenance() - ))) - .unwrap(), - buffer_num_pages, - space, - ) - .unwrap(); + if let Some(space) = address_spaces.get_mut(usize(regs.rcx - 1)) { + let buffer_num_pages = buffer.len() / 4096; + let buffer_raw = Box::into_raw(buffer); + let page = ACTIVE_SPACE + .lock() + .move_mappings_free( + Page::from_start_address(VirtAddr::new(u64( + buffer_raw.expose_provenance() + ))).map_err(|_| ())?, + buffer_num_pages, + space, + ).map_err(|_| ())?; space.run(|| unsafe { (ptr::with_exposed_provenance_mut::(usize(regs.rsi))) .copy_from(page.start_address().as_mut_ptr::(), len); }); - space.unmap(page, buffer_num_pages).unwrap(); - retval = 0; + space.unmap(page, buffer_num_pages).map_err(|_| ())?; + Ok(()) } else { - retval = 1; + Err(()) } - }); + }).is_err(); + #[expect(clippy::as_conversions, reason = "Needed to convert bool to u64")] + { + retval = err as u64; + } } else { retval = 1; } @@ -470,7 +528,9 @@ extern "C" fn syscall_handler() { clippy::arithmetic_side_effects, reason = "FIXME: The current address space should be usize::MAX as that is an invalid index, instead of 0." )] - let space = TASKING.address_spaces_mut(|x| x.remove(usize(regs.rdx - 1))); + let space = TASKING.current_process(|process| { + process.address_spaces().lock().remove(usize(regs.rdx - 1)) + }); let res = TASKING.new_process(ptr::with_exposed_provenance(usize(regs.rcx)), space); if let Ok(pid) = res { retval = 0; @@ -500,12 +560,12 @@ extern "C" fn syscall_handler() { if let Some(buffer) = get_buffer(regs.rdx) { let len = usize(regs.rsi); assert!(len <= buffer.len()); - if TASKING.message_queue_mut(pid, |_| ()).is_ok() { - send_ipc_to(pid, buffer, len); - retval = 0; - } else { - println!("ipc_send: Bad PID ({})", pid); - retval = 1; + match send_ipc_to(pid, buffer, len) { + Ok(()) => retval = 0, + Err(e) => { + println!("ipc_send: Error {e:?}"); + retval = 1; + } } } else { println!("ipc_send: Bad buffer ({})", regs.rdx); @@ -513,13 +573,16 @@ extern "C" fn syscall_handler() { } } 12 => { - if let Some(msg) = TASKING.current_message_queue_mut(|x| x.pop()) { + if let Some(msg) = + TASKING.current_process(|process| process.message_queue().lock().pop()) + { #[expect( clippy::unwrap_used, reason = "The message queue only contains valid buffer IDs" )] - let buffer_addr = - u64(TASKING.data_buffers_mut(|x| *x.get(msg.0).unwrap()).expose_provenance()); + let buffer_addr = u64(TASKING + .current_process(|process| *process.data_buffers().lock().get(msg.0).unwrap()) + .expose_provenance()); retval2 = u64(msg.1); retval = buffer_addr; retval3 = u64(msg.0); @@ -545,12 +608,13 @@ extern "C" fn syscall_handler() { let failed = if regs.rcx == 0 { ACTIVE_SPACE.lock().map_only_unused(page, num_pages, flags).is_err() } else { - TASKING.address_spaces_mut(|x| { + TASKING.current_process(|process| { + let mut address_spaces = process.address_spaces().lock(); #[warn( clippy::arithmetic_side_effects, reason = "FIXME: The current address space should be usize::MAX as that is an invalid index, instead of 0." )] - if let Some(space) = x.get_mut(usize(regs.rcx - 1)) { + if let Some(space) = address_spaces.get_mut(usize(regs.rcx - 1)) { space.map_only_unused(page, num_pages, flags).is_err() } else { true @@ -569,7 +633,9 @@ extern "C" fn syscall_handler() { buffer.resize(rounded_size, 0); let buffer = buffer.into_boxed_slice(); let buffer = Box::into_raw(buffer); - retval = u64(TASKING.data_buffers_mut(|x| x.insert(buffer))); + retval = u64( + TASKING.current_process(|process| process.data_buffers().lock().insert(buffer)) + ); retval2 = u64(buffer.cast::().expose_provenance()); retval3 = u64(rounded_size); } @@ -579,45 +645,56 @@ extern "C" fn syscall_handler() { clippy::arithmetic_side_effects, reason = "FIXME: The current address space should be usize::MAX as that is an invalid index, instead of 0." )] - TASKING.address_spaces_mut(|x| { - let space = x.get_mut(usize(regs.rcx - 1)).expect("Invalid address space"); - let slice_start: *mut u8 = ptr::with_exposed_provenance_mut(usize(regs.rdx)); - space.run(|| unsafe { - slice::from_raw_parts_mut(slice_start, usize(regs.rsi)).fill(0); - }); - }); - - retval = 0; + let err = TASKING + .current_process(|process| { + let mut address_spaces = process.address_spaces().lock(); + let space = address_spaces.get_mut(usize(regs.rcx - 1)).ok_or(())?; + let slice_start: *mut u8 = ptr::with_exposed_provenance_mut(usize(regs.rdx)); + space.run(|| unsafe { + slice::from_raw_parts_mut(slice_start, usize(regs.rsi)).fill(0); + }); + Ok::<_, ()>(()) + }) + .is_err(); + #[expect(clippy::as_conversions, reason = "Needed to convert bool to u64")] + { + retval = err as u64; + } } 18 => { - if TASKING.current_message_queue_mut(|x| x.is_empty()) { + if TASKING.current_process(|process| process.message_queue().lock().is_empty()) { TASKING.sleep(SleepReason::WaitingForIPC); } } 19 => (), - 20 => { + 20 => 'call20: { + let Ok(frame) = PhysFrame::from_start_address(PhysAddr::new(regs.rdx)) else { + retval = 0; + break 'call20; + }; retval = if regs.rcx == 0 { unsafe { ACTIVE_SPACE .lock() .map_free_to( - PhysFrame::from_start_address(PhysAddr::new(regs.rdx)).unwrap(), + frame, usize(regs.rsi), PageTableFlags::from_bits_truncate(regs.rdi), ) .map_or(0, |x| u64(x.expose_provenance())) } } else { - TASKING.address_spaces_mut(|x| { + TASKING.current_process(|process| { + let mut address_spaces = process.address_spaces().lock(); #[warn( clippy::arithmetic_side_effects, reason = "FIXME: The current address space should be usize::MAX as that is an invalid index, instead of 0." )] - if let Some(space) = x.get_mut(usize(regs.rcx - 1)) { + if let Some(space) = address_spaces.get_mut(usize(regs.rcx - 1)) { unsafe { space .map_free_to( - PhysFrame::from_start_address(PhysAddr::new(regs.rdx)).unwrap(), + frame, usize(regs.rsi), PageTableFlags::from_bits_truncate(regs.rdi), ) @@ -641,12 +718,13 @@ extern "C" fn syscall_handler() { retval2 = start_phys; start_virt } else { - TASKING.address_spaces_mut(|x| { + TASKING.current_process(|process| { + let mut address_spaces = process.address_spaces().lock(); #[warn( clippy::arithmetic_side_effects, reason = "FIXME: The current address space should be usize::MAX as that is an invalid index, instead of 0." )] - if let Some(space) = x.get_mut(usize(regs.rcx - 1)) { + if let Some(space) = address_spaces.get_mut(usize(regs.rcx - 1)) { let (start_virt, start_phys) = space .map_free_cont_phys( usize(regs.rdx), @@ -671,12 +749,13 @@ extern "C" fn syscall_handler() { retval = if regs.rcx == 0 { u64::from(ACTIVE_SPACE.lock().unmap(page, usize(regs.rsi)).is_err()) } else { - TASKING.address_spaces_mut(|x| { + TASKING.current_process(|process| { + let mut address_spaces = process.address_spaces().lock(); #[warn( clippy::arithmetic_side_effects, reason = "FIXME: The current address space should be usize::MAX as that is an invalid index, instead of 0." )] - if let Some(space) = x.get_mut(usize(regs.rcx - 1)) { + if let Some(space) = address_spaces.get_mut(usize(regs.rcx - 1)) { u64::from(space.unmap(page, usize(regs.rsi)).is_err()) } else { 1 @@ -687,7 +766,17 @@ extern "C" fn syscall_handler() { 23 => { let irq_num = regs.rcx; if irq_num < 16 { - IRQ_TASKS.write()[usize(irq_num)] = Some(TASKING.current_pid().unwrap()); + #[expect( + clippy::indexing_slicing, + reason = "The index has been checked to be in bounds above" + )] + IRQ_TASKS.write()[usize(irq_num)] = Some( + #[expect( + clippy::unwrap_used, + reason = "Syscalls cannot be called during early boot, the only time when there is no current PID" + )] + TASKING.current_pid().unwrap(), + ); retval = 0; } else { retval = 1; diff --git a/src/physical_memory.rs b/src/physical_memory.rs index d19258f..b9755df 100644 --- a/src/physical_memory.rs +++ b/src/physical_memory.rs @@ -96,28 +96,33 @@ impl FrameDeallocator for PhysicalMemory { impl PhysicalMemory { pub fn allocate_frame_range(&mut self, len: usize) -> Option { - self.bytes_used = self.bytes_used.saturating_add(4096 * len); - if self.bytes_used > self.peak_used { - self.peak_used = self.bytes_used; - } if len >= 0x10_0000_0000_0000 { return None; } - #[expect(clippy::arithmetic_side_effects, reason = "The check above makes sure the multiplication cannot overflow.")] + #[expect( + clippy::arithmetic_side_effects, + reason = "The check above makes sure the multiplication cannot overflow." + )] + let byte_len = 4096 * len; + self.bytes_used = self.bytes_used.saturating_add(byte_len); + if self.bytes_used > self.peak_used { + self.peak_used = self.bytes_used; + } #[expect(clippy::unwrap_used, reason = "4096 is a nonzero power of two, and len is already a multiple of 4096 so rounding cannot overflow.")] - self.alloc.allocate_first_fit(Layout::from_size_align(4096 * len, 4096).unwrap()).ok().map(|(ptr, _)| { + self.alloc.allocate_first_fit(Layout::from_size_align(byte_len, 4096).unwrap()).ok().map(|(ptr, _)| { #[expect(clippy::unwrap_used, reason = "PhysFrame requires its argument to be 4k aligned, which is guaranteed by the allocator")] #[expect(clippy::arithmetic_side_effects, reason = "All addresses passed to the allocator are > PHYS_OFFSET, so this cannot underflow")] let start = PhysFrame::from_start_address(PhysAddr::new( (u64(ptr.as_ptr().expose_provenance())) - PHYS_OFFSET.as_u64(), )) .unwrap(); - #[expect(clippy::unwrap_used, reason = "PhysFrame requires its argument to be 4k aligned, which is guaranteed by the allocator")] - #[expect(clippy::arithmetic_side_effects, reason = "All addresses passed to the allocator are > PHYS_OFFSET, so this cannot underflow")] - let end = PhysFrame::from_start_address(PhysAddr::new( - (u64(ptr.as_ptr().expose_provenance()) + (u64(len) * 4096)) - PHYS_OFFSET.as_u64(), - )) - .unwrap(); + #[expect(clippy::arithmetic_side_effects, reason = " + The pointer returned by the allocator is guaranteed to be a pointer to a contiguous block of len frames, + so this could only overflow if the range included the last physical frame. But since the allocator gets + passed regions from PhysFrameRanges, which can't include the last frame without overflow, that can't + happen and thus this can't overflow. + ")] + let end = start + u64(len); PhysFrameRange { start, end diff --git a/src/tasking.rs b/src/tasking.rs index 6e97f2e..ed8ec90 100644 --- a/src/tasking.rs +++ b/src/tasking.rs @@ -12,7 +12,6 @@ use core::{ sync::atomic::{AtomicBool, Ordering}, }; use crossbeam_queue::SegQueue; -//use humansize::{SizeFormatter, BINARY}; use slab::Slab; use spin::{Lazy, Mutex, RwLock}; use x86_64::{ @@ -86,7 +85,7 @@ pub enum SleepReason { } #[derive(Debug)] -struct Process { +pub struct Process { address_space: Option, kernel_stack: Box<[usize], &'static ASpaceMutex>, kernel_esp: *mut usize, @@ -97,6 +96,24 @@ struct Process { sleeping: RwLock>, } +impl Process { + pub fn address_spaces(&self) -> &Mutex> { + &self.address_spaces + } + + pub fn data_buffers(&self) -> &Mutex> { + &self.data_buffers + } + + pub fn message_queue(&self) -> &Mutex> { + &self.message_queue + } + + pub fn address_space_mut(&mut self) -> Option<&mut AddressSpace> { + self.address_space.as_mut() + } +} + unsafe impl Send for Process {} unsafe impl Sync for Process {} @@ -165,36 +182,33 @@ impl Tasking { sleeping: RwLock::new(Some(SleepReason::NewProcess)), }); if let Some(&proc_man_pid) = REGISTERD_PIDS.read().get(&3) { - let mut len: usize; - let rounded_size = 32usize.next_multiple_of(4096); - let mut buffer = Vec::with_capacity_in(rounded_size, &*ACTIVE_SPACE); - buffer.resize(rounded_size, 0); - let mut buffer = buffer.into_boxed_slice(); - buffer[0..8].copy_from_slice(&u64::MAX.to_le_bytes()); - buffer[8..10].copy_from_slice(&0u16.to_le_bytes()); - buffer[10] = 0; - buffer[11..19].copy_from_slice(&0u64.to_le_bytes()); - buffer[19..21].copy_from_slice(&8u16.to_le_bytes()); - buffer[21..23].copy_from_slice(&6u16.to_le_bytes()); - len = 23; - len += unsigned_varint::encode::u64( - u64(pid), - (&mut buffer[len..len + 10]).try_into().unwrap(), - ) - .len(); + let mut varint_buf = unsigned_varint::encode::u64_buffer(); + let mut buffer = Vec::new_in(&*ACTIVE_SPACE); + buffer.extend_from_slice(&u64::MAX.to_le_bytes()); + buffer.extend_from_slice(&0u16.to_le_bytes()); + buffer.push(0); + buffer.extend_from_slice(&0u64.to_le_bytes()); + buffer.extend_from_slice(&8u16.to_le_bytes()); + buffer.extend_from_slice(&6u16.to_le_bytes()); + buffer.extend_from_slice(unsigned_varint::encode::u64(u64(pid), &mut varint_buf)); if let Some(current_pid) = *(self.current_pid.read()) { - buffer[len] = 1; - len += 1; - len += unsigned_varint::encode::u64( + buffer.push(1); + buffer.extend_from_slice(unsigned_varint::encode::u64( u64(current_pid), - (&mut buffer[len..len + 10]).try_into().unwrap(), - ) - .len(); + &mut varint_buf, + )); } else { - buffer[len] = 0; - len += 1; + buffer.push(0); } - send_ipc_to(usize(proc_man_pid), buffer, len); + let len = buffer.len(); + buffer.resize(len.next_multiple_of(4096), 0); + let buffer = buffer.into_boxed_slice(); + #[expect( + clippy::expect_used, + reason = "The tasking code in the kernel and proc_man CANNOT lose sync. Failure to communicate is fatal." + )] + send_ipc_to(usize(proc_man_pid), buffer, len) + .expect("Failed to send exit message to proc_man"); } else { println!("[TASKING] No process manager when creating PID {pid}"); } @@ -215,51 +229,47 @@ impl Tasking { pub fn task_yield(&self) { loop { self.freeable_kstacks.lock().clear(); - let Some(current_pid) = *self.current_pid.read() else { + let Some(current_pid) = self.current_pid() else { self.wfi_loop.store(false, Ordering::Relaxed); break; }; let next_process_pid = self.ready_to_run.lock().pop_front(); if let Some(next_process_pid) = next_process_pid { self.wfi_loop.store(false, Ordering::Relaxed); - if next_process_pid == self.current_pid().unwrap() { - println!("Yielding to currect process! Returning"); + if Some(next_process_pid) == self.current_pid() { + println!("Yielding to current process! Returning"); break; } - #[expect( - clippy::expect_used, - reason = "This expect checks a critical invariant. If this fails, the kernel MUST panic" - )] - #[warn(clippy::indexing_slicing, reason = "FIXME(?)")] - let current_address_space = self.processes.write()[next_process_pid] - .address_space - .take() - .expect("Non-current process has active page table") - .activate(); - #[warn(clippy::indexing_slicing, reason = "FIXME(?)")] - self.processes.write()[current_pid].address_space = Some(current_address_space); - let processes = self.processes.read(); - #[warn(clippy::indexing_slicing, reason = "FIXME(?)")] - let current_process = &processes[current_pid]; - #[warn(clippy::indexing_slicing, reason = "FIXME(?)")] - let next_process = &processes[next_process_pid]; - gdt::set_tss_stack(next_process.kernel_esp_top); - if current_process.sleeping.read().is_none() { + #[warn(clippy::unwrap_used, reason = "FIXME(?)")] + let current_address_space = self + .process_mut(next_process_pid, |process| { + #[expect( + clippy::expect_used, + reason = "This expect checks a critical invariant. If this fails, the kernel MUST panic" + )] + process + .address_space + .take() + .expect("Non-current process has active page table") + .activate() + }) + .unwrap(); + self.current_process_mut(|process| { + process.address_space = Some(current_address_space); + }); + if self.current_process(|process| process.sleeping.read().is_none()) { self.ready_to_run.lock().push_back(current_pid); } - let kernel_esp = next_process.kernel_esp; - let previous_process = current_pid; + let curr_stack = + self.current_process_mut(|process| addr_of_mut!(process.kernel_esp)); *self.current_pid.write() = Some(next_process_pid); - core::mem::drop(processes); - let mut processes = self.processes.write(); - #[warn(clippy::indexing_slicing, reason = "FIXME(?)")] - let curr_stack = addr_of_mut!(processes[previous_process].kernel_esp); - core::mem::drop(processes); + let kernel_esp = self.current_process(|process| { + gdt::set_tss_stack(process.kernel_esp_top); + process.kernel_esp + }); switch_to_asm(curr_stack, kernel_esp); break; - } else if #[warn(clippy::indexing_slicing, reason = "FIXME(?)")] - self.processes.read()[current_pid].sleeping.read().is_some() - { + } else if self.current_process(|process| process.sleeping.read().is_some()) { self.wfi_loop.store(true, Ordering::Relaxed); x86_64::instructions::interrupts::enable_and_hlt(); x86_64::instructions::interrupts::disable(); @@ -275,28 +285,30 @@ impl Tasking { } pub fn exit(&self, code: u8) -> ! { - if let Some(current_pid) = *self.current_pid.read() { + if let Some(current_pid) = self.current_pid() { if let Some(&proc_man_pid) = REGISTERD_PIDS.read().get(&3) { - let mut len: usize; - let rounded_size = 32usize.next_multiple_of(4096); - let mut buffer = Vec::with_capacity_in(rounded_size, &*ACTIVE_SPACE); - buffer.resize(rounded_size, 0); - let mut buffer = buffer.into_boxed_slice(); - buffer[0..8].copy_from_slice(&u64::MAX.to_le_bytes()); - buffer[8..10].copy_from_slice(&0u16.to_le_bytes()); - buffer[10] = 0; - buffer[11..19].copy_from_slice(&0u64.to_le_bytes()); - buffer[19..21].copy_from_slice(&8u16.to_le_bytes()); - buffer[21..23].copy_from_slice(&4u16.to_le_bytes()); - len = 23; - len += unsigned_varint::encode::u64( + let mut varint_buf = unsigned_varint::encode::u64_buffer(); + let mut buffer = Vec::new_in(&*ACTIVE_SPACE); + buffer.extend_from_slice(&u64::MAX.to_le_bytes()); + buffer.extend_from_slice(&0u16.to_le_bytes()); + buffer.push(0); + buffer.extend_from_slice(&0u64.to_le_bytes()); + buffer.extend_from_slice(&8u16.to_le_bytes()); + buffer.extend_from_slice(&4u16.to_le_bytes()); + buffer.extend_from_slice(unsigned_varint::encode::u64( u64(current_pid), - (&mut buffer[len..len + 10]).try_into().unwrap(), - ) - .len(); - buffer[len] = code; - len += 1; - send_ipc_to(usize(proc_man_pid), buffer, len); + &mut varint_buf, + )); + buffer.push(code); + let len = buffer.len(); + buffer.resize(len.next_multiple_of(4096), 0); + let buffer = buffer.into_boxed_slice(); + #[expect( + clippy::expect_used, + reason = "The tasking code in the kernel and proc_man CANNOT lose sync. Failure to communicate is fatal." + )] + send_ipc_to(usize(proc_man_pid), buffer, len) + .expect("Failed to send exit message to proc_man"); } else { println!( "[TASKING] No process manager when PID {} exited with code {code}", @@ -309,25 +321,26 @@ impl Tasking { if let Some(next_process_pid) = next_process_pid { self.wfi_loop.store(false, Ordering::Relaxed); #[warn(clippy::indexing_slicing, reason = "FIXME(?)")] - let mut processes = self.processes.write(); - if let Some(current_pid) = *self.current_pid.read() { - *processes[current_pid].sleeping.write() = Some(SleepReason::Exited); + if self.current_pid.read().is_some() { + self.current_process(|process| { + *process.sleeping.write() = Some(SleepReason::Exited); + }); } - #[warn(clippy::indexing_slicing, reason = "FIXME(?)")] - let next_process = &mut processes[next_process_pid]; - #[expect( - clippy::expect_used, - reason = "This expect checks a critical invariant. If this fails, the kernel MUST panic" - )] - next_process - .address_space - .take() - .expect("Non-current process has active page table") - .activate(); - gdt::set_tss_stack(next_process.kernel_esp_top); - let kernel_esp = next_process.kernel_esp; *self.current_pid.write() = Some(next_process_pid); - core::mem::drop(processes); + let kernel_esp = self + .current_process_mut(|process| { + #[expect( + clippy::expect_used, + reason = "This expect checks a critical invariant. If this fails, the kernel MUST panic" + )] + process + .address_space + .take() + .expect("Non-current process has active page table") + .activate(); + gdt::set_tss_stack(process.kernel_esp_top); + process.kernel_esp + }); switch_to_asm_exit(kernel_esp); unreachable!() } else { @@ -351,67 +364,36 @@ impl Tasking { Ok(()) } - pub fn address_spaces_mut) -> T, T>(&self, func: F) -> T { + pub fn current_process T, T>(&self, func: F) -> T { let processes = self.processes.read(); #[warn(clippy::unwrap_used, reason = "FIXME")] #[warn(clippy::indexing_slicing, reason = "FIXME(?)")] - let mut aspaces = processes[self.current_pid.read().unwrap()].address_spaces.lock(); - func(&mut aspaces) + func(&processes[self.current_pid().unwrap()]) } - pub fn data_buffers_mut) -> T, T>(&self, func: F) -> T { - let processes = self.processes.read(); + pub fn current_process_mut T, T>(&self, func: F) -> T { + let mut processes = self.processes.write(); #[warn(clippy::unwrap_used, reason = "FIXME")] #[warn(clippy::indexing_slicing, reason = "FIXME(?)")] - let mut dbufs = processes[self.current_pid.read().unwrap()].data_buffers.lock(); - func(&mut dbufs) + func(&mut processes[self.current_pid().unwrap()]) } - pub fn proc_data_buffers_mut) -> T, T>( - &self, - pid: usize, - func: F, - ) -> Result { - let processes = self.processes.read(); - let mut dbufs = processes.get(pid).ok_or(InvalidPid)?.data_buffers.lock(); - Ok(func(&mut dbufs)) - } - - pub fn current_message_queue_mut) -> T, T>( - &self, - func: F, - ) -> T { - let processes = self.processes.read(); - #[warn(clippy::unwrap_used, reason = "FIXME")] - #[warn(clippy::indexing_slicing, reason = "FIXME(?)")] - let mut queue = processes[self.current_pid.read().unwrap()].message_queue.lock(); - func(&mut queue) - } - - pub fn message_queue_mut) -> T, T>( - &self, - pid: usize, - func: F, - ) -> Result { - let processes = self.processes.read(); - let mut queue = processes.get(pid).ok_or(InvalidPid)?.message_queue.lock(); - Ok(func(&mut queue)) - } - - pub fn address_space_mut) -> T, T>( + pub fn process_mut T, T>( &self, pid: usize, func: F, ) -> Result { let mut processes = self.processes.write(); - let aspace = processes.get_mut(pid).ok_or(InvalidPid)?.address_space.as_mut(); - Ok(func(aspace)) + #[warn(clippy::unwrap_used, reason = "FIXME")] + Ok(func(processes.get_mut(pid).ok_or(InvalidPid)?)) } pub fn sleep(&self, reason: SleepReason) { #[warn(clippy::unwrap_used, reason = "FIXME")] #[warn(clippy::indexing_slicing, reason = "FIXME(?)")] - *self.processes.read()[self.current_pid.read().unwrap()].sleeping.write() = Some(reason); + self.current_process(|process| { + *process.sleeping.write() = Some(reason); + }); self.task_yield(); } diff --git a/src/virtual_memory.rs b/src/virtual_memory.rs index b407741..647599d 100644 --- a/src/virtual_memory.rs +++ b/src/virtual_memory.rs @@ -45,7 +45,7 @@ impl fmt::Debug for AddressSpace { } } -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub enum PagingError { RequestInvalid, ParentEntryHugePage, @@ -171,6 +171,10 @@ impl<'a> PmemCountingWrapper<'a> { } unsafe impl FrameAllocator for PmemCountingWrapper<'_> { + #[expect( + clippy::arithmetic_side_effects, + reason = "This can never overflow as there are a theoretical maximum of usize::MAX/4096 physical frames." + )] fn allocate_frame(&mut self) -> Option { if let Some(frame) = self.pmem.allocate_frame() { self.count += 1; @@ -382,8 +386,13 @@ impl AddressSpace { PhysFrame::from_start_address_unchecked(PhysAddr::new(0x000F_FFFF_FFFF_F000)) }; self.check_request_valid(page, num_pages)?; - if self.bump_base < page + u64(num_pages) { - self.bump_base = page + u64(num_pages); + #[expect( + clippy::arithmetic_side_effects, + reason = "check_request_valid guarentees this won't overflow" + )] + let req_end = page + u64(num_pages); + if self.bump_base < req_end { + self.bump_base = req_end; } #[expect( clippy::arithmetic_side_effects, @@ -428,6 +437,10 @@ impl AddressSpace { &mut pmem_wrap, )? .flush(); + #[expect( + clippy::arithmetic_side_effects, + reason = "This can never overflow, as there are a theoretical maximum of usize::MAX/4096 frames." + )] self.record_alloc(pmem_wrap.count * 4096); } } @@ -461,14 +474,15 @@ impl AddressSpace { flags: PageTableFlags, ) -> Result<*mut u8, PagingError> { self.check_request_valid(page, num_pages)?; - if self.bump_base < page + u64(num_pages) { - self.bump_base = page + u64(num_pages); - } #[expect( clippy::arithmetic_side_effects, reason = "check_request_valid guarentees this won't overflow" )] - for page in (PageRange { start: page, end: page + u64(num_pages) }) { + let req_end = page + u64(num_pages); + if self.bump_base < req_end { + self.bump_base = req_end; + } + for page in (PageRange { start: page, end: req_end }) { unsafe { let mut phys_mem = PHYSICAL_MEMORY.lock(); let frame = phys_mem.allocate_frame().ok_or(PagingError::FrameAllocationFailed)?; @@ -493,6 +507,10 @@ impl AddressSpace { &mut pmem_wrap, )? .flush(); + #[expect( + clippy::arithmetic_side_effects, + reason = "This can never overflow, as there are a theoretical maximum of usize::MAX/4096 frames." + )] self.record_alloc(pmem_wrap.count * 4096); } } @@ -532,10 +550,19 @@ impl AddressSpace { .allocate_frame_range(num_pages) .ok_or(PagingError::FrameAllocationFailed)?; let phys_start = frame_range.start.start_address().as_u64(); - unsafe { + let res = unsafe { self.map_to(page, frame_range.start, num_pages, flags | PageTableFlags::WRITABLE) - .map(|ptr| (ptr, phys_start)) - } + .map(|ptr| (ptr, phys_start))? + }; + #[expect( + clippy::arithmetic_side_effects, + reason = " + This can never overflow, as there are a theoretical maximum of usize::MAX/4096 pages, + and check_request_valid makes sure that num_pages is below that max. + " + )] + self.record_alloc(num_pages * 4096); + Ok(res) } /// Maps new virtual pages and returns the starting addresss @@ -569,7 +596,6 @@ impl AddressSpace { // SAFETY: &mut aliasing is prevented by using free physical frames, and uninitialized // values are prevented by using free virtual pages. let ptr = unsafe { self.map(start_page, num_pages, flags)? }; - self.record_alloc(num_pages * 4096); Ok(ptr) } @@ -586,7 +612,6 @@ impl AddressSpace { // SAFETY: &mut aliasing is prevented by using free physical frames, and uninitialized // values are prevented by using free virtual pages. let ptr = unsafe { self.map_cont_phys(start_page, num_pages, flags)? }; - self.record_alloc(num_pages * 4096); Ok(ptr) } @@ -600,7 +625,6 @@ impl AddressSpace { ) -> Result<*mut u8, PagingError> { self.check_request_unmapped(page, num_pages)?; let ptr = unsafe { self.map(page, num_pages, flags)? }; - self.record_alloc(num_pages * 4096); Ok(ptr) } @@ -612,16 +636,16 @@ impl AddressSpace { num_pages: usize, flags: PageTableFlags, ) -> Result<*mut u8, PagingError> { - self.check_request_valid(page, num_pages)?; - if self.bump_base < page + u64(num_pages) { - self.bump_base = page + u64(num_pages); - } - assert!(!self.alloc_force_user); #[expect( clippy::arithmetic_side_effects, reason = "check_request_valid guarentees this won't overflow" )] - for page in (PageRange { start: page, end: page + u64(num_pages) }) { + let req_end = page + u64(num_pages); + if self.bump_base < req_end { + self.bump_base = req_end; + } + assert!(!self.alloc_force_user); + for page in (PageRange { start: page, end: req_end }) { if self.translate_addr(page.start_address()).is_some() { continue; } @@ -645,6 +669,10 @@ impl AddressSpace { &mut pmem_wrap, )? .flush(); + #[expect( + clippy::arithmetic_side_effects, + reason = "This can never overflow, as there are a theoretical maximum of usize::MAX/4096 frames." + )] self.record_alloc(pmem_wrap.count * 4096); } } @@ -775,12 +803,25 @@ impl AddressSpace { }; } Err(PagingError::PageAllocationFailed) - } else if USER_PAGE_RANGE.end - self.bump_base < u64(num_pages) { - Err(PagingError::PageAllocationFailed) } else { - let old_base = self.bump_base; - self.bump_base += u64(num_pages); - Ok(old_base) + #[expect( + clippy::arithmetic_side_effects, + reason = "bump_base can never be greater than USER_PAGE_RANGE.end" + )] + let num_pages_free = USER_PAGE_RANGE.end - self.bump_base; + if num_pages_free < u64(num_pages) { + Err(PagingError::PageAllocationFailed) + } else { + let old_base = self.bump_base; + #[expect( + clippy::arithmetic_side_effects, + reason = "bump_base can never be greater than USER_PAGE_RANGE.end, and num_pages can never push it over that due to the check above" + )] + { + self.bump_base += u64(num_pages); + } + Ok(old_base) + } } }