From bf1a81cf8265abe16910ca3b6d0fc90e7fbe416f Mon Sep 17 00:00:00 2001 From: Zoe Date: Mon, 7 Sep 2026 13:31:49 -0500 Subject: [PATCH] refactor: large code cleanup and safety pass --- Makefile | 2 +- src/arch/x86_64/interrupts/apic_vectors.rs | 53 +- src/arch/x86_64/interrupts/exceptions.rs | 136 ++--- src/arch/x86_64/interrupts/idt.rs | 151 ++++- src/arch/x86_64/paging.rs | 64 ++- src/format/cpio.rs | 4 + src/main.rs | 1 - src/memory/address_space.rs | 44 +- src/memory/mod.rs | 6 +- src/memory/stack.rs | 7 +- src/memory/user.rs | 64 ++- src/syscall/mod.rs | 16 +- src/syscall/table.rs | 607 +++++++++++++++------ src/task/bootstrap.rs | 14 +- src/task/scheduler.rs | 80 ++- src/task/tcb.rs | 73 ++- userspace/client/src/main.rs | 8 +- userspace/dusk-sys/src/lib.rs | 61 ++- userspace/echo/src/main.rs | 6 +- userspace/omega3/src/main.rs | 17 +- 20 files changed, 1026 insertions(+), 388 deletions(-) diff --git a/Makefile b/Makefile index 541f200..622faf8 100644 --- a/Makefile +++ b/Makefile @@ -137,7 +137,7 @@ compile-binaries: ovmf-x86_64: mkdir -p ovmf/ovmf-x86_64 - @if [ ! -d "ovmf/ovmf-x86_64/OVMF.fd" ]; then \ + @if [ ! -f "ovmf/ovmf-x86_64/OVMF.fd" ]; then \ cd ovmf/ovmf-x86_64 && curl -Lo OVMF.fd https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF.fd; \ fi diff --git a/src/arch/x86_64/interrupts/apic_vectors.rs b/src/arch/x86_64/interrupts/apic_vectors.rs index 41c2b65..c58c0f9 100644 --- a/src/arch/x86_64/interrupts/apic_vectors.rs +++ b/src/arch/x86_64/interrupts/apic_vectors.rs @@ -1,35 +1,40 @@ -use crate::arch::{ - apic, timer, - x86_64::interrupts::idt::{self, InterruptStackFrame}, -}; +use super::idt::{self, InterruptFrame, stub_no_err}; +use crate::arch::{apic, timer}; pub const PIT_CALIBRATION_VECTOR: u8 = 0xF1; pub const APIC_TIMER_VECTOR: u8 = 0xFD; pub const APIC_ERROR_VECTOR: u8 = 0xFE; pub const APIC_SPURIOUS_VECTOR: u8 = 0xFF; -extern "x86-interrupt" fn error_handler(_frame: InterruptStackFrame) { - apic::record_error(); - apic::end_of_interrupt(); -} +stub_no_err!(stub_pit_calibration, 0xF1); +stub_no_err!(stub_apic_timer, 0xFD); +stub_no_err!(stub_apic_error, 0xFE); +stub_no_err!(stub_apic_spurious, 0xFF); -extern "x86-interrupt" fn timer_handler(_frame: InterruptStackFrame) { - apic::record_timer(); - apic::end_of_interrupt(); -} - -extern "x86-interrupt" fn pit_calibration_handler(_frame: InterruptStackFrame) { - timer::record_pit_calibration(); - apic::end_of_interrupt(); -} - -extern "x86-interrupt" fn spurious_handler(_frame: InterruptStackFrame) { - // No EOI +pub(super) fn handle(frame: &mut InterruptFrame) { + match frame.vector as u8 { + PIT_CALIBRATION_VECTOR => { + timer::record_pit_calibration(); + apic::end_of_interrupt(); + } + APIC_TIMER_VECTOR => { + apic::record_timer(); + apic::end_of_interrupt(); + } + APIC_ERROR_VECTOR => { + apic::record_error(); + apic::end_of_interrupt(); + } + APIC_SPURIOUS_VECTOR => { + // No EOI + } + _ => {} + } } pub(super) fn install(idt: &mut idt::Idt) { - idt.set_handler(PIT_CALIBRATION_VECTOR, pit_calibration_handler, 0); - idt.set_handler(APIC_ERROR_VECTOR, error_handler, 0); - idt.set_handler(APIC_TIMER_VECTOR, timer_handler, 0); - idt.set_handler(APIC_SPURIOUS_VECTOR, spurious_handler, 0); + idt.set_handler(PIT_CALIBRATION_VECTOR, stub_pit_calibration, 0); + idt.set_handler(APIC_ERROR_VECTOR, stub_apic_error, 0); + idt.set_handler(APIC_TIMER_VECTOR, stub_apic_timer, 0); + idt.set_handler(APIC_SPURIOUS_VECTOR, stub_apic_spurious, 0); } diff --git a/src/arch/x86_64/interrupts/exceptions.rs b/src/arch/x86_64/interrupts/exceptions.rs index 1034aec..acd3f87 100644 --- a/src/arch/x86_64/interrupts/exceptions.rs +++ b/src/arch/x86_64/interrupts/exceptions.rs @@ -1,79 +1,87 @@ use core::arch::asm; -use super::idt::{self, InterruptStackFrame}; +use super::idt::{self, InterruptFrame, InterruptStackFrame, stub_err, stub_no_err}; use crate::{hcf, println}; -macro_rules! fatal_without_error_code { - ($handler:ident, $name:literal) => { - extern "x86-interrupt" fn $handler(frame: InterruptStackFrame) { - fatal_exception($name, &frame, None); +stub_no_err!(stub_divide_error, 0); +stub_no_err!(stub_debug, 1); +stub_no_err!(stub_non_maskable_interrupt, 2); +stub_no_err!(stub_breakpoint, 3); +stub_no_err!(stub_invalid_opcode, 6); +stub_no_err!(stub_device_not_available, 7); +stub_err!(stub_double_fault, 8); +stub_err!(stub_invalid_tss, 10); +stub_err!(stub_segment_not_present, 11); +stub_err!(stub_stack_segment_fault, 12); +stub_err!(stub_general_protection, 13); +stub_err!(stub_page_fault, 14); +stub_no_err!(stub_x87_floating_point, 16); +stub_err!(stub_alignment_check, 17); +stub_no_err!(stub_machine_check, 18); +stub_no_err!(stub_simd_floating_point, 19); +stub_no_err!(stub_user_test_exit, 0x80); + +pub(super) fn handle(frame: &mut InterruptFrame) { + match frame.vector as u8 { + 0 => fatal_exception("DIVIDE ERROR", &frame.stack_frame, None), + 1 => fatal_exception("DEBUG EXCEPTION", &frame.stack_frame, None), + 2 => fatal_exception("NON-MASKABLE INTERRUPT", &frame.stack_frame, None), + 3 => report_exception("BREAKPOINT", &frame.stack_frame, None), + 6 => fatal_exception("INVALID OPCODE", &frame.stack_frame, None), + 7 => fatal_exception("DEVICE NOT AVAILABLE", &frame.stack_frame, None), + 8 => fatal_exception("DOUBLE FAULT", &frame.stack_frame, Some(frame.error_code)), + 10 => fatal_exception("INVALID TSS", &frame.stack_frame, Some(frame.error_code)), + 11 => fatal_exception("SEGMENT NOT PRESENT", &frame.stack_frame, Some(frame.error_code)), + 12 => fatal_exception("STACK-SEGMENT FAULT", &frame.stack_frame, Some(frame.error_code)), + 13 => fatal_exception( + "GENERAL PROTECTION FAULT", + &frame.stack_frame, + Some(frame.error_code), + ), + 14 => { + report_exception("PAGE FAULT", &frame.stack_frame, Some(frame.error_code)); + println!("Faulting address: {:#X}", read_cr2()); + print_page_fault_error(frame.error_code); + hcf(); } - }; -} + 16 => fatal_exception("X87 FLOATING-POINT EXCEPTION", &frame.stack_frame, None), + 17 => fatal_exception("ALIGNMENT CHECK", &frame.stack_frame, Some(frame.error_code)), + 18 => fatal_exception("MACHINE CHECK", &frame.stack_frame, None), + 19 => fatal_exception("SIMD FLOATING-POINT EXCEPTION", &frame.stack_frame, None), + 0x80 => { + if frame.stack_frame.code_segment & 0b11 != 3 { + panic!("user_test_exit_handler called from kernel"); + } -macro_rules! fatal_with_error_code { - ($handler:ident, $name:literal) => { - extern "x86-interrupt" fn $handler(frame: InterruptStackFrame, error_code: u64) { - fatal_exception($name, &frame, Some(error_code)); + println!("User test exit"); + hcf(); + } + _ => { + println!("Unhandled exception vector: {:#X}", frame.vector); + hcf(); } - }; -} - -extern "x86-interrupt" fn breakpoint_handler(frame: InterruptStackFrame) { - report_exception("BREAKPOINT", &frame, None); -} - -extern "x86-interrupt" fn page_fault_handler(frame: InterruptStackFrame, error_code: u64) { - report_exception("PAGE FAULT", &frame, Some(error_code)); - println!("Faulting address: {:#X}", read_cr2()); - print_page_fault_error(error_code); - hcf(); -} - -fatal_without_error_code!(divide_error_handler, "DIVIDE ERROR"); -fatal_without_error_code!(debug_handler, "DEBUG EXCEPTION"); -fatal_without_error_code!(non_maskable_interrupt_handler, "NON-MASKABLE INTERRUPT"); -fatal_without_error_code!(invalid_opcode_handler, "INVALID OPCODE"); -fatal_without_error_code!(device_not_available_handler, "DEVICE NOT AVAILABLE"); -fatal_without_error_code!(x87_floating_point_handler, "X87 FLOATING-POINT EXCEPTION"); -fatal_without_error_code!(machine_check_handler, "MACHINE CHECK"); -fatal_without_error_code!(simd_floating_point_handler, "SIMD FLOATING-POINT EXCEPTION"); - -fatal_with_error_code!(double_fault_handler, "DOUBLE FAULT"); -fatal_with_error_code!(invalid_tss_handler, "INVALID TSS"); -fatal_with_error_code!(segment_not_present_handler, "SEGMENT NOT PRESENT"); -fatal_with_error_code!(stack_segment_fault_handler, "STACK-SEGMENT FAULT"); -fatal_with_error_code!(general_protection_handler, "GENERAL PROTECTION FAULT"); -fatal_with_error_code!(alignment_check_handler, "ALIGNMENT CHECK"); - -extern "x86-interrupt" fn user_test_exit_handler(frame: InterruptStackFrame) { - if frame.code_segment & 0b11 != 3 { - panic!("user_test_exit_handler called from kernel"); } - - println!("User test exit"); - hcf(); } pub(super) fn install(idt: &mut idt::Idt) { - idt.set_handler(0, divide_error_handler, 0); - idt.set_handler(1, debug_handler, 0); - idt.set_handler(2, non_maskable_interrupt_handler, 0); - idt.set_user_handler(3, breakpoint_handler, 0); - idt.set_handler(6, invalid_opcode_handler, 0); - idt.set_handler(7, device_not_available_handler, 0); - idt.set_error_code_handler(8, double_fault_handler, 1); - idt.set_error_code_handler(10, invalid_tss_handler, 0); - idt.set_error_code_handler(11, segment_not_present_handler, 0); - idt.set_error_code_handler(12, stack_segment_fault_handler, 0); - idt.set_error_code_handler(13, general_protection_handler, 0); - idt.set_error_code_handler(14, page_fault_handler, 0); - idt.set_handler(16, x87_floating_point_handler, 0); - idt.set_error_code_handler(17, alignment_check_handler, 0); - idt.set_handler(18, machine_check_handler, 0); - idt.set_handler(19, simd_floating_point_handler, 0); + idt.set_handler(0, stub_divide_error, 0); + idt.set_handler(1, stub_debug, 0); + idt.set_handler(2, stub_non_maskable_interrupt, 0); + idt.set_user_handler(3, stub_breakpoint, 0); + idt.set_handler(6, stub_invalid_opcode, 0); + idt.set_handler(7, stub_device_not_available, 0); + idt.set_handler(8, stub_double_fault, 1); + idt.set_handler(10, stub_invalid_tss, 0); + idt.set_handler(11, stub_segment_not_present, 0); + idt.set_handler(12, stub_stack_segment_fault, 0); + idt.set_handler(13, stub_general_protection, 0); + idt.set_handler(14, stub_page_fault, 0); + idt.set_handler(16, stub_x87_floating_point, 0); + idt.set_handler(17, stub_alignment_check, 0); + idt.set_handler(18, stub_machine_check, 0); + idt.set_handler(19, stub_simd_floating_point, 0); - idt.set_user_handler(0x80, user_test_exit_handler, 0); + idt.set_user_handler(0x80, stub_user_test_exit, 0); } fn read_cr2() -> u64 { diff --git a/src/arch/x86_64/interrupts/idt.rs b/src/arch/x86_64/interrupts/idt.rs index fd2de2a..ae32e11 100644 --- a/src/arch/x86_64/interrupts/idt.rs +++ b/src/arch/x86_64/interrupts/idt.rs @@ -18,7 +18,7 @@ struct IdtEntry { impl IdtEntry { const fn missing() -> Self { - return Self { + Self { offset_low: 0, code_selector: 0, ist: 0, @@ -26,7 +26,7 @@ impl IdtEntry { offset_middle: 0, offset_high: 0, reserved: 0, - }; + } } } @@ -37,8 +37,8 @@ struct IdtPointer { } #[repr(C)] -#[derive(Clone, Copy)] -pub(super) struct InterruptStackFrame { +#[derive(Clone, Copy, Debug)] +pub struct InterruptStackFrame { pub instruction_pointer: VirtualAddr, pub code_segment: u64, pub cpu_flags: u64, @@ -46,14 +46,36 @@ pub(super) struct InterruptStackFrame { pub stack_segment: u64, } +#[repr(C)] +#[derive(Debug)] +pub struct InterruptFrame { + pub rax: u64, + pub rcx: u64, + pub rdx: u64, + pub rsi: u64, + pub rdi: u64, + pub r8: u64, + pub r9: u64, + pub r10: u64, + pub r11: u64, + pub rbx: u64, + pub rbp: u64, + pub r12: u64, + pub r13: u64, + pub r14: u64, + pub r15: u64, + pub vector: u64, + pub error_code: u64, + pub stack_frame: InterruptStackFrame, +} + const INTERRUPT_GATE: u8 = 0b1110; const PRESENT: u8 = 1 << 7; const KERNEL_INTERRUPT_GATE: u8 = PRESENT | INTERRUPT_GATE; const USER_DPL: u8 = 3 << 5; const USER_INTERRUPT_GATE: u8 = PRESENT | USER_DPL | INTERRUPT_GATE; -pub(super) type Handler = extern "x86-interrupt" fn(InterruptStackFrame); -pub(super) type ErrorCodeHandler = extern "x86-interrupt" fn(InterruptStackFrame, u64); +pub(super) type RawHandler = unsafe extern "C" fn(); pub(super) struct Idt { entries: [IdtEntry; 256], @@ -66,20 +88,11 @@ impl Idt { } } - pub(super) fn set_handler(&mut self, vector: u8, handler: Handler, ist: u8) { + pub(super) fn set_handler(&mut self, vector: u8, handler: RawHandler, ist: u8) { self.set_handler_address(vector, handler as usize, ist, KERNEL_INTERRUPT_GATE); } - pub(super) fn set_error_code_handler( - &mut self, - vector: u8, - handler: ErrorCodeHandler, - ist: u8, - ) { - self.set_handler_address(vector, handler as usize, ist, KERNEL_INTERRUPT_GATE); - } - - pub(super) fn set_user_handler(&mut self, vector: u8, handler: Handler, ist: u8) { + pub(super) fn set_user_handler(&mut self, vector: u8, handler: RawHandler, ist: u8) { self.set_handler_address(vector, handler as usize, ist, USER_INTERRUPT_GATE); } @@ -101,6 +114,110 @@ static mut IDT: Idt = Idt::new(); const _: () = assert!(core::mem::size_of::() == 16); const _: () = assert!(core::mem::size_of::() == 10); const _: () = assert!(core::mem::size_of::() == 40); +const _: () = assert!(core::mem::size_of::() == 176); +const _: () = assert!(core::mem::offset_of!(InterruptFrame, stack_frame) == 136); + +#[unsafe(naked)] +pub(super) unsafe extern "C" fn interrupt_common() { + core::arch::naked_asm!( + "push r15", + "push r14", + "push r13", + "push r12", + "push rbp", + "push rbx", + "push r11", + "push r10", + "push r9", + "push r8", + "push rdi", + "push rsi", + "push rdx", + "push rcx", + "push rax", + + // Check CS: bit 0 and 1 are CPL. If CPL != 0 (user mode), swapgs + "test byte ptr [rsp + 144], 3", + "jz 1f", + "swapgs", + "1:", + + "mov rdi, rsp", + "cld", + "call {dispatch}", + + // Check CS: if returning to user mode, swapgs + "test byte ptr [rsp + 144], 3", + "jz 2f", + "swapgs", + "2:", + + "pop rax", + "pop rcx", + "pop rdx", + "pop rsi", + "pop rdi", + "pop r8", + "pop r9", + "pop r10", + "pop r11", + "pop rbx", + "pop rbp", + "pop r12", + "pop r13", + "pop r14", + "pop r15", + + "add rsp, 16", + "iretq", + + dispatch = sym interrupt_dispatch, + ); +} + +extern "C" fn interrupt_dispatch(frame: &mut InterruptFrame) { + let vector = frame.vector as u8; + match vector { + 0..=31 | 0x80 => exceptions::handle(frame), + apic_vectors::PIT_CALIBRATION_VECTOR + | apic_vectors::APIC_TIMER_VECTOR + | apic_vectors::APIC_ERROR_VECTOR + | apic_vectors::APIC_SPURIOUS_VECTOR => apic_vectors::handle(frame), + _ => { + crate::println!("Unhandled interrupt vector: {:#X}", vector); + } + } +} + +macro_rules! stub_no_err { + ($name:ident, $vec:literal) => { + #[unsafe(naked)] + pub(super) unsafe extern "C" fn $name() { + core::arch::naked_asm!( + "push 0", + concat!("push ", stringify!($vec)), + "jmp {common}", + common = sym $crate::arch::x86_64::interrupts::idt::interrupt_common, + ); + } + }; +} + +macro_rules! stub_err { + ($name:ident, $vec:literal) => { + #[unsafe(naked)] + pub(super) unsafe extern "C" fn $name() { + core::arch::naked_asm!( + concat!("push ", stringify!($vec)), + "jmp {common}", + common = sym $crate::arch::x86_64::interrupts::idt::interrupt_common, + ); + } + }; +} + +pub(super) use stub_err; +pub(super) use stub_no_err; pub fn idt_init() { let mut idt = Idt::new(); diff --git a/src/arch/x86_64/paging.rs b/src/arch/x86_64/paging.rs index c7ea8fd..2cb7776 100644 --- a/src/arch/x86_64/paging.rs +++ b/src/arch/x86_64/paging.rs @@ -4,7 +4,7 @@ use crate::{ arch::x86_64::cpu::CpuFeatures, memory::{ CachePolicy, DirectMap, FrameAddr, FrameAllocator, OwnedFrame, PagePermissions, - PhysicalAddr, VirtualAddr, + PageTableMapping, PhysicalAddr, VirtualAddr, }, }; @@ -199,6 +199,14 @@ impl PageTableEntry { self.0 & Self::PRESENT != 0 } + fn writable(&self) -> bool { + self.0 & Self::WRITABLE != 0 + } + + fn executable(&self) -> bool { + self.0 & Self::NX == 0 + } + fn is_user_accessible(&self) -> bool { self.0 & Self::USER_ACCESSIBLE != 0 } @@ -226,6 +234,14 @@ impl PageTableEntry { FrameAddr::from_start_address(self.physical_address(config)) } + + fn permissions(&self) -> PagePermissions { + PagePermissions::new( + self.writable(), + self.executable(), + self.is_user_accessible(), + ) + } } pub(crate) enum MapError { @@ -363,6 +379,52 @@ impl PageTable { self.direct_map.translate(addr) } + pub fn mapping(&self, virtual_addr: VirtualAddr) -> Option { + let address = virtual_addr.as_usize(); + + if !self.is_canonical(address) { + return None; + } + + let mut table_frame = self.frame.frame_address(); + let mut permissions = PagePermissions::new(true, true, true); + + for &level in self.config.mode.intermediate_levels() { + let table = self.table(table_frame)?; + let entry = table[level.index(address)]; + + if !entry.is_present() { + return None; + } + + let entry_permissions = entry.permissions(); + permissions.writable &= entry_permissions.writable; + permissions.user_accessible &= entry_permissions.user_accessible; + permissions.executable &= entry_permissions.executable; + + if entry.is_huge() { + level.large_page_size()?; + return Some(PageTableMapping { permissions }); + } + + table_frame = entry.table_frame(self.config)?; + } + + let page_table = self.table(table_frame)?; + let entry = page_table[p1_index(address)]; + + if !entry.is_present() { + return None; + } + + let entry_permissions = entry.permissions(); + permissions.writable &= entry_permissions.writable; + permissions.user_accessible &= entry_permissions.user_accessible; + permissions.executable &= entry_permissions.executable; + + Some(PageTableMapping { permissions }) + } + fn get_next_level( &self, parent: FrameAddr, diff --git a/src/format/cpio.rs b/src/format/cpio.rs index 0549a5c..7e4acdd 100644 --- a/src/format/cpio.rs +++ b/src/format/cpio.rs @@ -38,6 +38,10 @@ pub fn find_file<'a>(archive: &'a [u8], target: &str) -> Option<&'a [u8]> { let mut offset = 0; while offset + core::mem::size_of::
() <= archive.len() { + if offset + core::mem::size_of::
() > archive.len() { + return None; + } + let header = Header::from_bytes(&archive[offset..])?; let header_start = offset; offset += core::mem::size_of::
(); diff --git a/src/main.rs b/src/main.rs index 26036a0..86c1e01 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -#![feature(abi_x86_interrupt)] #![allow(clippy::needless_return)] #![no_std] #![no_main] diff --git a/src/memory/address_space.rs b/src/memory/address_space.rs index 9b4f795..a7e6cad 100644 --- a/src/memory/address_space.rs +++ b/src/memory/address_space.rs @@ -8,6 +8,10 @@ use crate::{ }, }; +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(transparent)] +pub struct AddressSpaceId(usize); + const MAX_ADDRESS_SPACES: usize = 32; struct AddressSpaceTable { entries: [Option; MAX_ADDRESS_SPACES], @@ -20,27 +24,27 @@ impl AddressSpaceTable { } } - fn insert(&mut self, address_space: AddressSpace) -> Option { + fn insert(&mut self, address_space: AddressSpace) -> Result { for (i, slot) in self.entries.iter_mut().enumerate() { if slot.is_none() { *slot = Some(address_space); - return Some(i); + return Ok(AddressSpaceId(i)); } } - None + Err(address_space) } - fn get(&self, id: usize) -> Option<&AddressSpace> { - self.entries.get(id).and_then(Option::as_ref) + fn get(&self, id: AddressSpaceId) -> Option<&AddressSpace> { + self.entries.get(id.0).and_then(Option::as_ref) } - fn get_mut(&mut self, id: usize) -> Option<&mut AddressSpace> { - self.entries.get_mut(id).and_then(Option::as_mut) + fn get_mut(&mut self, id: AddressSpaceId) -> Option<&mut AddressSpace> { + self.entries.get_mut(id.0).and_then(Option::as_mut) } - fn remove(&mut self, id: usize) -> Option { - self.entries.get_mut(id).and_then(Option::take) + fn remove(&mut self, id: AddressSpaceId) -> Option { + self.entries.get_mut(id.0).and_then(Option::take) } } @@ -51,20 +55,19 @@ unsafe impl Sync for GlobalAddressSpaceTable {} static ADDRESS_SPACE_TABLE: GlobalAddressSpaceTable = GlobalAddressSpaceTable(UnsafeCell::new(AddressSpaceTable::new())); -pub fn insert_address_space(address_space: AddressSpace) -> Option { +pub fn insert_address_space(address_space: AddressSpace) -> Result { let table = unsafe { &mut *ADDRESS_SPACE_TABLE.0.get() }; - let id = table.insert(address_space); - id + table.insert(address_space) } -pub fn remove_address_space(id: usize) -> Option { +pub fn remove_address_space(id: AddressSpaceId) -> Option { let table = unsafe { &mut *ADDRESS_SPACE_TABLE.0.get() }; table.remove(id) } -pub fn with_address_space(id: usize, f: impl FnOnce(&AddressSpace) -> R) -> Option { +pub fn with_address_space(id: AddressSpaceId, f: impl FnOnce(&AddressSpace) -> R) -> Option { let interrupt_state = crate::arch::disable_interrupts_and_save(); let table = unsafe { &*ADDRESS_SPACE_TABLE.0.get() }; let res = table.get(id).map(f); @@ -72,7 +75,10 @@ pub fn with_address_space(id: usize, f: impl FnOnce(&AddressSpace) -> R) -> O res } -pub fn with_address_space_mut(id: usize, f: impl FnOnce(&mut AddressSpace) -> R) -> Option { +pub fn with_address_space_mut( + id: AddressSpaceId, + f: impl FnOnce(&mut AddressSpace) -> R, +) -> Option { let interrupt_state = crate::arch::disable_interrupts_and_save(); let table = unsafe { &mut *ADDRESS_SPACE_TABLE.0.get() }; let res = table.get_mut(id).map(f); @@ -183,6 +189,10 @@ impl From for AddressSpaceCreateError { } } +pub struct PageTableMapping { + pub permissions: PagePermissions, +} + #[derive(PartialEq, Eq)] enum AddressSpaceKind { Kernel, @@ -427,6 +437,10 @@ impl AddressSpace { self.root.to_virtual(physical_addr) } + pub fn mapping(&self, virtual_addr: VirtualAddr) -> Option { + self.root.mapping(virtual_addr) + } + pub unsafe fn activate(&self) { unsafe { self.root.activate() } } diff --git a/src/memory/mod.rs b/src/memory/mod.rs index e8dc970..7d734dd 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -7,9 +7,9 @@ use core::ops::Add; #[allow(unused)] pub use address_space::{ - AddressSpace, AddressSpaceCreateError, MapError, UnmapError, init_kernel_address_space, - insert_address_space, remove_address_space, with_address_space, with_address_space_mut, - with_kernel_address_space, + AddressSpace, AddressSpaceCreateError, AddressSpaceId, MapError, PageTableMapping, UnmapError, + init_kernel_address_space, insert_address_space, remove_address_space, with_address_space, + with_address_space_mut, with_kernel_address_space, }; pub use frame::{ FRAME_SIZE, FrameAddr, FrameAllocator, OwnedFrame, alloc_frame, dealloc_frame, diff --git a/src/memory/stack.rs b/src/memory/stack.rs index 59f65d9..5634ac7 100644 --- a/src/memory/stack.rs +++ b/src/memory/stack.rs @@ -237,8 +237,13 @@ impl KernelStackPool { Err(StackCreateError::OutOfStacks) } - pub fn free(&mut self, stack: &KernelStack) { + pub fn free(&mut self, stack: KernelStack) { let slot = (stack.top().as_usize() - KERNEL_STACK_BASE) / KERNEL_SLOT_SIZE - 1; + crate::memory::with_kernel_address_space(|kernel_as| { + crate::memory::with_allocator(|allocator| unsafe { + stack.destroy(kernel_as, allocator) + }); + }); self.free_slots &= !(1 << slot); } } diff --git a/src/memory/user.rs b/src/memory/user.rs index 2d97add..be65270 100644 --- a/src/memory/user.rs +++ b/src/memory/user.rs @@ -1,9 +1,51 @@ -use crate::{memory::VirtualAddr, syscall::Status}; +use crate::{ + memory::{FRAME_SIZE, VirtualAddr, address_space::AddressSpaceId, with_address_space}, + syscall::Status, +}; pub const USER_SPACE_END: VirtualAddr = VirtualAddr::new(0x0000_8000_0000_0000); -pub fn copy_from_user(src: VirtualAddr, dst: &mut [u8]) -> Result<(), Status> { - // TODO: guard against unmapped pages +pub fn validate_user_range( + as_id: AddressSpaceId, + start: VirtualAddr, + len: usize, + writable: bool, +) -> Result<(), Status> { + let start_addr = start.as_usize(); + let end_addr = start_addr.checked_add(len).ok_or(Status::BadAddress)?; + + if start_addr >= USER_SPACE_END.as_usize() || end_addr > USER_SPACE_END.as_usize() { + return Err(Status::BadAddress); + } + + if len == 0 { + return Ok(()); + } + + let page_start = start_addr & !(FRAME_SIZE - 1); + for page in (page_start..end_addr).step_by(FRAME_SIZE) { + let is_valid = with_address_space(as_id, |address_space| { + address_space + .mapping(VirtualAddr::new(page)) + .is_some_and(|mapping| { + mapping.permissions.user_accessible + && (!writable || mapping.permissions.writable) + }) + }) + .ok_or(Status::BadAddress)?; + + if !is_valid { + return Err(Status::BadAddress); + } + } + + Ok(()) +} + +/// # Safety +/// +/// The caller must ensure that the user address range is valid, mapped, and user-accessible (e.g. via [`validate_user_range`]). +pub unsafe fn copy_from_user(src: VirtualAddr, dst: &mut [u8]) -> Result<(), Status> { let end = src .as_usize() .checked_add(dst.len()) @@ -19,7 +61,10 @@ pub fn copy_from_user(src: VirtualAddr, dst: &mut [u8]) -> Result<(), Status> { Ok(()) } -pub fn copy_to_user(dst: VirtualAddr, src: &[u8]) -> Result<(), Status> { +/// # Safety +/// +/// The caller must ensure that the user address range is valid, mapped, user-accessible, and writable (e.g. via [`validate_user_range`]). +pub unsafe fn copy_to_user(dst: VirtualAddr, src: &[u8]) -> Result<(), Status> { let end = dst .as_usize() .checked_add(src.len()) @@ -35,7 +80,10 @@ pub fn copy_to_user(dst: VirtualAddr, src: &[u8]) -> Result<(), Status> { Ok(()) } -pub fn copy_val_to_user(dst: VirtualAddr, val: &T) -> Result<(), Status> { +/// # Safety +/// +/// The caller must ensure that the user address is valid, mapped, user-accessible, and writable (e.g. via [`validate_user_range`]). +pub unsafe fn copy_val_to_user(dst: VirtualAddr, val: &T) -> Result<(), Status> { if dst.as_usize() % core::mem::align_of::() != 0 { return Err(Status::InvalidArgument); } @@ -54,7 +102,11 @@ pub fn copy_val_to_user(dst: VirtualAddr, val: &T) -> Result<(), Status Ok(()) } -pub fn copy_val_from_user(src: VirtualAddr) -> Result { +/// # Safety +/// +/// The caller must ensure that the user address is valid, mapped, and user-accessible (e.g. via [`validate_user_range`]). +#[allow(unused)] +pub unsafe fn copy_val_from_user(src: VirtualAddr) -> Result { if src.as_usize() % core::mem::align_of::() != 0 { return Err(Status::InvalidArgument); } diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs index c31286a..f1c68f4 100644 --- a/src/syscall/mod.rs +++ b/src/syscall/mod.rs @@ -2,7 +2,7 @@ mod table; use table::*; -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u64)] pub enum Status { // Status::Success = 0 @@ -50,7 +50,7 @@ impl TryFrom for SyscallNumber { } } -pub fn handle(num: u64, arg0: u64, arg1: u64, arg2: u64, arg3: u64, _arg4: u64, _arg5: u64) -> u64 { +pub fn handle(num: u64, arg0: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64, _arg5: u64) -> u64 { let result = (|| -> Result<(), Status> { let syscall = SyscallNumber::try_from(num)?; match syscall { @@ -66,10 +66,14 @@ pub fn handle(num: u64, arg0: u64, arg1: u64, arg2: u64, arg3: u64, _arg4: u64, SyscallNumber::FrameAlloc => sys_frame_alloc(arg0 as usize), SyscallNumber::FrameDealloc => sys_frame_dealloc(arg0 as usize), SyscallNumber::AsCreate => sys_as_create(arg0 as usize), - SyscallNumber::Map => { - sys_map(arg0 as usize, arg1 as usize, arg2 as usize, arg3 as usize) - } - SyscallNumber::Unmap => sys_unmap(arg0 as usize, arg1 as usize), + SyscallNumber::Map => sys_map( + arg0 as usize, + arg1 as usize, + arg2 as usize, + arg3 as usize, + arg4 as usize, + ), + SyscallNumber::Unmap => sys_unmap(arg0 as usize), SyscallNumber::TaskCreate => { sys_task_create(arg0 as usize, arg1 as usize, arg2 as usize, arg3 as usize) } diff --git a/src/syscall/table.rs b/src/syscall/table.rs index b310180..0d268b1 100644 --- a/src/syscall/table.rs +++ b/src/syscall/table.rs @@ -1,10 +1,13 @@ use crate::{ memory::{ - FRAME_SIZE, OwnedFrame, PagePermissions, USER_SPACE_END, VirtualAddr, copy_from_user, - copy_to_user, copy_val_to_user, + FRAME_SIZE, MapError, PagePermissions, USER_SPACE_END, VirtualAddr, copy_from_user, + copy_to_user, copy_val_to_user, validate_user_range, }, println, - task::tcb::{BlockReason, Handle, KernelObject, MAX_MSG_SIZE, Message, Rights}, + task::{ + scheduler::TaskId, + tcb::{BlockReason, Handle, KernelObject, MAX_MSG_SIZE, Message, Rights}, + }, }; use super::Status; @@ -23,32 +26,60 @@ pub fn sys_write(fd: usize, buf_ptr: usize, len: usize, out_ptr: usize) -> Resul return Err(Status::BadFileDescriptor); } + crate::task::scheduler::with_task(crate::task::scheduler::current(), |current_task| { + validate_user_range(current_task.as_id, VirtualAddr::new(buf_ptr), len, false)?; + + if out_ptr != 0 { + if out_ptr % core::mem::align_of::() != 0 { + return Err(Status::InvalidArgument); + } + validate_user_range( + current_task.as_id, + VirtualAddr::new(out_ptr), + core::mem::size_of::(), + true, + )?; + } + + Ok(()) + }) + .expect("failed to resolve self task")?; + let mut chunk = [0u8; 128]; let mut written = 0; while written < len { let n = (len - written).min(chunk.len()); - copy_from_user(VirtualAddr::new(buf_ptr + written), &mut chunk[..n])?; + unsafe { + copy_from_user(VirtualAddr::new(buf_ptr + written), &mut chunk[..n])?; + } crate::debug::serial::write_bytes(&chunk[..n]); written += n; } if out_ptr != 0 { - copy_val_to_user(VirtualAddr::new(out_ptr), &written)?; + unsafe { + copy_val_to_user(VirtualAddr::new(out_ptr), &written)?; + } } Ok(()) } pub fn sys_send(dest_task_id: usize, msg_ptr: usize, len: usize) -> Result<(), Status> { + let dest_task_id = TaskId::new(dest_task_id); + if len > MAX_MSG_SIZE { return Err(Status::InvalidArgument); } - let mut msg_buf = [0u8; MAX_MSG_SIZE]; - copy_from_user(VirtualAddr::new(msg_ptr), &mut msg_buf[..len])?; - - let dest_task = crate::task::scheduler::get_task_mut(dest_task_id).ok_or(Status::NoSuchTask)?; let sender = crate::task::scheduler::current(); + crate::task::scheduler::with_task(sender, |current_task| { + validate_user_range(current_task.as_id, VirtualAddr::new(msg_ptr), len, false) + }) + .expect("failed to resolve self task")?; + + let mut msg_buf = [0u8; MAX_MSG_SIZE]; + unsafe { copy_from_user(VirtualAddr::new(msg_ptr), &mut msg_buf[..len])? }; let msg = Message { sender, @@ -56,14 +87,18 @@ pub fn sys_send(dest_task_id: usize, msg_ptr: usize, len: usize) -> Result<(), S data: msg_buf, }; - if !dest_task.mailbox.push(msg) { - return Err(Status::OutOfMemory); - } + let should_unblock = crate::task::scheduler::with_task_mut(dest_task_id, |dest_task| { + if !dest_task.mailbox.push(msg) { + return Err(Status::OutOfMemory); + } + Ok(matches!( + dest_task.state, + crate::task::tcb::ThreadState::Blocked(BlockReason::Recv) + )) + }) + .ok_or(Status::NoSuchTask)??; - if matches!( - dest_task.state, - crate::task::tcb::ThreadState::Blocked(BlockReason::Recv { .. }) - ) { + if should_unblock { crate::task::scheduler::unblock(dest_task_id); } @@ -80,28 +115,58 @@ pub fn sys_recv( return Err(Status::InvalidArgument); } - let current_task = crate::task::scheduler::get_task_mut(crate::task::scheduler::current()) - .ok_or(Status::NoSuchTask)?; + crate::task::scheduler::with_task(crate::task::scheduler::current(), |current_task| { + if current_task.mailbox.len == 0 { + crate::task::scheduler::block_current(BlockReason::Recv); + } - if current_task.mailbox.len == 0 { - crate::task::scheduler::block_current(BlockReason::Recv { ep: 0 }); - } + validate_user_range(current_task.as_id, VirtualAddr::new(out_ptr), max_len, true)?; - // if we blocked, we will wake up when the mailbox is non-empty + if out_actual_len != 0 { + if out_actual_len % core::mem::align_of::() != 0 { + return Err(Status::InvalidArgument); + } + validate_user_range( + current_task.as_id, + VirtualAddr::new(out_actual_len), + core::mem::size_of::(), + true, + )?; + } - let current_task = crate::task::scheduler::get_task_mut(crate::task::scheduler::current()) - .ok_or(Status::NoSuchTask)?; - let msg = current_task.mailbox.pop().ok_or(Status::NoSuchTask)?; + if out_sender != 0 { + if out_sender % core::mem::align_of::() != 0 { + return Err(Status::InvalidArgument); + } + validate_user_range( + current_task.as_id, + VirtualAddr::new(out_sender), + core::mem::size_of::(), + true, + )?; + } - copy_to_user( - VirtualAddr::new(out_ptr), - &msg.data[..msg.length.min(max_len)], - )?; - if out_actual_len != 0 { - copy_val_to_user(VirtualAddr::new(out_actual_len), &msg.length)?; - } - if out_sender != 0 { - copy_val_to_user(VirtualAddr::new(out_sender), &msg.sender)?; + Ok(()) + }) + .expect("failed to resolve self task")?; + + let msg = + crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |current_task| { + current_task.mailbox.pop().ok_or(Status::NoSuchTask) + }) + .expect("failed to resolve self task")?; + + unsafe { + copy_to_user( + VirtualAddr::new(out_ptr), + &msg.data[..msg.length.min(max_len)], + )?; + if out_actual_len != 0 { + copy_val_to_user(VirtualAddr::new(out_actual_len), &msg.length)?; + } + if out_sender != 0 { + copy_val_to_user(VirtualAddr::new(out_sender), &msg.sender)?; + } } Ok(()) @@ -112,49 +177,69 @@ pub fn sys_frame_alloc(out_handle: usize) -> Result<(), Status> { return Err(Status::InvalidArgument); } + if out_handle % core::mem::align_of::() != 0 { + return Err(Status::InvalidArgument); + } + + crate::task::scheduler::with_task(crate::task::scheduler::current(), |current_task| { + validate_user_range( + current_task.as_id, + VirtualAddr::new(out_handle), + core::mem::size_of::(), + true, + ) + }) + .expect("failed to resolve self task")?; + let frame = crate::memory::alloc_frame().ok_or(Status::OutOfMemory)?; - let task_id = crate::task::scheduler::current(); - let task = match crate::task::scheduler::get_task_mut(task_id) { - Some(task) => task, - None => { - unsafe { crate::memory::dealloc_frame(frame) }; - return Err(Status::NoSuchTask); - } - }; - - let frame_addr = frame.into_raw(); let handle = Handle { - object: KernelObject::Frame(frame_addr), - rights: Rights::READ | Rights::WRITE | Rights::EXECUTE, + object: KernelObject::Frame(frame), + rights: Rights::READ | Rights::WRITE | Rights::EXECUTE | Rights::MAP, }; - let handle_id = match task.handles.push(handle) { - Some(id) => id, - None => { - unsafe { crate::memory::dealloc_frame(OwnedFrame::from_raw(frame_addr)) }; - return Err(Status::OutOfMemory); - } - }; + crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |current_task| { + let handle_id = match current_task.handles.push(handle) { + Ok(id) => id, + Err(handle) => { + let frame = match handle.object { + KernelObject::Frame(frame) => frame, + _ => unreachable!("pushed handle was not a frame"), + }; + unsafe { crate::memory::dealloc_frame(frame) }; + return Err(Status::OutOfMemory); + } + }; - copy_val_to_user(VirtualAddr::new(out_handle), &handle_id)?; - - Ok(()) + unsafe { copy_val_to_user(VirtualAddr::new(out_handle), &handle_id) } + }) + .expect("failed to resolve self task") } -pub fn sys_frame_dealloc(frame_handle: usize) -> Result<(), Status> { - let task = crate::task::scheduler::current(); - let task = crate::task::scheduler::get_task_mut(task).ok_or(Status::NoSuchTask)?; +pub fn sys_frame_dealloc(frame_handle_id: usize) -> Result<(), Status> { + crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |task| { + let frame_handle = task + .handles + .take(frame_handle_id) + .ok_or(Status::BadHandle)?; + match frame_handle.object { + KernelObject::Frame(frame_addr) => { + unsafe { crate::memory::dealloc_frame(frame_addr) }; - let frame_handle = task.handles.get(frame_handle).ok_or(Status::BadHandle)?; - let frame = match frame_handle.object { - KernelObject::Frame(frame_addr) => frame_addr, - _ => return Err(Status::InvalidArgument), - }; + Ok(()) + } + _ => { + // Wrong-type operations must not consume the handle + match task.handles.put(frame_handle_id, frame_handle) { + Ok(_) => {} + Err(_) => panic!("taken handle was unexpectedly occupied"), + } - unsafe { crate::memory::dealloc_frame(OwnedFrame::from_raw(frame)) }; - - Ok(()) + return Err(Status::InvalidArgument); + } + } + }) + .expect("failed to resolve self task") } pub fn sys_as_create(out_handle: usize) -> Result<(), Status> { @@ -162,34 +247,62 @@ pub fn sys_as_create(out_handle: usize) -> Result<(), Status> { return Err(Status::InvalidArgument); } - let task_id = crate::task::scheduler::current(); - let task = crate::task::scheduler::get_task_mut(task_id).ok_or(Status::NoSuchTask)?; + if out_handle % core::mem::align_of::() != 0 { + return Err(Status::InvalidArgument); + } - let new_as = match crate::memory::with_address_space(task.as_id, |caller_as| { - crate::memory::with_allocator(|allocator| caller_as.new_user(allocator)) - }) { - Some(Ok(as_space)) => as_space, - _ => return Err(Status::OutOfMemory), + let new_as = + crate::task::scheduler::with_task(crate::task::scheduler::current(), |current_task| { + validate_user_range( + current_task.as_id, + VirtualAddr::new(out_handle), + core::mem::size_of::(), + true, + )?; + + let new_as = match crate::memory::with_address_space(current_task.as_id, |caller_as| { + crate::memory::with_allocator(|allocator| caller_as.new_user(allocator)) + }) { + Some(Ok(as_space)) => as_space, + _ => return Err(Status::OutOfMemory), + }; + + Ok(new_as) + }) + .expect("failed to resolve self task")?; + + let as_id = match crate::memory::insert_address_space(new_as) { + Ok(id) => id, + Err(addr_space) => { + crate::memory::with_allocator(|allocator| unsafe { addr_space.destroy(allocator) }); + return Err(Status::OutOfMemory); + } }; - let as_id = crate::memory::insert_address_space(new_as).ok_or(Status::OutOfMemory)?; - let handle = Handle { object: KernelObject::AddressSpace(as_id), rights: Rights::READ | Rights::WRITE | Rights::EXECUTE, }; - let handle_id = match task.handles.push(handle) { - Some(id) => id, - None => { - crate::memory::remove_address_space(as_id); - return Err(Status::OutOfMemory); - } - }; + crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |current_task| { + let handle_id = match current_task.handles.push(handle) { + Ok(id) => id, + Err(handle) => { + let address_space = match handle.object { + KernelObject::AddressSpace(as_id) => crate::memory::remove_address_space(as_id) + .expect("address space was just inserted"), + _ => unreachable!("pushed handle was not an address space"), + }; + crate::memory::with_allocator(|allocator| unsafe { + address_space.destroy(allocator) + }); + return Err(Status::OutOfMemory); + } + }; - copy_val_to_user(VirtualAddr::new(out_handle), &handle_id)?; - - Ok(()) + unsafe { copy_val_to_user(VirtualAddr::new(out_handle), &handle_id) } + }) + .expect("failed to resolve self task") } pub fn sys_map( @@ -197,41 +310,81 @@ pub fn sys_map( frame_handle: usize, virtual_addr: usize, permissions: usize, + out_handle: usize, ) -> Result<(), Status> { - if virtual_addr % FRAME_SIZE != 0 { + if out_handle == 0 || out_handle % core::mem::align_of::() != 0 { return Err(Status::InvalidArgument); } - if virtual_addr >= USER_SPACE_END.as_usize() { + if virtual_addr % FRAME_SIZE != 0 || permissions & !0b11 != 0 { return Err(Status::InvalidArgument); } - let task = crate::task::scheduler::current(); - let task = crate::task::scheduler::get_task_mut(task).ok_or(Status::NoSuchTask)?; + let writable = permissions & (1 << 0) != 0; + let executable = permissions & (1 << 1) != 0; - let as_handle = task.handles.get(as_handle).ok_or(Status::BadHandle)?; - let as_id = match as_handle.object { - KernelObject::AddressSpace(as_id) => as_id, - _ => return Err(Status::InvalidArgument), - }; - - let frame_handle = task.handles.get(frame_handle).ok_or(Status::BadHandle)?; - let frame = match frame_handle.object { - KernelObject::Frame(frame_addr) => frame_addr, - _ => return Err(Status::InvalidArgument), + let end = virtual_addr + .checked_add(FRAME_SIZE) + .ok_or(Status::InvalidArgument)?; + if end > USER_SPACE_END.as_usize() { + return Err(Status::InvalidArgument); + } + + let current_task = crate::task::scheduler::current(); + let as_id = crate::task::scheduler::with_task(current_task, |task| { + validate_user_range( + task.as_id, + VirtualAddr::new(out_handle), + core::mem::size_of::(), + true, + )?; + + let as_handle = task.handles.get(as_handle).ok_or(Status::BadHandle)?; + let as_id = match as_handle.object { + KernelObject::AddressSpace(as_id) => as_id, + _ => return Err(Status::InvalidArgument), + }; + if as_handle.rights.0 & Rights::WRITE.0 == 0 { + return Err(Status::InvalidArgument); + } + + let frame_handle = task.handles.get(frame_handle).ok_or(Status::BadHandle)?; + if !matches!(frame_handle.object, KernelObject::Frame(_)) { + return Err(Status::InvalidArgument); + } + + let mut required_rights = Rights::READ | Rights::MAP; + if writable { + required_rights = required_rights | Rights::WRITE; + } + if executable { + required_rights = required_rights | Rights::EXECUTE; + } + if frame_handle.rights.0 & required_rights.0 != required_rights.0 { + return Err(Status::InvalidArgument); + } + + Ok(as_id) + }) + .expect("failed to resolve self task")?; + + let handle = crate::task::scheduler::with_task_mut(current_task, |task| { + task.handles.take(frame_handle).ok_or(Status::BadHandle) + }) + .expect("failed to resolve self task")?; + + let Handle { object, rights } = handle; + let KernelObject::Frame(frame) = object else { + panic!("validated frame handle changed before it was taken"); }; + let permissions = PagePermissions::new(writable, executable, true); let virtual_addr = VirtualAddr::new(virtual_addr); - let permissions = PagePermissions::new( - permissions & (1 << 0) != 0, - permissions & (1 << 1) != 0, - true, - ); let map_result = crate::memory::with_address_space_mut(as_id, |target_as| { crate::memory::with_allocator(|allocator| { target_as.map( - frame.start_address(), + frame.frame_address().start_address(), virtual_addr, permissions, allocator, @@ -239,48 +392,151 @@ pub fn sys_map( ) }) }) - .ok_or(Status::BadHandle)?; + .expect("failed to resolve self address space"); - map_result.map_err(|_| Status::OutOfMemory)?; + match map_result { + Ok(_) => { + crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |task| { + match task.handles.put( + frame_handle, + Handle { + object: KernelObject::Mapping { + frame, + address_space: as_id, + virtual_addr, + }, + rights, + }, + ) { + Ok(_) => {} + Err(_) => panic!("taken handle was unexpectedly occupied"), + } + }); - Ok(()) + unsafe { + copy_val_to_user(VirtualAddr::new(out_handle), &frame_handle) + .expect("out_handle has already been checked") + } + + Ok(()) + } + Err(err) => { + crate::task::scheduler::with_task_mut(current_task, |task| { + match task.handles.put( + frame_handle, + Handle { + object: KernelObject::Frame(frame), + rights, + }, + ) { + Ok(_) => {} + Err(_) => panic!("taken handle was unexpectedly occupied"), + } + }) + .expect("failed to resolve self task"); + + match err { + MapError::AlreadyMapped | MapError::UnsupportedPermissions => { + Err(Status::InvalidArgument) + } + MapError::OutOfMemory => Err(Status::OutOfMemory), + MapError::InvalidVirtualAddress + | MapError::VirtualAddressUnaligned + | MapError::PhysicalAddressTooLarge + | MapError::PhysicalAddressUnaligned + | MapError::RangeLengthUnaligned + | MapError::AddressOverflow + | MapError::MappingConflict + | MapError::PageTableUnavailable + | MapError::CorruptedPageTable + | MapError::InvalidUserAddress + | MapError::InvalidUserMap => { + panic!("validated user mapping failed with an impossible error: {err:?}") + } + } + } + } } -pub fn sys_unmap(as_handle: usize, virtual_addr: usize) -> Result<(), Status> { - if virtual_addr % FRAME_SIZE != 0 { +pub fn sys_unmap(mapping_handle: usize) -> Result<(), Status> { + let handle = crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |task| { + task.handles.take(mapping_handle).ok_or(Status::BadHandle) + }) + .expect("failed to resolve self task")?; + + let Handle { object, rights } = handle; + + let KernelObject::Mapping { + frame, + address_space, + virtual_addr, + } = object + else { + // Wrong-type operations must not consume the handle + crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |task| match task + .handles + .put(mapping_handle, Handle { object, rights }) + { + Ok(_) => {} + Err(_) => panic!("taken handle was unexpectedly occupied"), + }) + .expect("failed to resolve self task"); + return Err(Status::InvalidArgument); - } - - if virtual_addr >= USER_SPACE_END.as_usize() { - return Err(Status::InvalidArgument); - } - - let task = crate::task::scheduler::current(); - let task = crate::task::scheduler::get_task_mut(task).ok_or(Status::NoSuchTask)?; - - let as_handle = task.handles.get(as_handle).ok_or(Status::BadHandle)?; - let as_id = match as_handle.object { - KernelObject::AddressSpace(as_id) => as_id, - _ => return Err(Status::InvalidArgument), }; - let virtual_addr = VirtualAddr::new(virtual_addr); - - let map_result = crate::memory::with_address_space_mut(as_id, |target_as| { - if target_as.to_physical(virtual_addr).is_none() { - return Err(Status::InvalidArgument); - } - + let unmap_result = crate::memory::with_address_space_mut(address_space, |target_as| { crate::memory::with_allocator(|allocator| unsafe { target_as.unmap(virtual_addr, allocator) }) - .map_err(|_| Status::BadAddress) - }) - .ok_or(Status::BadHandle)?; + }); - map_result.map_err(|_| Status::OutOfMemory)?; + match unmap_result { + Some(Ok(unmapped_frame)) => { + if unmapped_frame == frame.frame_address() { + crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |task| { + match task.handles.put( + mapping_handle, + Handle { + object: KernelObject::Frame(frame), + rights, + }, + ) { + Ok(_) => {} + Err(_) => panic!("taken handle was unexpectedly occupied"), + } + }); - Ok(()) + Ok(()) + } else { + panic!("unmap resulted in a frame that was not the one we expected") + } + } + Some(Err(err)) => { + // every unmapping error should be impossible to occur + panic!("failed to unmap: {err:?}"); + } + None => { + crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |task| { + match task.handles.put( + mapping_handle, + Handle { + object: KernelObject::Mapping { + frame: frame, + address_space, + virtual_addr, + }, + rights, + }, + ) { + Ok(_) => {} + Err(_) => panic!("taken handle was unexpectedly occupied"), + } + }); + + Err(Status::BadHandle) + } + } } pub fn sys_task_create( @@ -289,31 +545,64 @@ pub fn sys_task_create( user_stack: usize, out_task_handle: usize, ) -> Result<(), Status> { - if out_task_handle == 0 || entry == 0 || user_stack == 0 { + if entry == 0 || user_stack == 0 || out_task_handle == 0 { return Err(Status::InvalidArgument); } - if entry >= 0x0000_8000_0000_0000 || user_stack >= 0x0000_8000_0000_0000 { + if entry >= USER_SPACE_END.as_usize() || user_stack > USER_SPACE_END.as_usize() { return Err(Status::BadAddress); } - let task_id = crate::task::scheduler::current(); - let task = crate::task::scheduler::get_task_mut(task_id).ok_or(Status::NoSuchTask)?; - - let as_handle = task.handles.get(as_handle).ok_or(Status::BadHandle)?; - let as_id = match as_handle.object { - KernelObject::AddressSpace(as_id) => as_id, - _ => return Err(Status::InvalidArgument), - }; - - if as_handle.rights.0 & Rights::EXECUTE.0 == 0 { + if out_task_handle % core::mem::align_of::() != 0 { return Err(Status::InvalidArgument); } + let as_id = + crate::task::scheduler::with_task(crate::task::scheduler::current(), |current_task| { + validate_user_range( + current_task.as_id, + VirtualAddr::new(out_task_handle), + core::mem::size_of::(), + true, + )?; + + let as_handle = current_task + .handles + .get(as_handle) + .ok_or(Status::BadHandle)?; + let as_id = match as_handle.object { + KernelObject::AddressSpace(as_id) => as_id, + _ => return Err(Status::InvalidArgument), + }; + + if as_handle.rights.0 & Rights::EXECUTE.0 == 0 { + return Err(Status::InvalidArgument); + } + + Ok(as_id) + }) + .expect("failed to resolve self task")?; + if crate::memory::with_address_space(as_id, |_| ()).is_none() { return Err(Status::BadHandle); } + let stack_probe = user_stack.checked_sub(1).ok_or(Status::BadAddress)?; + validate_user_range(as_id, VirtualAddr::new(stack_probe), 1, true)?; + + let entry_is_valid = crate::memory::with_address_space(as_id, |address_space| { + address_space + .mapping(VirtualAddr::new(entry)) + .is_some_and(|mapping| { + mapping.permissions.user_accessible && mapping.permissions.executable + }) + }) + .ok_or(Status::BadHandle)?; + + if !entry_is_valid { + return Err(Status::BadAddress); + } + let kernel_stack = match crate::memory::with_kernel_address_space(|kernel_as| { crate::memory::with_allocator(|allocator| { crate::task::scheduler::allocate_kernel_stack(kernel_as, allocator) @@ -327,7 +616,6 @@ pub fn sys_task_create( }; let new_tcb = crate::task::tcb::Tcb::new_user( - 0, // assigned by scheduler::add_task as_id, kernel_stack, VirtualAddr::new(entry), @@ -344,15 +632,16 @@ pub fn sys_task_create( rights: Rights::READ | Rights::WRITE | Rights::EXECUTE, }; - let handle_id = match task.handles.push(handle) { - Some(id) => id, - None => { - crate::task::scheduler::remove_task(new_task_id); - return Err(Status::OutOfMemory); - } - }; + crate::task::scheduler::with_task_mut(crate::task::scheduler::current(), |current_task| { + let handle_id = match current_task.handles.push(handle) { + Ok(id) => id, + Err(_) => { + crate::task::scheduler::remove_task(new_task_id); + return Err(Status::OutOfMemory); + } + }; - copy_val_to_user(VirtualAddr::new(out_task_handle), &handle_id)?; - - Ok(()) + unsafe { copy_val_to_user(VirtualAddr::new(out_task_handle), &handle_id) } + }) + .expect("failed to resolve self task") } diff --git a/src/task/bootstrap.rs b/src/task/bootstrap.rs index 29262c7..510eb0e 100644 --- a/src/task/bootstrap.rs +++ b/src/task/bootstrap.rs @@ -4,7 +4,7 @@ use crate::{ self, AddressSpace, DirectMap, FRAME_SIZE, FrameAllocator, InitramfsImage, PagePermissions, UserStack, VirtualAddr, }, - task::tcb::Tcb, + task::{scheduler::TaskId, tcb::Tcb}, }; pub fn spawn( @@ -13,7 +13,7 @@ pub fn spawn( kernel_as: &mut AddressSpace, allocator: &mut FrameAllocator, direct_map: DirectMap, -) -> usize { +) -> TaskId { let bytes = format::cpio::find_file(initramfs.data(), name) .unwrap_or_else(|| panic!("{name} missing from initramfs")); let kernel_stack = crate::task::scheduler::allocate_kernel_stack(kernel_as, allocator) @@ -41,10 +41,14 @@ pub fn spawn( let entry = load_elf(bytes, &mut address_space, allocator, direct_map).expect("invalid ELF"); - let as_id = - crate::memory::insert_address_space(address_space).expect("address space table is full"); + let as_id = match crate::memory::insert_address_space(address_space) { + Ok(id) => id, + Err(_) => { + panic!("address space table is full"); + } + }; - let task = Tcb::new_user(0, as_id, kernel_stack, entry, user_stack.top()); + let task = Tcb::new_user(as_id, kernel_stack, entry, user_stack.top()); crate::task::scheduler::add_task(task).expect("scheduler is full") } diff --git a/src/task/scheduler.rs b/src/task/scheduler.rs index 29beb37..57b7af1 100644 --- a/src/task/scheduler.rs +++ b/src/task/scheduler.rs @@ -3,7 +3,8 @@ use core::cell::UnsafeCell; use crate::{ arch::ThreadContext, memory::{ - AddressSpace, FrameAllocator, KernelStack, KernelStackPool, StackCreateError, VirtualAddr, + AddressSpace, AddressSpaceId, FrameAllocator, KernelStack, KernelStackPool, + StackCreateError, VirtualAddr, }, println, task::tcb::{BlockReason, ExitReason, Handle, KernelObject, Rights, Tcb, ThreadState}, @@ -11,7 +12,15 @@ use crate::{ const MAX_TASKS: usize = 32; -type TaskId = usize; +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(transparent)] +pub struct TaskId(usize); + +impl TaskId { + pub const fn new(id: usize) -> Self { + Self(id) + } +} struct Scheduler { current: Option, @@ -33,11 +42,11 @@ impl Scheduler { fn make_switch(&mut self, current_id: TaskId, next_id: TaskId) -> Switch { assert_ne!(current_id, next_id); - let current = self.tasks[current_id].as_mut().unwrap(); + let current = self.tasks[current_id.0].as_mut().unwrap(); let prev_ctx = &mut current.context as *mut ThreadContext; let prev_as_id = current.as_id; - let next = self.tasks[next_id].as_ref().unwrap(); + let next = self.tasks[next_id.0].as_ref().unwrap(); let next_ctx = &next.context as *const ThreadContext; let next_as_id = next.as_id; let next_kernel_stack = next.kernel_stack.top(); @@ -55,7 +64,7 @@ impl Scheduler { struct Switch { previous_context: *mut ThreadContext, next_context: *const ThreadContext, - next_as_id: usize, + next_as_id: AddressSpaceId, next_kernel_stack: VirtualAddr, activate_address_space: bool, } @@ -85,7 +94,7 @@ struct ReadyQueue { impl ReadyQueue { pub const fn new() -> Self { Self { - entries: [0; MAX_TASKS], + entries: [TaskId(0); MAX_TASKS], head: 0, len: 0, } @@ -147,15 +156,20 @@ pub fn add_task(mut task: Tcb) -> Result { match scheduler.tasks.iter().position(Option::is_none) { Some(id) => { + let id = TaskId(id); + task.id = id; - task.handles.push(Handle { + match task.handles.push(Handle { object: KernelObject::Thread(id), rights: Rights::READ | Rights::WRITE | Rights::EXECUTE, - }); + }) { + Ok(_) => {} + Err(_) => unreachable!("Cant push root thread handle"), + } task.state = ThreadState::Ready; - scheduler.tasks[id] = Some(task); + scheduler.tasks[id.0] = Some(task); assert!(scheduler.ready.push_back(id)); Ok(id) } @@ -176,7 +190,7 @@ pub fn start() -> ! { let scheduler = unsafe { &mut *SCHEDULER.0.get() }; let next_id = scheduler.ready.pop_front().expect("no tasks to run"); - let next = scheduler.tasks[next_id] + let next = scheduler.tasks[next_id.0] .as_mut() .expect("ready task is missing"); @@ -210,20 +224,20 @@ pub fn allocate_kernel_stack( result } -pub fn remove_task(id: TaskId) -> Option { +pub fn remove_task(id: TaskId) -> bool { let interrupt_state = crate::arch::disable_interrupts_and_save(); let result = { let scheduler = unsafe { &mut *SCHEDULER.0.get() }; if scheduler.current == Some(id) { - None - } else if let Some(task) = scheduler.tasks.get_mut(id).and_then(Option::take) { + false + } else if let Some(task) = scheduler.tasks.get_mut(id.0).and_then(Option::take) { scheduler.ready.remove(id); - scheduler.stacks.free(&task.kernel_stack); - Some(task) + scheduler.stacks.free(task.kernel_stack); + true } else { - None + false } }; @@ -231,14 +245,24 @@ pub fn remove_task(id: TaskId) -> Option { result } -pub fn get_task(id: TaskId) -> Option<&'static Tcb> { - let scheduler = unsafe { &mut *SCHEDULER.0.get() }; - scheduler.tasks.get(id).and_then(Option::as_ref) +pub fn with_task(id: TaskId, f: impl FnOnce(&Tcb) -> R) -> Option { + let interrupt_state = crate::arch::disable_interrupts_and_save(); + let scheduler = unsafe { &*SCHEDULER.0.get() }; + let res = scheduler.tasks.get(id.0).and_then(Option::as_ref).map(f); + crate::arch::restore_interrupts(interrupt_state); + res } -pub fn get_task_mut(id: TaskId) -> Option<&'static mut Tcb> { +pub fn with_task_mut(id: TaskId, f: impl FnOnce(&mut Tcb) -> R) -> Option { + let interrupt_state = crate::arch::disable_interrupts_and_save(); let scheduler = unsafe { &mut *SCHEDULER.0.get() }; - scheduler.tasks.get_mut(id).and_then(Option::as_mut) + let res = scheduler + .tasks + .get_mut(id.0) + .and_then(Option::as_mut) + .map(f); + crate::arch::restore_interrupts(interrupt_state); + res } pub fn current() -> TaskId { @@ -259,10 +283,10 @@ pub fn block_current(reason: BlockReason) { let current_id = scheduler.current.expect("no current task"); - scheduler.tasks[current_id].as_mut().unwrap().state = ThreadState::Blocked(reason); + scheduler.tasks[current_id.0].as_mut().unwrap().state = ThreadState::Blocked(reason); // explicitly do NOT push back the current task, because it is not ready - scheduler.tasks[next_id].as_mut().unwrap().state = ThreadState::Running; + scheduler.tasks[next_id.0].as_mut().unwrap().state = ThreadState::Running; scheduler.current = Some(next_id); scheduler.make_switch(current_id, next_id) @@ -280,7 +304,7 @@ pub fn unblock(id: TaskId) { let interrupt_state = crate::arch::disable_interrupts_and_save(); let scheduler = unsafe { &mut *SCHEDULER.0.get() }; - if let Some(task) = scheduler.tasks[id].as_mut() { + if let Some(task) = scheduler.tasks[id.0].as_mut() { if matches!(task.state, ThreadState::Blocked(_)) { task.state = ThreadState::Ready; assert!(scheduler.ready.push_back(id)); @@ -303,10 +327,10 @@ pub fn yield_current() { let current_id = scheduler.current.expect("no current task"); - scheduler.tasks[current_id].as_mut().unwrap().state = ThreadState::Ready; + scheduler.tasks[current_id.0].as_mut().unwrap().state = ThreadState::Ready; assert!(scheduler.ready.push_back(current_id)); - scheduler.tasks[next_id].as_mut().unwrap().state = ThreadState::Running; + scheduler.tasks[next_id.0].as_mut().unwrap().state = ThreadState::Running; scheduler.current = Some(next_id); scheduler.make_switch(current_id, next_id) @@ -331,10 +355,10 @@ pub fn exit_current(exit_code: usize) -> ! { crate::hcf(); }; - let current = scheduler.tasks[current_id].as_mut().unwrap(); + let current = scheduler.tasks[current_id.0].as_mut().unwrap(); current.state = ThreadState::Dead(ExitReason::Exited(exit_code)); - scheduler.tasks[next_id].as_mut().unwrap().state = ThreadState::Running; + scheduler.tasks[next_id.0].as_mut().unwrap().state = ThreadState::Running; scheduler.current = Some(next_id); scheduler.make_switch(current_id, next_id) diff --git a/src/task/tcb.rs b/src/task/tcb.rs index 1430c20..92fcce6 100644 --- a/src/task/tcb.rs +++ b/src/task/tcb.rs @@ -2,22 +2,29 @@ use core::ops::BitOr; use crate::{ arch::ThreadContext, - memory::{FrameAddr, KernelStack, VirtualAddr}, + memory::{AddressSpaceId, KernelStack, OwnedFrame, VirtualAddr}, + task::scheduler::TaskId, }; +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Fault { + SegmentationFault, + IllegalInstruction, + Abort, + BadSystemCall, +} + // Thread Control Block #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ExitReason { Exited(usize), Killed, - Fault, + Fault(Fault), } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum BlockReason { - Send { ep: usize }, - Recv { ep: usize }, - Reply { client: usize }, + Recv, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -33,7 +40,7 @@ pub const MAILBOX_CAPACITY: usize = 4; #[derive(Clone, Copy)] pub struct Message { - pub sender: usize, + pub sender: TaskId, pub length: usize, pub data: [u8; MAX_MSG_SIZE], } @@ -79,9 +86,14 @@ impl Mailbox { const MAX_HANDLES: usize = 32; pub enum KernelObject { - AddressSpace(usize), - Frame(FrameAddr), - Thread(usize), + AddressSpace(AddressSpaceId), + Frame(OwnedFrame), + Mapping { + frame: OwnedFrame, + address_space: AddressSpaceId, + virtual_addr: VirtualAddr, + }, + Thread(TaskId), } pub struct Handle { @@ -118,15 +130,36 @@ impl HandleTable { } } - pub fn push(&mut self, handle: Handle) -> Option { + pub fn push(&mut self, handle: Handle) -> Result { for (i, slot) in self.handles.iter_mut().enumerate() { if slot.is_none() { *slot = Some(handle); - return Some(i); + return Ok(i); } } - None + Err(handle) + } + + pub fn remove(&mut self, id: usize) -> Option { + self.handles.get_mut(id).and_then(Option::take) + } + + pub fn take(&mut self, id: usize) -> Option { + self.handles.get_mut(id)?.take() + } + + pub fn put(&mut self, id: usize, handle: Handle) -> Result<(), Handle> { + let Some(slot) = self.handles.get_mut(id) else { + return Err(handle); + }; + + if slot.is_some() { + return Err(handle); + } + + *slot = Some(handle); + Ok(()) } pub fn get(&self, id: usize) -> Option<&Handle> { @@ -139,8 +172,8 @@ impl HandleTable { } pub struct Tcb { - pub id: usize, - pub as_id: usize, + pub id: TaskId, + pub as_id: AddressSpaceId, pub state: ThreadState, pub kernel_stack: KernelStack, pub context: ThreadContext, @@ -161,21 +194,23 @@ impl core::fmt::Debug for Tcb { impl Tcb { pub fn new_user( - id: usize, - as_id: usize, + as_id: AddressSpaceId, kernel_stack: KernelStack, entry: VirtualAddr, user_stack: VirtualAddr, ) -> Self { let context = ThreadContext::new(entry, user_stack, kernel_stack.top()); let mut handles = HandleTable::new(); - handles.push(Handle { + match handles.push(Handle { object: KernelObject::AddressSpace(as_id), rights: Rights::READ | Rights::WRITE | Rights::EXECUTE, - }); + }) { + Ok(_) => {} + Err(_) => unreachable!("Cant push root address space handle"), + }; Self { - id, + id: TaskId::new(0), as_id, state: ThreadState::Ready, kernel_stack, diff --git a/userspace/client/src/main.rs b/userspace/client/src/main.rs index 470d07b..e0c2838 100644 --- a/userspace/client/src/main.rs +++ b/userspace/client/src/main.rs @@ -8,12 +8,10 @@ pub extern "C" fn _start() -> ! { let msg = "Hello from client!"; println!("[client] Sent: {}", msg); // TODO: we assume the echo server is task 1 (spawned by omega3) - sys_send(1, msg.as_ptr() as usize, msg.len()).unwrap(); + sys_send(1, msg.as_bytes()).unwrap(); - let out = [0u8; 128]; - let out_ptr = out.as_ptr() as usize; - let max_len = out.len(); - let (actual_len, _) = sys_recv(out_ptr, max_len).unwrap(); + let mut out = [0u8; 128]; + let (actual_len, _) = sys_recv(&mut out).unwrap(); println!( "[client] Received: {}", core::str::from_utf8(&out[..actual_len]).unwrap() diff --git a/userspace/dusk-sys/src/lib.rs b/userspace/dusk-sys/src/lib.rs index 37d0657..bd20a21 100644 --- a/userspace/dusk-sys/src/lib.rs +++ b/userspace/dusk-sys/src/lib.rs @@ -10,15 +10,25 @@ pub enum Status { BadFileDescriptor = 3, NoSuchTask = 4, OutOfMemory = 5, + BadHandle = 6, } // Opaque handle type #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Handle(usize); +pub struct AddressSpaceHandle(usize); + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct FrameHandle(usize); + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct MappingHandle(usize); + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ThreadHandle(usize); // our own address space and thread handle are always given to us -pub const SELF_AS: Handle = Handle(0); -pub const SELF_THREAD: Handle = Handle(1); +pub const SELF_AS: AddressSpaceHandle = AddressSpaceHandle(0); +pub const SELF_THREAD: ThreadHandle = ThreadHandle(1); impl From for Status { fn from(value: usize) -> Self { @@ -28,6 +38,7 @@ impl From for Status { 3 => Self::BadFileDescriptor, 4 => Self::NoSuchTask, 5 => Self::OutOfMemory, + 6 => Self::BadHandle, _ => Self::InvalidArgument, } } @@ -125,15 +136,15 @@ pub fn sys_exit(exit_code: usize) -> ! { } } -pub fn sys_send(dest_task_id: usize, msg_ptr: usize, len: usize) -> Result<(), Status> { +pub fn sys_send(dest_task_id: usize, msg: &[u8]) -> Result<(), Status> { unsafe { let status: usize; asm!( "syscall", in("rdi") dest_task_id, - in("rsi") msg_ptr, - in("rdx") len, + in("rsi") msg.as_ptr(), + in("rdx") msg.len(), inlateout("rax") SyscallNumber::Send as usize => status, lateout("rcx") _, lateout("r11") _, @@ -147,7 +158,7 @@ pub fn sys_send(dest_task_id: usize, msg_ptr: usize, len: usize) -> Result<(), S } } -pub fn sys_recv(buf_ptr: usize, max_len: usize) -> Result<(usize, usize), Status> { +pub fn sys_recv(buf: &mut [u8]) -> Result<(usize, usize), Status> { let mut actual_len: usize = 0; let mut sender: usize = 0; @@ -156,8 +167,8 @@ pub fn sys_recv(buf_ptr: usize, max_len: usize) -> Result<(usize, usize), Status asm!( "syscall", - in("rdi") buf_ptr, - in("rsi") max_len, + in("rdi") buf.as_mut_ptr(), + in("rsi") buf.len(), in("rdx") &raw mut actual_len as usize, in("r10") &raw mut sender as usize, inlateout("rax") SyscallNumber::Recv as usize => status, @@ -173,7 +184,7 @@ pub fn sys_recv(buf_ptr: usize, max_len: usize) -> Result<(usize, usize), Status } } -pub fn sys_frame_alloc() -> Result { +pub fn sys_frame_alloc() -> Result { let mut handle: usize = 0; unsafe { let status: usize; @@ -189,12 +200,12 @@ pub fn sys_frame_alloc() -> Result { if status != 0 { Err(status.into()) } else { - Ok(Handle(handle)) + Ok(FrameHandle(handle)) } } } -pub fn sys_frame_dealloc(frame_handle: Handle) -> Result<(), Status> { +pub fn sys_frame_dealloc(frame_handle: FrameHandle) -> Result<(), Status> { unsafe { let status: usize; @@ -214,7 +225,7 @@ pub fn sys_frame_dealloc(frame_handle: Handle) -> Result<(), Status> { } } -pub fn sys_as_create() -> Result { +pub fn sys_as_create() -> Result { let mut handle: usize = 0; unsafe { let status: usize; @@ -230,17 +241,19 @@ pub fn sys_as_create() -> Result { if status != 0 { Err(status.into()) } else { - Ok(Handle(handle)) + Ok(AddressSpaceHandle(handle)) } } } pub fn sys_map( - as_handle: Handle, - frame_handle: Handle, + as_handle: AddressSpaceHandle, + frame_handle: FrameHandle, virtual_addr: usize, permissions: usize, -) -> Result<(), Status> { +) -> Result { + let mut handle: usize = 0; + unsafe { let status: usize; @@ -250,6 +263,7 @@ pub fn sys_map( in("rsi") frame_handle.0, in("rdx") virtual_addr, in("r10") permissions, + in("r8") &raw mut handle as usize, inlateout("rax") SyscallNumber::Map as usize => status, lateout("rcx") _, lateout("r11") _, @@ -258,19 +272,18 @@ pub fn sys_map( if status != 0 { Err(status.into()) } else { - Ok(()) + Ok(MappingHandle(handle)) } } } -pub fn sys_unmap(as_handle: Handle, virtual_addr: usize) -> Result<(), Status> { +pub fn sys_unmap(mapping_handle: MappingHandle) -> Result<(), Status> { unsafe { let status: usize; asm!( "syscall", - in("rdi") as_handle.0, - in("rsi") virtual_addr, + in("rdi") mapping_handle.0, inlateout("rax") SyscallNumber::Unmap as usize => status, lateout("rcx") _, lateout("r11") _, @@ -285,10 +298,10 @@ pub fn sys_unmap(as_handle: Handle, virtual_addr: usize) -> Result<(), Status> { } pub fn sys_task_create( - as_handle: Handle, + as_handle: AddressSpaceHandle, entry: usize, user_stack: usize, -) -> Result { +) -> Result { let mut handle: usize = 0; unsafe { @@ -308,7 +321,7 @@ pub fn sys_task_create( if status != 0 { Err(status.into()) } else { - Ok(Handle(handle)) + Ok(ThreadHandle(handle)) } } } diff --git a/userspace/echo/src/main.rs b/userspace/echo/src/main.rs index 3b3e503..7f42878 100644 --- a/userspace/echo/src/main.rs +++ b/userspace/echo/src/main.rs @@ -5,14 +5,14 @@ use dusk_sys::{println, sys_exit, sys_recv, sys_send}; #[unsafe(no_mangle)] pub extern "C" fn _start() -> ! { - let out = [0u8; 128]; + let mut out = [0u8; 128]; loop { - let (actual_len, sender) = sys_recv(out.as_ptr() as usize, out.len()).unwrap(); + let (actual_len, sender) = sys_recv(&mut out).unwrap(); println!( "[echo] Received: {}", core::str::from_utf8(&out[..actual_len]).unwrap() ); - sys_send(sender, out.as_ptr() as usize, actual_len).unwrap(); + sys_send(sender, &out[..actual_len]).unwrap(); } } diff --git a/userspace/omega3/src/main.rs b/userspace/omega3/src/main.rs index aca8ccd..3c32d76 100644 --- a/userspace/omega3/src/main.rs +++ b/userspace/omega3/src/main.rs @@ -5,8 +5,8 @@ mod cpio; mod elf; use dusk_sys::{ - Handle, SELF_AS, println, sys_as_create, sys_exit, sys_frame_alloc, sys_map, sys_task_create, - sys_unmap, sys_yield, + AddressSpaceHandle, SELF_AS, println, sys_as_create, sys_exit, sys_frame_alloc, sys_map, + sys_task_create, sys_unmap, sys_yield, }; // Mapped into the root task's address space by the kernel. @@ -40,10 +40,15 @@ pub extern "C" fn _start() -> ! { map_stack(client_as, STACK_TOP, STACK_PAGES); let _ = sys_task_create(client_as, client_entry, STACK_TOP).unwrap(); + let ptr = 0xDEAD_BEEF as *mut u32; + unsafe { + core::ptr::write_volatile(&mut *ptr, 0xDEAD_BEEF); + } + sys_exit(0); } -fn load_elf(elf: &elf::Elf, target_as: Handle) -> usize { +fn load_elf(elf: &elf::Elf, target_as: AddressSpaceHandle) -> usize { for header in elf.program_headers().unwrap() { let header = header.unwrap(); if header.segment_type != elf::ProgramHeaderType::Load || header.memory_size == 0 { @@ -66,7 +71,7 @@ fn load_elf(elf: &elf::Elf, target_as: Handle) -> usize { for page in (page_start..segment_end).step_by(0x1000) { let frame = sys_frame_alloc().unwrap(); - sys_map(SELF_AS, frame, SCRATCH_PAGE, 0b01).unwrap(); + let scratch_handle = sys_map(SELF_AS, frame, SCRATCH_PAGE, 0b01).unwrap(); unsafe { core::ptr::write_bytes(SCRATCH_PAGE as *mut u8, 0, 0x1000); @@ -84,7 +89,7 @@ fn load_elf(elf: &elf::Elf, target_as: Handle) -> usize { ); } } - sys_unmap(SELF_AS, SCRATCH_PAGE).unwrap(); + sys_unmap(scratch_handle).unwrap(); sys_map(target_as, frame, page, perms).unwrap(); } @@ -93,7 +98,7 @@ fn load_elf(elf: &elf::Elf, target_as: Handle) -> usize { elf.entry() } -fn map_stack(target_as: Handle, stack_top: usize, pages: usize) { +fn map_stack(target_as: AddressSpaceHandle, stack_top: usize, pages: usize) { for i in 1..=pages { let frame = sys_frame_alloc().unwrap(); let page_addr = stack_top - i * 0x1000;