xous-ipc-test/xous-rs/src/syscall.rs

1353 lines
47 KiB
Rust

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<MemoryAddress>, /* phys */
Option<MemoryAddress>, /* 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<MemoryAddress>, /* 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::<SysCall>() == mem::size_of::<usize>() * 8,
"SysCall is not the expected size (expected {}, got {})",
mem::size_of::<usize>() * 8,
mem::size_of::<SysCall>()
);
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<Self, Error> {
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<MemoryRange> {
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<MemoryAddress>,
virt: Option<MemoryAddress>,
size: usize,
flags: MemoryFlags,
) -> core::result::Result<MemoryRange, Error> {
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<SID, Error> {
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<SID, Error> {
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<CID, Error> {
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<CID, Error> {
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<MessageEnvelope, Error> {
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<Option<MessageEnvelope>, 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<Result, Error> {
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<Result, Error> {
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<Result, Error> {
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<T, U>(
f: fn(T) -> U,
arg: T,
) -> core::result::Result<crate::arch::WaitHandle<U>, 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, T>(f: F) -> core::result::Result<crate::arch::WaitHandle<T>, 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<T>(joiner: crate::arch::WaitHandle<T>) -> 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<F>(
args: ProcessArgsAsThread<F>,
) -> core::result::Result<crate::arch::ProcessHandleAsThread, Error>
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<crate::arch::ProcessHandle, Error> {
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),
// }
// }