use core::convert::TryInto; use core::num::{NonZeroU8, NonZeroUsize}; pub type MemoryAddress = NonZeroUsize; pub type MemorySize = NonZeroUsize; pub type StackPointer = usize; pub type MessageId = usize; pub type PID = NonZeroU8; pub type Connection = usize; #[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Copy, Clone)] pub struct MessageSender { data: usize, } impl MessageSender { pub fn to_usize(&self) -> usize { self.data } pub fn from_usize(data: usize) -> Self { MessageSender { data } } pub fn pid(&self) -> Option { let pid_u8 = ((self.data >> 24) & 0xff) as u8; PID::new(pid_u8) } } impl core::default::Default for MessageSender { fn default() -> Self { MessageSender { data: 0 } } } impl core::fmt::Display for MessageSender { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "data: {}", self.data) } } /// Server ID #[derive(Debug, Copy, Clone, PartialEq)] pub struct SID((u32, u32, u32, u32)); impl SID { pub fn from_bytes(b: &[u8]) -> Option { if b.len() > 16 { None } else { let mut sid = (0, 0, 0, 0); let mut byte_iter = b.chunks_exact(4); if let Some(val) = byte_iter.next() { sid.0 = u32::from_le_bytes(val.try_into().ok()?); } if let Some(val) = byte_iter.next() { sid.1 = u32::from_le_bytes(val.try_into().ok()?); } if let Some(val) = byte_iter.next() { sid.2 = u32::from_le_bytes(val.try_into().ok()?); } if let Some(val) = byte_iter.next() { sid.3 = u32::from_le_bytes(val.try_into().ok()?); } Some(SID(sid)) } } pub fn from_u32(a0: u32, a1: u32, a2: u32, a3: u32) -> SID { SID((a0, a1, a2, a3)) } pub fn to_u32(&self) -> (u32, u32, u32, u32) { ((self.0).0, (self.0).1, (self.0).2, (self.0).3) } } impl core::str::FromStr for SID { type Err = (); fn from_str(s: &str) -> core::result::Result { Self::from_bytes(s.as_bytes()).ok_or(()) } } /// Connection ID pub type CID = usize; /// Context ID pub type TID = usize; /// Equivalent to a RISC-V Hart ID pub type CpuID = usize; #[derive(Debug, PartialEq, Copy, Clone)] pub struct MemoryRange { pub addr: MemoryAddress, pub size: MemorySize, } bitflags! { /// Flags to be passed to the MapMemory struct. /// Note that it is an error to have memory be /// writable and not readable. pub struct MemoryFlags: usize { /// Free this memory const FREE = 0b0000_0000; /// Immediately allocate this memory. Otherwise it will /// be demand-paged. This is implicitly set when `phys` /// is not 0. const RESERVE = 0b0000_0001; /// Allow the CPU to read from this page. const R = 0b0000_0010; /// Allow the CPU to write to this page. const W = 0b0000_0100; /// Allow the CPU to execute from this page. const X = 0b0000_1000; } } pub fn pid_from_usize(src: usize) -> core::result::Result { if src > u8::MAX as _ { return Err(Error::InvalidPID); } Ok(PID::new(src as u8).ok_or(Error::InvalidPID)?) } #[repr(usize)] #[derive(Debug, PartialEq)] pub enum Error { NoError = 0, BadAlignment = 1, BadAddress = 2, OutOfMemory = 3, MemoryInUse = 4, InterruptNotFound = 5, InterruptInUse = 6, InvalidString = 7, ServerExists = 8, ServerNotFound = 9, ProcessNotFound = 10, ProcessNotChild = 11, ProcessTerminated = 12, Timeout = 13, InternalError = 14, ServerQueueFull = 15, ThreadNotAvailable = 16, UnhandledSyscall = 17, InvalidSyscall = 18, ShareViolation = 19, InvalidThread = 20, InvalidPID = 21, UnknownError = 22, } impl Error { pub fn from_usize(arg: usize) -> Self { use crate::Error::*; match arg { 0 => NoError, 1 => BadAlignment, 2 => BadAddress, 3 => OutOfMemory, 4 => MemoryInUse, 5 => InterruptNotFound, 6 => InterruptInUse, 7 => InvalidString, 8 => ServerExists, 9 => ServerNotFound, 10 => ProcessNotFound, 11 => ProcessNotChild, 12 => ProcessTerminated, 13 => Timeout, 14 => InternalError, 15 => ServerQueueFull, 16 => ThreadNotAvailable, 17 => UnhandledSyscall, 18 => InvalidSyscall, 19 => ShareViolation, 20 => InvalidThread, 21 => InvalidPID, _ => UnknownError, } } pub fn to_usize(&self) -> usize { use crate::Error::*; match *self { NoError => 0, BadAlignment => 1, BadAddress => 2, OutOfMemory => 3, MemoryInUse => 4, InterruptNotFound => 5, InterruptInUse => 6, InvalidString => 7, ServerExists => 8, ServerNotFound => 9, ProcessNotFound => 10, ProcessNotChild => 11, ProcessTerminated => 12, Timeout => 13, InternalError => 14, ServerQueueFull => 15, ThreadNotAvailable => 16, UnhandledSyscall => 17, InvalidSyscall => 18, ShareViolation => 19, InvalidThread => 20, InvalidPID => 21, UnknownError => usize::MAX, } } } #[repr(C)] pub struct Context { stack: StackPointer, pid: PID, } #[repr(C)] #[derive(Debug, PartialEq)] /// A struct describing memory that is passed between processes. /// The `buf` value will get translated as necessary. pub struct MemoryMessage { /// A user-assignable message ID. pub id: MessageId, /// The offset of the buffer. This address will get transformed when the /// message is moved between processes. pub buf: MemoryRange, /// The offset within the buffer where the interesting stuff starts. pub offset: Option, /// How many bytes in the buffer are valid pub valid: Option, } impl MemoryMessage { pub fn from_usize( id: usize, addr: usize, size: usize, offset: usize, valid: usize, ) -> Option { let addr = match MemoryAddress::new(addr) { None => return None, Some(s) => s, }; let size = match MemorySize::new(size) { None => return None, Some(s) => s, }; let buf = MemoryRange { addr, size }; let offset = MemoryAddress::new(offset); let valid = MemorySize::new(valid); Some(MemoryMessage { id, buf, offset, valid, }) } pub fn to_usize(&self) -> [usize; 5] { [ self.id, self.buf.addr.get(), self.buf.size.get(), self.offset.map(|e| e.get()).unwrap_or(0), self.valid.map(|e| e.get()).unwrap_or(0), ] } } #[repr(C)] #[derive(Debug, PartialEq, Clone, Copy)] /// A simple scalar message. This is similar to a `move` message. pub struct ScalarMessage { pub id: MessageId, pub arg1: usize, pub arg2: usize, pub arg3: usize, pub arg4: usize, } impl ScalarMessage { pub fn from_usize( id: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, ) -> ScalarMessage { ScalarMessage { id, arg1, arg2, arg3, arg4, } } pub fn to_usize(&self) -> [usize; 5] { [self.id, self.arg1, self.arg2, self.arg3, self.arg4] } } #[repr(usize)] #[derive(Debug, PartialEq)] pub enum Message { MutableBorrow(MemoryMessage), Borrow(MemoryMessage), Move(MemoryMessage), Scalar(ScalarMessage), BlockingScalar(ScalarMessage), } impl Message { /// Determine whether the specified Message will block pub fn is_blocking(&self) -> bool { match *self { Message::MutableBorrow(_) | Message::Borrow(_) | Message::BlockingScalar(_) => true, Message::Move(_) | Message::Scalar(_) => false, } } /// Determine whether the specified message has data attached pub fn has_memory(&self) -> bool { match *self { Message::MutableBorrow(_) | Message::Borrow(_) | Message::Move(_) => true, Message::BlockingScalar(_) | Message::Scalar(_) => false, } } pub fn memory(&self) -> Option<&MemoryRange> { match self { Message::MutableBorrow(mem) | Message::Borrow(mem) | Message::Move(mem) => { Some(&mem.buf) } Message::BlockingScalar(_) | Message::Scalar(_) => None, } } pub fn message_type(&self) -> usize { match *self { Message::MutableBorrow(_) => 1, Message::Borrow(_) => 2, Message::Move(_) => 3, Message::Scalar(_) => 4, Message::BlockingScalar(_) => 5, } } } #[repr(C)] #[derive(Debug, PartialEq)] pub struct MessageEnvelope { pub sender: MessageSender, pub body: Message, } impl MessageEnvelope { pub fn to_usize(&self) -> [usize; 7] { let ret = match &self.body { Message::MutableBorrow(m) => (0, m.to_usize()), Message::Borrow(m) => (1, m.to_usize()), Message::Move(m) => (2, m.to_usize()), Message::Scalar(m) => (3, m.to_usize()), Message::BlockingScalar(m) => (4, m.to_usize()), }; [ self.sender.to_usize(), ret.0, ret.1[0], ret.1[1], ret.1[2], ret.1[3], ret.1[4], ] } } #[cfg(not(feature = "forget-memory-messages"))] /// When a MessageEnvelope goes out of scope, return the memory. It must either /// go to the kernel (in the case of a Move), or back to the borrowed process /// (in the case of a Borrow). Ignore Scalar messages. impl Drop for MessageEnvelope { fn drop(&mut self) { match &self.body { Message::Borrow(x) | Message::MutableBorrow(x) => { crate::syscall::return_memory(self.sender, x.buf).expect("couldn't return memory") } Message::Move(msg) => { crate::syscall::unmap_memory(msg.buf).expect("couldn't free memory message") } _ => (), } } } impl MemoryRange { pub fn new(addr: usize, size: usize) -> core::result::Result { assert!( addr != 0, "tried to construct a memory range with a null pointer" ); assert!(size != 0, "tried to construct a zero-length memory range"); Ok(MemoryRange { addr: MemoryAddress::new(addr).ok_or(Error::BadAddress)?, size: MemorySize::new(size).ok_or(Error::BadAddress)?, }) } pub fn from_parts(addr: MemoryAddress, size: MemorySize) -> MemoryRange { MemoryRange { addr, size } } pub fn len(&self) -> usize { self.size.get() } pub fn is_empty(&self) -> bool { self.size.get() > 0 } pub fn as_ptr(&self) -> *const u8 { self.addr.get() as *const u8 } pub fn as_mut_ptr(&self) -> *mut u8 { self.addr.get() as *mut u8 } } /// Which memory region the operation should affect. #[derive(Debug, Copy, Clone, PartialEq)] pub enum MemoryType { /// The address where addresses go when no `virt` is specified. Default = 1, /// Addresses will begin here when `IncreaseHeap` is called. Heap = 2, /// When messages are passed to a process, they will go here. Messages = 3, /// Unlike other memory types, this defines the "end" of the region. Stack = 4, } impl From for MemoryType { fn from(arg: usize) -> Self { match arg { 2 => MemoryType::Heap, 3 => MemoryType::Messages, 4 => MemoryType::Stack, _ => MemoryType::Default, } } } #[repr(C)] #[derive(Debug, PartialEq)] pub enum Result { Ok, Error(Error), MemoryAddress(MemoryAddress), MemoryRange(MemoryRange), ReadyThreads( usize, /* count */ usize, /* pid0 */ usize, /* context0 */ usize, /* pid1 */ usize, /* context1 */ usize, /* pid2 */ usize, /* context2 */ ), ResumeProcess, ServerID(SID), ConnectionID(CID), NewServerID(SID, CID), Message(MessageEnvelope), ThreadID(TID), ProcessID(PID), /// The requested system call is unimplemented Unimplemented, /// The process is blocked and should perform the read() again. This is only /// ever seen in `Hosted` mode, because when running natively the kernel /// simply never schedules the process. BlockedProcess, /// A scalar with one value Scalar1(usize), /// A scalar with two values Scalar2(usize, usize), /// The syscall should be attempted again. This is returned when calling /// functions such as `try_connect()` and `try_send()` that may block. WouldBlock, /// The message was successful but no value was returned. None, UnknownResult(usize, usize, usize, usize, usize, usize, usize), } impl Result { pub fn to_args(&self) -> [usize; 8] { match self { Result::Ok => [0, 0, 0, 0, 0, 0, 0, 0], Result::Error(e) => [1, e.to_usize(), 0, 0, 0, 0, 0, 0], Result::MemoryAddress(s) => [2, s.get(), 0, 0, 0, 0, 0, 0], Result::MemoryRange(r) => [3, r.addr.get(), r.size.get(), 0, 0, 0, 0, 0], Result::ReadyThreads(count, pid0, ctx0, pid1, ctx1, pid2, ctx2) => { [4, *count, *pid0, *ctx0, *pid1, *ctx1, *pid2, *ctx2] } Result::ResumeProcess => [5, 0, 0, 0, 0, 0, 0, 0], Result::ServerID(sid) => { let s = sid.to_u32(); [6, s.0 as _, s.1 as _, s.2 as _, s.3 as _, 0, 0, 0] } Result::ConnectionID(cid) => [7, *cid, 0, 0, 0, 0, 0, 0], Result::Message(me) => { let me_enc = me.to_usize(); [ 8, me_enc[0], me_enc[1], me_enc[2], me_enc[3], me_enc[4], me_enc[5], me_enc[6], ] } Result::ThreadID(ctx) => [9, *ctx as usize, 0, 0, 0, 0, 0, 0], Result::ProcessID(pid) => [10, pid.get() as _, 0, 0, 0, 0, 0, 0], Result::Unimplemented => [11, 0, 0, 0, 0, 0, 0, 0], Result::BlockedProcess => [12, 0, 0, 0, 0, 0, 0, 0], Result::Scalar1(a) => [13, *a, 0, 0, 0, 0, 0, 0], Result::Scalar2(a, b) => [14, *a, *b, 0, 0, 0, 0, 0], Result::NewServerID(sid, cid) => { let s = sid.to_u32(); [15, s.0 as _, s.1 as _, s.2 as _, s.3 as _, *cid, 0, 0] } Result::WouldBlock => [16, 0, 0, 0, 0, 0, 0, 0], Result::None => [17, 0, 0, 0, 0, 0, 0, 0], Result::UnknownResult(arg1, arg2, arg3, arg4, arg5, arg6, arg7) => { [usize::MAX, *arg1, *arg2, *arg3, *arg4, *arg5, *arg6, *arg7] } } } pub fn from_args(src: [usize; 8]) -> Self { match src[0] { 0 => Result::Ok, 1 => Result::Error(Error::from_usize(src[1])), 2 => match MemoryAddress::new(src[1]) { None => Result::Error(Error::InternalError), Some(s) => Result::MemoryAddress(s), }, 3 => { let addr = match MemoryAddress::new(src[1]) { None => return Result::Error(Error::InternalError), Some(s) => s, }; let size = match MemorySize::new(src[2]) { None => return Result::Error(Error::InternalError), Some(s) => s, }; Result::MemoryRange(MemoryRange { addr, size }) } 4 => Result::ReadyThreads(src[1], src[2], src[3], src[4], src[5], src[6], src[7]), 5 => Result::ResumeProcess, 6 => Result::ServerID(SID::from_u32( src[1] as _, src[2] as _, src[3] as _, src[4] as _, )), 7 => Result::ConnectionID(src[1] as CID), 8 => { let sender = src[1]; let message = match src[2] { 0 => match MemoryMessage::from_usize(src[3], src[4], src[5], src[6], src[7]) { None => return Result::Error(Error::InternalError), Some(s) => Message::MutableBorrow(s), }, 1 => match MemoryMessage::from_usize(src[3], src[4], src[5], src[6], src[7]) { None => return Result::Error(Error::InternalError), Some(s) => Message::Borrow(s), }, 2 => match MemoryMessage::from_usize(src[3], src[4], src[5], src[6], src[7]) { None => return Result::Error(Error::InternalError), Some(s) => Message::Move(s), }, 3 => Message::Scalar(ScalarMessage::from_usize( src[3], src[4], src[5], src[6], src[7], )), 4 => Message::BlockingScalar(ScalarMessage::from_usize( src[3], src[4], src[5], src[6], src[7], )), _ => return Result::Error(Error::InternalError), }; Result::Message(MessageEnvelope { sender: MessageSender::from_usize(sender), body: message, }) } 9 => Result::ThreadID(src[1] as TID), 10 => Result::ProcessID(PID::new(src[1] as _).unwrap()), 11 => Result::Unimplemented, 12 => Result::BlockedProcess, 13 => Result::Scalar1(src[1]), 14 => Result::Scalar2(src[1], src[2]), 15 => Result::NewServerID( SID::from_u32(src[1] as _, src[2] as _, src[3] as _, src[4] as _), src[5] as _, ), 16 => Result::WouldBlock, 17 => Result::None, _ => Result::UnknownResult(src[0], src[1], src[2], src[3], src[4], src[5], src[6]), } } /// If the Result has memory attached to it, return the memory pub fn memory(&self) -> Option { match self { Result::Message(msg) => match &msg.body { Message::Move(memory_message) | Message::Borrow(memory_message) | Message::MutableBorrow(memory_message) => Some(memory_message.buf), _ => None, }, _ => None, } } } impl From for Result { fn from(e: Error) -> Self { Result::Error(e) } } pub type SysCallRequest = core::result::Result; pub type SysCallResult = core::result::Result;