From 7bbcd951feb9e99f1734a313639a42f708ddaf74 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Fri, 26 Mar 2021 20:22:06 +0800 Subject: [PATCH] xous-ipc: add an initial implementation This adds a first draft of `xous-ipc`, which specializes the IPC features of Xous. Signed-off-by: Sean Cross --- xous-ipc/Cargo.toml | 13 ++ xous-ipc/src/buffer.rs | 226 ++++++++++++++++++++++++++++++ xous-ipc/src/lib.rs | 8 ++ xous-ipc/src/string.rs | 306 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 553 insertions(+) create mode 100644 xous-ipc/Cargo.toml create mode 100644 xous-ipc/src/buffer.rs create mode 100644 xous-ipc/src/lib.rs create mode 100644 xous-ipc/src/string.rs diff --git a/xous-ipc/Cargo.toml b/xous-ipc/Cargo.toml new file mode 100644 index 000000000..43949bec9 --- /dev/null +++ b/xous-ipc/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["Sean Cross "] +description = "System call interface for Xous" +edition = "2018" +license = "MIT OR Apache-2.0" +name = "xous-ipc" +version = "0.4.0" + +[dependencies] +bitflags = {version = "1"} +# log = {version = "0.4"} +rkyv = {version = "0.4.3", features = ["const_generics"], default-features = false} +xous = {path = "../xous-rs"} diff --git a/xous-ipc/src/buffer.rs b/xous-ipc/src/buffer.rs new file mode 100644 index 000000000..8b5af32ee --- /dev/null +++ b/xous-ipc/src/buffer.rs @@ -0,0 +1,226 @@ +use rkyv::{ser::Serializer, Fallible}; +use xous::{ + map_memory, send_message, unmap_memory, Error, MemoryAddress, MemoryFlags, MemoryMessage, + MemoryRange, MemorySize, Message, Result, CID, +}; + +#[derive(Debug)] +pub struct Buffer<'a> { + range: MemoryRange, + valid: MemoryRange, + offset: Option, + slice: &'a mut [u8], + should_drop: bool, + memory_message: Option<&'a mut MemoryMessage>, +} + +pub struct XousDeserializer; + +// Unreachable enum pattern, swap out for the never type (!) whenever that gets stabilized +#[derive(Debug)] +pub enum XousUnreachable {} + +impl rkyv::Fallible for XousDeserializer { + type Error = XousUnreachable; +} + +impl<'a> Buffer<'a> { + #[allow(dead_code)] + pub fn new(len: usize) -> Self { + let remainder = if ((len & 0xFFF) == 0) && (len > 0) { + 0 + } else { + 0x1000 - (len & 0xFFF) + }; + + let flags = MemoryFlags::R | MemoryFlags::W; + + // Allocate enough memory to hold the requested data + let new_mem = map_memory( + None, + None, + // Ensure our byte size is a multiple of 4096 + len + remainder, + flags, + ) + .expect("Buffer: error in new()/map_memory"); + + let mut valid = new_mem; + valid.size = MemorySize::new(len + remainder).unwrap(); + Buffer { + range: new_mem, + slice: unsafe { + core::slice::from_raw_parts_mut(new_mem.as_mut_ptr(), len + remainder) + }, + valid, + offset: None, + should_drop: true, + memory_message: None, + } + } + + #[allow(dead_code)] + pub unsafe fn from_memory_message(mem: &'a MemoryMessage) -> Self { + Buffer { + range: mem.buf, + slice: core::slice::from_raw_parts_mut(mem.buf.as_mut_ptr(), mem.buf.len()), + valid: mem.buf, + offset: mem.offset, + should_drop: false, + memory_message: None, + } + } + + #[allow(dead_code)] + pub unsafe fn from_memory_message_mut(mem: &'a mut MemoryMessage) -> Self { + Buffer { + range: mem.buf, + slice: core::slice::from_raw_parts_mut(mem.buf.as_mut_ptr(), mem.buf.len()), + valid: mem.buf, + offset: mem.offset, + should_drop: false, + memory_message: Some(mem), + } + } + + /// Perform a mutable lend of this Buffer to the server. + #[allow(dead_code)] + pub fn lend_mut(&mut self, connection: CID, id: u32) -> core::result::Result { + let msg = MemoryMessage { + id: id as usize, + buf: self.valid, + offset: self.offset, + valid: MemorySize::new(self.slice.len()), + }; + + // Update the offset pointer if the server modified it. + let result = send_message(connection, Message::MutableBorrow(msg)); + if let Ok(Result::MemoryReturned(offset, _valid)) = result { + self.offset = offset; + } + + result + } + + #[allow(dead_code)] + pub fn lend(&self, connection: CID, id: u32) -> core::result::Result { + let msg = MemoryMessage { + id: id as usize, + buf: self.valid, + offset: self.offset, + valid: MemorySize::new(self.slice.len()), + }; + send_message(connection, Message::Borrow(msg)) + } + + #[allow(dead_code)] + pub fn send(mut self, connection: CID, id: u32) -> core::result::Result { + let msg = MemoryMessage { + id: id as usize, + buf: self.valid, + offset: self.offset, + valid: MemorySize::new(self.slice.len()), + }; + let result = send_message(connection, Message::Move(msg))?; + + // prevents it from being Dropped. + self.should_drop = false; + Ok(result) + } + + #[allow(dead_code)] + pub fn try_from(src: S) -> core::result::Result + where + S: rkyv::Serialize>>, + { + let buf = Self::new(4096); + let mut ser = rkyv::ser::serializers::BufferSerializer::new(buf); + let pos = ser.serialize_value(&src).or(Err(()))?; + let mut buf = ser.into_inner(); + buf.offset = MemoryAddress::new(pos); + Ok(buf) + } + + #[allow(dead_code)] + pub fn serialize_from(&mut self, src: S) -> core::result::Result<(), &'static str> + where + S: rkyv::Serialize>, + { + // We must have a `memory_message` to update in order for this to work. + // Otherwise, we risk having the pointer go to somewhere invalid. + if self.memory_message.is_none() { + // Create this message using `from_memory_message_mut()` instead of + // `from_memory_message()`. + Err("couldn't serialize because buffer wasn't mutable")?; + } + // Unsafe Warning: Create a copy of the backing slice to hand to the deserializer. + // This is required because the deserializer consumes the buffer and returns it + // later as part of `.into_inner()`. + // The "correct" way to do this would be to implement `rkyv::Serializer` an `rkyv::Fallible` + // for ourselves. + let copied_slice = + unsafe { core::slice::from_raw_parts_mut(self.slice.as_mut_ptr(), self.slice.len()) }; + let mut ser = rkyv::ser::serializers::BufferSerializer::new(copied_slice); + let pos = ser.serialize_value(&src).or(Err("couldn't serialize"))?; + self.offset = MemoryAddress::new(pos); + if let Some(ref mut msg) = self.memory_message.as_mut() { + msg.offset = MemoryAddress::new(pos); + } + Ok(()) + } + + #[allow(dead_code)] + pub fn try_into(&self) -> core::result::Result<&U, ()> + where + T: rkyv::Archive, + { + let pos = self.offset.map(|o| o.get()).unwrap_or_default(); + let r = unsafe { rkyv::archived_value::(self.slice, pos) }; + Ok(r) + } + + #[allow(dead_code)] + pub fn deserialize(&self) -> core::result::Result + where + T: rkyv::Archive, + U: rkyv::Deserialize>, + { + let pos = self.offset.map(|o| o.get()).unwrap_or_default(); + let r = unsafe { rkyv::archived_value::(self.slice, pos) }; + Ok(r.deserialize(&mut XousDeserializer {}).unwrap()) + } +} + +impl<'a> core::convert::AsRef<[u8]> for Buffer<'a> { + fn as_ref(&self) -> &[u8] { + self.slice + } +} + +impl<'a> core::convert::AsMut<[u8]> for Buffer<'a> { + fn as_mut(&mut self) -> &mut [u8] { + self.slice + } +} + +impl<'a> core::ops::Deref for Buffer<'a> { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &*self.slice + } +} + +impl<'a> core::ops::DerefMut for Buffer<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut *self.slice + } +} + +impl<'a> Drop for Buffer<'a> { + fn drop(&mut self) { + if self.should_drop { + unmap_memory(self.range).expect("Buffer: failed to drop memory"); + } + } +} diff --git a/xous-ipc/src/lib.rs b/xous-ipc/src/lib.rs new file mode 100644 index 000000000..f3312fbf6 --- /dev/null +++ b/xous-ipc/src/lib.rs @@ -0,0 +1,8 @@ +#![cfg_attr(target_os = "none", no_std)] + +mod buffer; +pub use buffer::*; +pub use buffer::XousDeserializer; + +mod string; +pub use string::*; diff --git a/xous-ipc/src/string.rs b/xous-ipc/src/string.rs new file mode 100644 index 000000000..d9050f9c9 --- /dev/null +++ b/xous-ipc/src/string.rs @@ -0,0 +1,306 @@ +use xous::{Error, MemoryMessage, Result, CID}; + +use core::pin::Pin; +use rkyv::ser::Serializer; +use rkyv::archived_value; +use rkyv::ArchiveUnsized; +use rkyv::SerializeUnsized; + +#[derive(Copy, Clone)] +pub struct String { + bytes: [u8; N], + len: u32, // length in bytes, not characters +} + +impl String { + pub fn new() -> String { + String { + bytes: [0; N], + len: 0, + } + } + + pub fn from_str(src: T) -> String where T: AsRef { + let src = src.as_ref(); + let mut s = Self::new(); + // Copy the string into our backing store. + for (&src_byte, dest_byte) in src.as_bytes().iter().zip(&mut s.bytes) { + *dest_byte = src_byte; + } + // Set the string length to the length of the passed-in String, + // or the maximum possible length. Which ever is smaller. + s.len = s.bytes.len().min(src.as_bytes().len()) as u32; + + // If the string is not valid, set its length to 0. + if s.as_str().is_err() { + s.len = 0; + } + + s + } + + pub fn as_bytes(&self) -> [u8; N] { + self.bytes + } + + pub fn as_str(&self) -> core::result::Result<&str, core::str::Utf8Error> { + core::str::from_utf8(&self.bytes[0..self.len as usize]) + } + + pub fn len(&self) -> usize { + self.len as usize + } + + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + /// Convert a `MemoryMessage` into a `String` + pub fn from_message( + message: &mut MemoryMessage, + ) -> core::result::Result, core::str::Utf8Error> { + let buf = unsafe { crate::Buffer::from_memory_message(message) }; + let bytes = Pin::new(buf.as_ref()); + let value = unsafe { archived_value::>(&bytes, message.id as usize) }; + let s = String::::from_str(value.as_str()); + Ok(s) + } + + /// Perform an immutable lend of this String to the specified server. + /// This function will block until the server returns. + /// Note that this convenience should only be used if the server only ever + /// expects to deal with one type of String, ever. Otherwise, this should be + /// implemented in the API and wrapped in an Enum to help decorate the functional + /// target of the string. An example of a server that uses this convencience function + /// is the logger. + pub fn lend( + &self, + connection: CID, + // id: crate::MessageId, + ) -> core::result::Result { + let mut writer = rkyv::ser::serializers::BufferSerializer::new(crate::Buffer::new(N)); + let pos = writer + .serialize_value(self) + .expect("xous::String -- couldn't archive self"); + let xous_buffer = writer.into_inner(); + + // note that "id" is actually used as the position into the rkyv buffer + xous_buffer.lend(connection, pos as u32) + } + + /// Move this string from the client into the server. + pub fn send( + self, + connection: CID, + // id: crate::MessageId, + ) -> core::result::Result { + let mut writer = rkyv::ser::serializers::BufferSerializer::new(crate::Buffer::new(N)); + let pos = writer + .serialize_value(&self) + .expect("xous::String -- couldn't archive self"); + let xous_buffer = writer.into_inner(); + + xous_buffer.send(connection, pos as u32) + } + + /// Clear the contents of this String and set the length to 0 + pub fn clear(&mut self) { + self.len = 0; + self.bytes = [0; N]; + } + + pub fn to_str(&self) -> &str { + unsafe { core::str::from_utf8_unchecked(&self.bytes[0..self.len()]) } + } + + /// awful, O(N) implementation because we have to iterate through the entire string + /// and decode variable-length utf8 characters, until we can't. + pub fn pop(&mut self) -> Option { + if self.len() < 1 { + return None; + } + // first, make a copy of the string + let tempbytes: [u8; N] = self.bytes; + let tempstr = unsafe { core::str::from_utf8_unchecked(&tempbytes[0..self.len()]) }.clone(); + // clear our own string + self.len = 0; + self.bytes = [0; N]; + + // now copy over character by character, until just before the last character + let mut char_iter = tempstr.chars(); + let mut maybe_c = char_iter.next(); + loop { + match maybe_c { + Some(c) => { + let next_c = char_iter.next(); + match next_c { + Some(_thing) => { + self.push(c).unwrap(); // always succeeds because we're re-encoding our string + maybe_c = next_c; + } + None => return next_c, + } + } + None => { + return None; // we should actually never get here because len() == 0 case already covered + } + } + } + } + + pub fn push(&mut self, ch: char) -> core::result::Result { + match ch.len_utf8() { + 1 => { + if self.len() < self.bytes.len() { + self.bytes[self.len()] = ch as u8; + self.len += 1; + Ok(1) + } else { + Err(Error::OutOfMemory) + } + } + _ => { + let mut bytes: usize = 0; + let mut data: [u8; 4] = [0; 4]; + let subslice = ch.encode_utf8(&mut data); + if self.len() + subslice.len() < self.bytes.len() { + for c in subslice.bytes() { + self.bytes[self.len()] = c; + self.len += 1; + bytes += 1; + } + Ok(bytes) + } else { + Err(Error::OutOfMemory) + } + } + } + } + + pub fn append(&mut self, s: &str) -> core::result::Result { + let mut bytes_added = 0; + for ch in s.chars() { + if let Ok(bytes) = self.push(ch) { + bytes_added += bytes; + } else { + return Err(Error::OutOfMemory); + } + } + Ok(bytes_added) + } + + pub fn push_byte(&mut self, b: u8) -> core::result::Result<(), Error> { + if self.len() < self.bytes.len() { + self.bytes[self.len()] = b; + self.len += 1; + Ok(()) + } else { + Err(Error::OutOfMemory) + } + } +} + +impl core::fmt::Display for String { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", self.to_str()) + } +} + +impl core::fmt::Write for String { + fn write_str(&mut self, s: &str) -> core::result::Result<(), core::fmt::Error> { + for c in s.bytes() { + if self.len() < self.bytes.len() { + self.bytes[self.len()] = c; + self.len += 1; + } + } + Ok(()) + } +} + +impl core::fmt::Debug for String { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", self.to_str()) + } +} + +impl core::convert::AsRef for String { + fn as_ref(&self) -> &str { + self.to_str() + } +} + +impl PartialEq for String { + fn eq(&self, other: &Self) -> bool { + self.bytes[..self.len as usize] == other.bytes[..other.len as usize] + && self.len == other.len + } +} + +impl Eq for String {} + +#[repr(C)] +pub struct ArchivedString { + ptr: rkyv::RelPtr, +} + +impl ArchivedString { + // Provide a `str` view of an `ArchivedString`. + pub fn as_str(&self) -> &str { + unsafe { + // The as_ptr() function of RelPtr will get a pointer + // to its memory. + &*self.ptr.as_ptr() + } + } +} + +pub struct StringResolver { + bytes_pos: usize, + _metadata_resolver: rkyv::MetadataResolver, +} + +/// Turn a `String` into an archived object +impl rkyv::Archive for String { + type Archived = ArchivedString; + type Resolver = StringResolver; + + fn resolve(&self, pos: usize, resolver: Self::Resolver) -> Self::Archived { + Self::Archived { + ptr: unsafe { + self.as_str().unwrap().resolve_unsized( + pos + rkyv::offset_of!(Self::Archived, ptr), + resolver.bytes_pos, + (), + ) + }, + } + } +} + +// Turn a stream of bytes into an `ArchivedString`. +impl rkyv::Serialize for String { + fn serialize(&self, serializer: &mut S) -> core::result::Result { + // This is where we want to write the bytes of our string and return + // a resolver that knows where those bytes were written. + // We also need to serialize the metadata for our str. + Ok(StringResolver { + bytes_pos: self.as_str().unwrap().serialize_unsized(serializer)?, + _metadata_resolver: self.as_str().unwrap().serialize_metadata(serializer)?, + }) + } +} +// Turn an `ArchivedString` back into a String +use rkyv::Fallible; +impl rkyv::Deserialize, D> for ArchivedString { + fn deserialize(&self, _deserializer: &mut D) -> core::result::Result, D::Error> { + Ok(String::::from_str(self.as_str())) + } +} + +// a "no-op" deserializer for in-place data, required for rkyv 0.4.1 +// perhaps in rkyv 0.5 this will go away, see https://github.com/djkoloski/rkyv/issues/67 +pub struct XousDeserializer; +impl rkyv::Fallible for XousDeserializer { + type Error = xous::Error; +}