use crate::{ pid_from_usize, CpuID, Error, MemoryAddress, MemoryFlags, MemoryMessage, MemoryRange, MemorySize, MemoryType, Message, MessageEnvelope, MessageSender, ProcessArgs, ProcessInit, Result, ScalarMessage, SysCallResult, ThreadInit, CID, PID, SID, }; // use num_derive::FromPrimitive; // use num_traits::FromPrimitive; #[cfg(not(target_os = "none"))] use crate::ProcessArgsAsThread; #[derive(Debug, PartialEq)] pub enum SysCall { /// Allocates pages of memory, equal to a total of `size` bytes. A physical /// address may be specified, which can be used to allocate regions such as /// memory-mapped I/O. /// /// If a virtual address is specified, then the returned pages are located /// at that address. Otherwise, they are located at the Default offset. /// /// # Errors /// /// * **BadAlignment**: Either the physical or virtual addresses aren't /// page-aligned, or the size isn't a multiple of the /// page width. /// * **OutOfMemory**: A contiguous chunk of memory couldn't be found, or /// the system's memory size has been exceeded. MapMemory( Option, /* phys */ Option, /* virt */ MemorySize, /* region size */ MemoryFlags, /* flags */ ), /// Release the memory back to the operating system. /// /// # Errors /// UnmapMemory(MemoryRange), /// Sets the offset and size of a given memory region. This call may only /// be made by processes that have not yet started, or processes that have a /// PPID of 1. Care must be taken to ensure this region doesn't run into /// other regions. Additionally, the base address must avoid the kernel /// regions. /// /// # Errors /// /// * **BadAlignment**: Either the physical or virtual addresses aren't /// page-aligned, or the size isn't a multiple of the /// page width. /// * **BadAddress**: The address conflicts with the kernel SetMemRegion( PID, /* pid */ MemoryType, /* region type */ MemoryAddress, /* region address */ usize, /* region size */ ), /// Add the given number of bytes to the heap. The number of bytes must be /// divisible by the page size. The newly-allocated pages will have the /// specified flags. To get the current heap base, call this with a size of /// `0`. /// /// # Returns /// /// * **MemoryRange(*mut usize /* The base of the heap */, usize /* the new /// size of the heap */) /// /// # Errors /// /// * **BadAlignment**: Either the physical or virtual addresses aren't /// page-aligned, or the size isn't a multiple of the /// page width. /// * **OutOfMemory**: A contiguous chunk of memory couldn't be found, or /// the system's memory size has been exceeded. IncreaseHeap(usize /* number of bytes to add */, MemoryFlags), /// Remove the given number of bytes from the heap. /// /// # Returns /// /// * **MemoryRange(*mut usize /* The base of the heap */, usize /* the new /// size of the heap */) /// /// # Errors /// /// * **BadAlignment**: Either the physical or virtual addresses aren't /// page-aligned, or the size isn't a multiple of the /// page width. /// * **OutOfMemory**: A contiguous chunk of memory couldn't be found, or /// the system's memory size has been exceeded. DecreaseHeap(usize /* desired heap size */), /// Set the specified flags on the virtual address range. This can be used /// to REMOVE flags on a memory region, for example to mark it as no-execute /// after writing program data. /// /// # Errors /// /// * **ProcessNotChild**: The given PID is not a child of the current /// process. /// * **MemoryInUse**: The given PID has already been started, and it is not /// legal to modify memory flags anymore. UpdateMemoryFlags( MemoryAddress, /* virt */ usize, /* number of pages */ MemoryFlags, /* new flags */ ), /// Pauses execution of the current thread and returns execution to the parent /// process. This may return at any time in the future, including immediately. Yield, /// This process will now wait for an event such as an IRQ or Message. WaitEvent, /// This thread will now wait for a message with the given server ID. You /// can set up a pool by having multiple threads call `ReceiveMessage` with /// the same SID. ReceiveMessage(SID), /// If a message is available for the specified server, return that message /// and resume execution. If no message is available, return `Result::None` /// immediately without blocking. TryReceiveMessage(SID), /// Stop running the given process and return control to the parent. This /// will force a Yield on the process currently running on the target CPU. /// This can be run during an Interrupt context. /// /// # Errors /// /// * **ProcessNotChild**: The given PID is not a child of the current /// process ReturnToParent(PID, CpuID), /// Claims an interrupt and unmasks it immediately. The provided function /// will be called from within an interrupt context, but using the ordinary /// privilege level of the process. /// /// # Errors /// /// * **InterruptNotFound**: The specified interrupt isn't valid on this /// system /// * **InterruptInUse**: The specified interrupt has already been claimed ClaimInterrupt( usize, /* IRQ number */ MemoryAddress, /* function pointer */ Option, /* argument */ ), /// Returns the interrupt back to the operating system and masks it again. /// This function is implicitly called when a process exits. /// /// # Errors /// /// * **InterruptNotFound**: The specified interrupt doesn't exist, or isn't /// assigned to this process. FreeInterrupt(usize /* IRQ number */), /// Resumes a process using the given context. A parent could use this /// function to implement multi-threading inside a child process, or to /// create a task switcher. /// /// To resume a process exactly where it left off, set `context_id` to `0`. /// This would be done in a very simple system that has no threads. /// /// If no more contexts are available when one is required, then the child /// automatically relinquishes its quantum. /// /// # Returns /// /// When this function returns, it provides a list of the processes and /// contexts that are ready to be run. Three can fit as return values. /// /// # Examples /// /// If a process called `yield()`, or if its quantum expired normally, then /// a single pair is returned: (pid, context). /// /// If the child process called `client_send()` and ended up blocking due to /// the server not being ready, then this would return no pairs. This thread /// or process should not be scheduled to run. /// /// If the child called `client_send()` and the server was ready, then the /// server process would be run immediately. If the child process' quantum /// expired while the server was running, then this function would return a /// single pair containing the PID of the server, and the context number. /// /// If the child called `client_send()` and the server was ready, then the /// server process would be run immediately. If the server then finishes, /// execution flow is returned to the child process. If the quantum then /// expires, this would return two pairs: the server's PID and its context /// when it called `client_reply()`, and the child's PID with its current /// context. /// /// If the server in turn called another server, and both servers ended up /// returning to the child before the quantum expired, then there would be /// three pairs returned. /// /// # Errors /// /// * **ProcessNotFound**: The requested process does not exist /// * **ProcessNotChild**: The given process was not a child process, and /// therefore couldn't be resumed. /// * **ProcessTerminated**: The process has crashed. SwitchTo(PID, usize /* context ID */), /// Get a list of contexts that can be run in the given PID. ReadyThreads(PID), /// Create a new Server with a specified address /// /// This will return a 128-bit Server ID that can be used to send messages /// to this server, as well as a connection ID. This connection ID will be /// unique per process, while the server ID is available globally. /// /// # Returns /// /// The specified SID, along with the connection ID for this process to talk /// to the server. /// /// # Errors /// /// * **OutOfMemory**: The server table was full and a new server couldn't /// be created. /// * **ServerExists**: The server hash is already in use. CreateServerWithAddress(SID /* server hash */), /// Connect to a server. This turns a 128-bit Serever ID into a 32-bit /// Connection ID. Blocks until the server is available. /// /// # Errors /// /// None Connect(SID /* server id */), /// Try to connect to a server. This turns a 128-bit Serever ID into a 32-bit /// Connection ID. /// /// # Errors /// /// * **ServerNotFound**: The server could not be found. TryConnect(SID /* server id */), /// Send a message to a server (blocking until it's ready) SendMessage(CID, Message), /// Try to send a message to a server TrySendMessage(CID, Message), /// Return a Borrowed memory region to the sender ReturnMemory(MessageSender, MemoryRange), /// Return a scalar to the sender ReturnScalar1(MessageSender, usize), /// Return two scalars to the sender ReturnScalar2(MessageSender, usize, usize), /// Spawn a new thread CreateThread(ThreadInit), /// Create a new process, setting the current process as the parent ID. /// Does not start the process immediately. CreateProcess(ProcessInit), /// Terminate the current process, closing all server connections. TerminateProcess, /// Shut down the entire system Shutdown, /// Create a new Server /// /// This will return a 128-bit Server ID that can be used to send messages /// to this server. The returned Derver ID is random. /// /// # Returns /// /// The SID, along with a Connection ID that can be used to immediately /// communicate with this process. /// /// # Errors /// /// * **OutOfMemory**: The server table was full and a new server couldn't /// be created. CreateServer, /// Establish a connection in the given process to the given server. This /// call can be used by a nameserver to make server connections without /// disclosing SIDs. ConnectForProcess(PID, SID), /// This syscall does not exist. It captures all possible /// arguments so detailed analysis can be performed. Invalid(usize, usize, usize, usize, usize, usize, usize), } // #[derive(FromPrimitive)] pub enum SysCallNumber { MapMemory = 2, Yield = 3, ReturnToParent = 4, ClaimInterrupt = 5, FreeInterrupt = 6, SwitchTo = 7, ReadyThreads = 8, WaitEvent = 9, IncreaseHeap = 10, DecreaseHeap = 11, UpdateMemoryFlags = 12, SetMemRegion = 13, CreateServerWithAddress = 14, ReceiveMessage = 15, SendMessage = 16, Connect = 17, CreateThread = 18, UnmapMemory = 19, ReturnMemory = 20, CreateProcess = 21, TerminateProcess = 22, Shutdown = 23, TrySendMessage = 24, TryConnect = 25, ReturnScalar1 = 26, ReturnScalar2 = 27, TryReceiveMessage = 28, CreateServer = 29, ConnectForProcess = 30, Invalid, } impl SysCallNumber { pub fn from(val: usize) -> SysCallNumber { use SysCallNumber::*; match val { 2 => MapMemory, 3 => Yield, 4 => ReturnToParent, 5 => ClaimInterrupt, 6 => FreeInterrupt, 7 => SwitchTo, 8 => ReadyThreads, 9 => WaitEvent, 10 => IncreaseHeap, 11 => DecreaseHeap, 12 => UpdateMemoryFlags, 13 => SetMemRegion, 14 => CreateServerWithAddress, 15 => ReceiveMessage, 16 => SendMessage, 17 => Connect, 18 => CreateThread, 19 => UnmapMemory, 20 => ReturnMemory, 21 => CreateProcess, 22 => TerminateProcess, 23 => Shutdown, 24 => TrySendMessage, 25 => TryConnect, 26 => ReturnScalar1, 27 => ReturnScalar2, 28 => TryReceiveMessage, 29 => CreateServer, 30 => ConnectForProcess, _ => Invalid, } } } impl SysCall { /// Convert the SysCall into an array of eight `usize` elements, /// suitable for passing to the kernel. pub fn as_args(&self) -> [usize; 8] { use core::mem; assert!( mem::size_of::() == mem::size_of::() * 8, "SysCall is not the expected size (expected {}, got {})", mem::size_of::() * 8, mem::size_of::() ); match self { SysCall::MapMemory(a1, a2, a3, a4) => [ SysCallNumber::MapMemory as usize, a1.map(|x| x.get()).unwrap_or_default(), a2.map(|x| x.get()).unwrap_or_default(), a3.get(), a4.bits(), 0, 0, 0, ], SysCall::UnmapMemory(range) => [ SysCallNumber::UnmapMemory as usize, range.as_ptr() as usize, range.len(), 0, 0, 0, 0, 0, ], SysCall::Yield => [SysCallNumber::Yield as usize, 0, 0, 0, 0, 0, 0, 0], SysCall::WaitEvent => [SysCallNumber::WaitEvent as usize, 0, 0, 0, 0, 0, 0, 0], SysCall::ReceiveMessage(sid) => { let s = sid.to_u32(); [ SysCallNumber::ReceiveMessage as usize, s.0 as _, s.1 as _, s.2 as _, s.3 as _, 0, 0, 0, ] } SysCall::TryReceiveMessage(sid) => { let s = sid.to_u32(); [ SysCallNumber::TryReceiveMessage as usize, s.0 as _, s.1 as _, s.2 as _, s.3 as _, 0, 0, 0, ] } SysCall::ConnectForProcess(pid, sid) => { let s = sid.to_u32(); [ SysCallNumber::ConnectForProcess as usize, pid.get() as _, s.0 as _, s.1 as _, s.2 as _, s.3 as _, 0, 0, ] } SysCall::ReturnToParent(a1, a2) => [ SysCallNumber::ReturnToParent as usize, a1.get() as usize, *a2 as usize, 0, 0, 0, 0, 0, ], SysCall::ClaimInterrupt(a1, a2, a3) => [ SysCallNumber::ClaimInterrupt as usize, *a1, a2.get(), a3.map(|x| x.get()).unwrap_or_default(), 0, 0, 0, 0, ], SysCall::FreeInterrupt(a1) => { [SysCallNumber::FreeInterrupt as usize, *a1, 0, 0, 0, 0, 0, 0] } SysCall::SwitchTo(a1, a2) => [ SysCallNumber::SwitchTo as usize, a1.get() as usize, *a2 as usize, 0, 0, 0, 0, 0, ], SysCall::ReadyThreads(a1) => [ SysCallNumber::ReadyThreads as usize, a1.get() as usize, 0, 0, 0, 0, 0, 0, ], SysCall::IncreaseHeap(a1, a2) => [ SysCallNumber::IncreaseHeap as usize, *a1 as usize, a2.bits(), 0, 0, 0, 0, 0, ], SysCall::DecreaseHeap(a1) => [ SysCallNumber::DecreaseHeap as usize, *a1 as usize, 0, 0, 0, 0, 0, 0, ], SysCall::UpdateMemoryFlags(a1, a2, a3) => [ SysCallNumber::UpdateMemoryFlags as usize, a1.get(), *a2 as usize, a3.bits(), 0, 0, 0, 0, ], SysCall::SetMemRegion(a1, a2, a3, a4) => [ SysCallNumber::SetMemRegion as usize, a1.get() as usize, *a2 as usize, a3.get(), *a4, 0, 0, 0, ], SysCall::CreateServerWithAddress(sid) => { let s = sid.to_u32(); [ SysCallNumber::CreateServerWithAddress as usize, s.0 as _, s.1 as _, s.2 as _, s.3 as _, 0, 0, 0, ] } SysCall::CreateServer => [SysCallNumber::CreateServer as usize, 0, 0, 0, 0, 0, 0, 0], SysCall::Connect(sid) => { let s = sid.to_u32(); [ SysCallNumber::Connect as usize, s.0 as _, s.1 as _, s.2 as _, s.3 as _, 0, 0, 0, ] } SysCall::SendMessage(a1, ref a2) => match a2 { Message::MutableBorrow(mm) | Message::Borrow(mm) | Message::Move(mm) => [ SysCallNumber::SendMessage as usize, *a1, a2.message_type(), mm.id as usize, mm.buf.as_ptr() as usize, mm.buf.len(), mm.offset.map(|x| x.get()).unwrap_or(0) as usize, mm.valid.map(|x| x.get()).unwrap_or(0) as usize, ], Message::Scalar(sc) | Message::BlockingScalar(sc) => [ SysCallNumber::SendMessage as usize, *a1, a2.message_type(), sc.id as usize, sc.arg1, sc.arg2, sc.arg3, sc.arg4, ], }, SysCall::ReturnMemory(sender, buf) => [ SysCallNumber::ReturnMemory as usize, sender.to_usize(), buf.as_ptr() as usize, buf.len(), 0, 0, 0, 0, ], SysCall::CreateThread(init) => { crate::arch::thread_to_args(SysCallNumber::CreateThread as usize, init) } SysCall::CreateProcess(init) => { crate::arch::process_to_args(SysCallNumber::CreateProcess as usize, init) } SysCall::TerminateProcess => [ SysCallNumber::TerminateProcess as usize, 0, 0, 0, 0, 0, 0, 0, ], SysCall::Shutdown => [SysCallNumber::Shutdown as usize, 0, 0, 0, 0, 0, 0, 0], SysCall::TryConnect(sid) => { let s = sid.to_u32(); [ SysCallNumber::TryConnect as usize, s.0 as _, s.1 as _, s.2 as _, s.3 as _, 0, 0, 0, ] } SysCall::TrySendMessage(a1, ref a2) => match a2 { Message::MutableBorrow(mm) | Message::Borrow(mm) | Message::Move(mm) => [ SysCallNumber::TrySendMessage as usize, *a1, a2.message_type(), mm.id as usize, mm.buf.as_ptr() as usize, mm.buf.len(), mm.offset.map(|x| x.get()).unwrap_or(0) as usize, mm.valid.map(|x| x.get()).unwrap_or(0) as usize, ], Message::Scalar(sc) | Message::BlockingScalar(sc) => [ SysCallNumber::TrySendMessage as usize, *a1, a2.message_type(), sc.id as usize, sc.arg1, sc.arg2, sc.arg3, sc.arg4, ], }, SysCall::ReturnScalar1(sender, arg1) => [ SysCallNumber::ReturnScalar1 as usize, sender.to_usize(), *arg1, 0, 0, 0, 0, 0, ], SysCall::ReturnScalar2(sender, arg1, arg2) => [ SysCallNumber::ReturnScalar2 as usize, sender.to_usize(), *arg1, *arg2, 0, 0, 0, 0, ], SysCall::Invalid(a1, a2, a3, a4, a5, a6, a7) => [ SysCallNumber::Invalid as usize, *a1, *a2, *a3, *a4, *a5, *a6, *a7, ], } } #[allow(clippy::too_many_arguments)] pub fn from_args( a0: usize, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize, a6: usize, a7: usize, ) -> core::result::Result { Ok(match SysCallNumber::from(a0) { SysCallNumber::MapMemory => SysCall::MapMemory( MemoryAddress::new(a1), MemoryAddress::new(a2), MemoryAddress::new(a3).ok_or(Error::InvalidSyscall)?, MemoryFlags::from_bits(a4).ok_or(Error::InvalidSyscall)?, ), SysCallNumber::UnmapMemory => { SysCall::UnmapMemory(MemoryRange::new(a1, a2).or(Err(Error::InvalidSyscall))?) } SysCallNumber::Yield => SysCall::Yield, SysCallNumber::WaitEvent => SysCall::WaitEvent, SysCallNumber::ReceiveMessage => { SysCall::ReceiveMessage(SID::from_u32(a1 as _, a2 as _, a3 as _, a4 as _)) } SysCallNumber::TryReceiveMessage => { SysCall::TryReceiveMessage(SID::from_u32(a1 as _, a2 as _, a3 as _, a4 as _)) } SysCallNumber::ReturnToParent => SysCall::ReturnToParent(pid_from_usize(a1)?, a2), SysCallNumber::ClaimInterrupt => SysCall::ClaimInterrupt( a1, MemoryAddress::new(a2).ok_or(Error::InvalidSyscall)?, MemoryAddress::new(a3), ), SysCallNumber::FreeInterrupt => SysCall::FreeInterrupt(a1), SysCallNumber::SwitchTo => SysCall::SwitchTo(pid_from_usize(a1)?, a2 as usize), SysCallNumber::ReadyThreads => SysCall::ReadyThreads(pid_from_usize(a1)?), SysCallNumber::IncreaseHeap => SysCall::IncreaseHeap( a1 as usize, MemoryFlags::from_bits(a2).ok_or(Error::InvalidSyscall)?, ), SysCallNumber::DecreaseHeap => SysCall::DecreaseHeap(a1 as usize), SysCallNumber::UpdateMemoryFlags => SysCall::UpdateMemoryFlags( MemoryAddress::new(a1).ok_or(Error::InvalidSyscall)?, a2 as usize, MemoryFlags::from_bits(a3).ok_or(Error::InvalidSyscall)?, ), SysCallNumber::SetMemRegion => SysCall::SetMemRegion( pid_from_usize(a1)?, MemoryType::from(a2), MemoryAddress::new(a3).ok_or(Error::InvalidSyscall)?, a4, ), SysCallNumber::CreateServerWithAddress => { SysCall::CreateServerWithAddress(SID::from_u32(a1 as _, a2 as _, a3 as _, a4 as _)) } SysCallNumber::CreateServer => SysCall::CreateServer, SysCallNumber::Connect => { SysCall::Connect(SID::from_u32(a1 as _, a2 as _, a3 as _, a4 as _)) } SysCallNumber::SendMessage => match a2 { 1 => SysCall::SendMessage( a1, Message::MutableBorrow(MemoryMessage { id: a3, buf: MemoryRange::new(a4, a5)?, offset: MemoryAddress::new(a6), valid: MemorySize::new(a7), }), ), 2 => SysCall::SendMessage( a1, Message::Borrow(MemoryMessage { id: a3, buf: MemoryRange::new(a4, a5)?, offset: MemoryAddress::new(a6), valid: MemorySize::new(a7), }), ), 3 => SysCall::SendMessage( a1, Message::Move(MemoryMessage { id: a3, buf: MemoryRange::new(a4, a5)?, offset: MemoryAddress::new(a6), valid: MemorySize::new(a7), }), ), 4 => SysCall::SendMessage( a1, Message::Scalar(ScalarMessage { id: a3, arg1: a4, arg2: a5, arg3: a6, arg4: a7, }), ), 5 => SysCall::SendMessage( a1, Message::BlockingScalar(ScalarMessage { id: a3, arg1: a4, arg2: a5, arg3: a6, arg4: a7, }), ), _ => SysCall::Invalid(a1, a2, a3, a4, a5, a6, a7), }, SysCallNumber::ReturnMemory => { SysCall::ReturnMemory(MessageSender::from_usize(a1), MemoryRange::new(a2, a3)?) } SysCallNumber::CreateThread => { SysCall::CreateThread(crate::arch::args_to_thread(a1, a2, a3, a4, a5, a6, a7)?) } SysCallNumber::CreateProcess => { SysCall::CreateProcess(crate::arch::args_to_process(a1, a2, a3, a4, a5, a6, a7)?) } SysCallNumber::TerminateProcess => SysCall::TerminateProcess, SysCallNumber::Shutdown => SysCall::Shutdown, SysCallNumber::TryConnect => { SysCall::TryConnect(SID::from_u32(a1 as _, a2 as _, a3 as _, a4 as _)) } SysCallNumber::TrySendMessage => match a2 { 1 => SysCall::TrySendMessage( a1, Message::MutableBorrow(MemoryMessage { id: a3, buf: MemoryRange::new(a4, a5)?, offset: MemoryAddress::new(a6), valid: MemorySize::new(a7), }), ), 2 => SysCall::TrySendMessage( a1, Message::Borrow(MemoryMessage { id: a3, buf: MemoryRange::new(a4, a5)?, offset: MemoryAddress::new(a6), valid: MemorySize::new(a7), }), ), 3 => SysCall::TrySendMessage( a1, Message::Move(MemoryMessage { id: a3, buf: MemoryRange::new(a4, a5)?, offset: MemoryAddress::new(a6), valid: MemorySize::new(a7), }), ), 4 => SysCall::TrySendMessage( a1, Message::Scalar(ScalarMessage { id: a3, arg1: a4, arg2: a5, arg3: a6, arg4: a7, }), ), 5 => SysCall::TrySendMessage( a1, Message::BlockingScalar(ScalarMessage { id: a3, arg1: a4, arg2: a5, arg3: a6, arg4: a7, }), ), _ => SysCall::Invalid(a1, a2, a3, a4, a5, a6, a7), }, SysCallNumber::ReturnScalar1 => { SysCall::ReturnScalar1(MessageSender::from_usize(a1), a2) } SysCallNumber::ReturnScalar2 => { SysCall::ReturnScalar2(MessageSender::from_usize(a1), a2, a3) } SysCallNumber::ConnectForProcess => SysCall::ConnectForProcess( PID::new(a1 as _).ok_or(Error::InvalidSyscall)?, SID::from_u32(a2 as _, a3 as _, a4 as _, a5 as _), ), SysCallNumber::Invalid => SysCall::Invalid(a1, a2, a3, a4, a5, a6, a7), }) } /// Returns `true` if the associated syscall is a message that has memory attached to it pub fn has_memory(&self) -> bool { match self { SysCall::TrySendMessage(_, msg) | SysCall::SendMessage(_, msg) => { matches!(msg, Message::Move(_) | Message::Borrow(_) | Message::MutableBorrow(_)) } SysCall::ReturnMemory(_, _) => true, _ => false, } } /// Returns `true` if the associated syscall is a message that is a Move pub fn is_move(&self) -> bool { match self { SysCall::TrySendMessage(_, msg) | SysCall::SendMessage(_, msg) => { matches!(msg, Message::Move(_)) } _ => false, } } /// Returns `true` if the associated syscall is a message that is a Borrow pub fn is_borrow(&self) -> bool { match self { SysCall::TrySendMessage(_, msg) | SysCall::SendMessage(_, msg) => { matches!(msg, Message::Borrow(_)) } _ => false, } } /// Returns `true` if the associated syscall is a message that is a MutableBorrow pub fn is_mutableborrow(&self) -> bool { match self { SysCall::TrySendMessage(_, msg) | SysCall::SendMessage(_, msg) => { matches!(msg, Message::MutableBorrow(_)) } _ => false, } } /// Returns `true` if the associated syscall is returning memory pub fn is_return_memory(&self) -> bool { matches!(self, SysCall::ReturnMemory(_, _)) } /// If the syscall has memory attached to it, return the memory pub fn memory(&self) -> Option { match self { SysCall::TrySendMessage(_, msg) | SysCall::SendMessage(_, msg) => match msg { Message::Move(memory_message) | Message::Borrow(memory_message) | Message::MutableBorrow(memory_message) => Some(memory_message.buf), _ => None, }, SysCall::ReturnMemory(_, range) => Some(*range), _ => None, } } /// Returns `true` if the given syscall may be called from an IRQ context pub fn can_call_from_interrupt(&self) -> bool { matches!(self, SysCall::TrySendMessage(_, _) | SysCall::TryConnect(_) | SysCall::TryReceiveMessage(_) | SysCall::ReturnToParent(_, _) | SysCall::ReturnScalar2(_, _, _) | SysCall::ReturnScalar1(_, _) | SysCall::ReturnMemory(_, _)) } } extern "Rust" { // fn _xous_syscall_rust( // nr: usize, // a1: usize, // a2: usize, // a3: usize, // a4: usize, // a5: usize, // a6: usize, // a7: usize, // ret: &mut Result, // ); fn _xous_syscall( nr: usize, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize, a6: usize, a7: usize, ret: &mut Result, ); } /// Map the given physical address to the given virtual address. /// The `size` field must be page-aligned. pub fn map_memory( phys: Option, virt: Option, size: usize, flags: MemoryFlags, ) -> core::result::Result { crate::arch::map_memory_pre(&phys, &virt, size, flags)?; let result = rsyscall(SysCall::MapMemory( phys, virt, MemorySize::new(size).ok_or(Error::InvalidSyscall)?, flags, ))?; if let Result::MemoryRange(range) = result { Ok(crate::arch::map_memory_post( phys, virt, size, flags, range, )?) } else if let Result::Error(e) = result { Err(e) } else { Err(Error::InternalError) } } /// Map the given physical address to the given virtual address. /// The `size` field must be page-aligned. pub fn unmap_memory(range: MemoryRange) -> core::result::Result<(), Error> { crate::arch::unmap_memory_pre(&range)?; let result = rsyscall(SysCall::UnmapMemory(range))?; if let crate::Result::Ok = result { crate::arch::unmap_memory_post(range)?; Ok(()) } else if let Result::Error(e) = result { Err(e) } else { Err(Error::InternalError) } } /// Map the given physical address to the given virtual address. /// The `size` field must be page-aligned. pub fn return_memory(sender: MessageSender, mem: MemoryRange) -> core::result::Result<(), Error> { let result = rsyscall(SysCall::ReturnMemory(sender, mem))?; if let crate::Result::Ok = result { Ok(()) } else if let Result::Error(e) = result { Err(e) } else { Err(Error::InternalError) } } /// Map the given physical address to the given virtual address. /// The `size` field must be page-aligned. pub fn return_scalar(sender: MessageSender, val: usize) -> core::result::Result<(), Error> { let result = rsyscall(SysCall::ReturnScalar1(sender, val))?; if let crate::Result::Ok = result { Ok(()) } else if let Result::Error(e) = result { Err(e) } else { Err(Error::InternalError) } } /// Map the given physical address to the given virtual address. /// The `size` field must be page-aligned. pub fn return_scalar2( sender: MessageSender, val1: usize, val2: usize, ) -> core::result::Result<(), Error> { let result = rsyscall(SysCall::ReturnScalar2(sender, val1, val2))?; if let crate::Result::Ok = result { Ok(()) } else if let Result::Error(e) = result { Err(e) } else { Err(Error::InternalError) } } /// Claim a hardware interrupt for this process. pub fn claim_interrupt( irq_no: usize, callback: fn(irq_no: usize, arg: *mut usize), arg: *mut usize, ) -> core::result::Result<(), Error> { let result = rsyscall(SysCall::ClaimInterrupt( irq_no, MemoryAddress::new(callback as *mut usize as usize).ok_or(Error::InvalidSyscall)?, MemoryAddress::new(arg as *mut usize as usize), ))?; if let crate::Result::Ok = result { Ok(()) } else if let Result::Error(e) = result { Err(e) } else { Err(Error::InternalError) } } /// Create a new server with the given name. This enables other processes to /// connect to this server to send messages. The name is a UTF-8 token that /// will be mixed with other random data that is unique to each process. /// That way, if a process crashes and is restarted, it can keep the same /// name. However, other processes cannot spoof this process. /// /// # Errors /// /// * **OutOfMemory**: No more servers may be created /// * **ServerExists**: A server has already registered with that name /// * **InvalidString**: The name was not a valid UTF-8 string pub fn create_server_with_address(name_bytes: &[u8; 16]) -> core::result::Result { let sid = SID::from_bytes(name_bytes).ok_or(Error::InvalidString)?; let result = rsyscall(SysCall::CreateServerWithAddress(sid))?; if let Result::NewServerID(sid, _cid) = result { Ok(sid) } else if let Result::Error(e) = result { Err(e) } else { Err(Error::InternalError) } } /// Create a new server with a random name. This enables other processes to /// connect to this server to send messages. A random server ID is generated /// by the kernel and returned to the caller. This address can then be registered /// to a namserver. /// /// # Errors /// /// * **OutOfMemory**: No more servers may be created pub fn create_server() -> core::result::Result { let result = rsyscall(SysCall::CreateServer)?; if let Result::NewServerID(sid, _cid) = result { Ok(sid) } else if let Result::Error(e) = result { Err(e) } else { Err(Error::InternalError) } } /// Connect to a server with the given SID pub fn connect(server: SID) -> core::result::Result { let result = rsyscall(SysCall::Connect(server))?; if let Result::ConnectionID(cid) = result { Ok(cid) } else if let Result::Error(e) = result { Err(e) } else { Err(Error::InternalError) } } /// Connect to a server with the given SID pub fn try_connect(server: SID) -> core::result::Result { let result = rsyscall(SysCall::TryConnect(server))?; if let Result::ConnectionID(cid) = result { Ok(cid) } else if let Result::Error(e) = result { Err(e) } else { Err(Error::InternalError) } } /// Suspend the current process until a message is received. This thread will /// block until a message is received. /// /// # Errors /// pub fn receive_message(server: SID) -> core::result::Result { let result = rsyscall(SysCall::ReceiveMessage(server)).expect("Couldn't call ReceiveMessage"); if let Result::Message(envelope) = result { Ok(envelope) } else if let Result::Error(e) = result { Err(e) } else { Err(Error::InternalError) } } /// Retrieve a message from the message queue for the provided server. If no message /// is available, returns `Ok(None)` without blocking /// /// # Errors /// pub fn try_receive_message(server: SID) -> core::result::Result, Error> { let result = rsyscall(SysCall::TryReceiveMessage(server)).expect("Couldn't call ReceiveMessage"); if let Result::Message(envelope) = result { Ok(Some(envelope)) } else if result == Result::None { Ok(None) } else if let Result::Error(e) = result { Err(e) } else { Err(Error::InternalError) } } /// Send a message to a server. Depending on the mesage type (move or borrow), it /// will either block (borrow) or return immediately (move). /// If the message type is `borrow`, then the memory addresses pointed to will be /// unavailable to this process until this function returns. /// /// # Errors /// /// * **ServerNotFound**: The server does not exist so the connection is now invalid /// * **BadAddress**: The client tried to pass a Memory message using an address it doesn't own /// * **ServerQueueFull**: The queue in the server is full, and this call would block /// * **Timeout**: The timeout limit has been reached pub fn try_send_message(connection: CID, message: Message) -> core::result::Result { let result = rsyscall(SysCall::TrySendMessage(connection, message)); match result { Ok(Result::Ok) => Ok(Result::Ok), Ok(Result::Scalar1(a)) => Ok(Result::Scalar1(a)), Ok(Result::Scalar2(a, b)) => Ok(Result::Scalar2(a, b)), Err(e) => Err(e), v => panic!("Unexpected return value: {:?}", v), } } /// Connect to a server on behalf of another process. This can be used by a name /// resolution server to securely create connections without disclosing a SID. /// /// # Errors /// /// * **ServerNotFound**: The server does not exist so the connection is now invalid /// * **BadAddress**: The client tried to pass a Memory message using an address it doesn't own /// * **ServerQueueFull**: The queue in the server is full, and this call would block /// * **Timeout**: The timeout limit has been reached pub fn connect_for_process(pid: PID, sid: SID) -> core::result::Result { let result = rsyscall(SysCall::ConnectForProcess(pid, sid)); match result { Ok(Result::ConnectionID(cid)) => Ok(Result::ConnectionID(cid)), Err(e) => Err(e), v => panic!("Unexpected return value: {:?}", v), } } /// Send a message to a server. Depending on the mesage type (move or borrow), it /// will either block (borrow) or return immediately (move). /// If the message type is `borrow`, then the memory addresses pointed to will be /// unavailable to this process until this function returns. /// /// If the server queue is full, this will block. /// /// # Errors /// /// * **ServerNotFound**: The server does not exist so the connection is now invalid /// * **BadAddress**: The client tried to pass a Memory message using an address it doesn't own /// * **Timeout**: The timeout limit has been reached pub fn send_message(connection: CID, message: Message) -> core::result::Result { let result = rsyscall(SysCall::SendMessage(connection, message)); match result { Ok(Result::Ok) => Ok(Result::Ok), Ok(Result::Scalar1(a)) => Ok(Result::Scalar1(a)), Ok(Result::Scalar2(a, b)) => Ok(Result::Scalar2(a, b)), Err(e) => Err(e), v => panic!("Unexpected return value: {:?}", v), } } pub fn terminate_process() { rsyscall(SysCall::TerminateProcess).expect("terminate_process returned an error"); } /// Return execution to the kernel. This function may return at any time, /// including immediately pub fn yield_slice() { rsyscall(SysCall::Yield).expect("yield_slice returned an error"); } /// Return execution to the kernel and wait for a message or an interrupt. pub fn wait_event() { rsyscall(SysCall::WaitEvent).expect("wait_event returned an error"); } pub fn create_thread_simple( f: fn(T) -> U, arg: T, ) -> core::result::Result, Error> where T: Send + 'static, U: Send + 'static, { let thread_info = crate::arch::create_thread_simple_pre(&f, &arg)?; rsyscall(SysCall::CreateThread(thread_info)).and_then(|result| { if let Result::ThreadID(thread_id) = result { crate::arch::create_thread_simple_post(f, arg, thread_id) } else { Err(Error::InternalError) } }) } /// Create a new thread with the given closure. pub fn create_thread(f: F) -> core::result::Result, Error> where F: FnOnce() -> T, F: Send + 'static, T: Send + 'static, { let thread_info = crate::arch::create_thread_pre(&f)?; rsyscall(SysCall::CreateThread(thread_info)).and_then(|result| { if let Result::ThreadID(thread_id) = result { crate::arch::create_thread_post(f, thread_id) } else { Err(Error::InternalError) } }) } /// Wait for a thread to finish pub fn wait_thread(joiner: crate::arch::WaitHandle) -> SysCallResult { crate::arch::wait_thread(joiner) } /// Create a new process by running it in its own thread #[cfg(not(target_os = "none"))] pub fn create_process_as_thread( args: ProcessArgsAsThread, ) -> core::result::Result where F: FnOnce() + Send + 'static, { let process_init = crate::arch::create_process_pre_as_thread(&args)?; rsyscall(SysCall::CreateProcess(process_init)).and_then(|result| { if let Result::ProcessID(pid) = result { crate::arch::create_process_post_as_thread(args, process_init, pid) } else { Err(Error::InternalError) } }) } /// Wait for a thread to finish #[cfg(not(target_os = "none"))] pub fn wait_process_as_thread(joiner: crate::arch::ProcessHandleAsThread) -> SysCallResult { crate::arch::wait_process_as_thread(joiner) } pub fn create_process( args: ProcessArgs, ) -> core::result::Result { let process_init = crate::arch::create_process_pre(&args)?; rsyscall(SysCall::CreateProcess(process_init)).and_then(|result| { if let Result::ProcessID(pid) = result { crate::arch::create_process_post(args, process_init, pid) } else { Err(Error::InternalError) } }) } /// Wait for a thread to finish pub fn wait_process(joiner: crate::arch::ProcessHandle) -> SysCallResult { crate::arch::wait_process(joiner) } pub fn rsyscall(call: SysCall) -> SysCallResult { let mut ret = Result::Ok; let args = call.as_args(); unsafe { _xous_syscall( args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], &mut ret, ) }; match ret { Result::Error(e) => Err(e), other => Ok(other), } } // /// This is dangerous, but fast. // pub unsafe fn dangerous_syscall(call: SysCall) -> SyscallResult { // use core::mem::{transmute, MaybeUninit}; // let mut ret = MaybeUninit::uninit().assume_init(); // let presto = transmute::<_, (usize, usize, usize, usize, usize, usize, usize, usize)>(call); // _xous_syscall_rust( // presto.0, presto.1, presto.2, presto.3, presto.4, presto.5, presto.6, presto.7, &mut ret, // ); // match ret { // Result::Error(e) => Err(e), // other => Ok(other), // } // }