flatipc/
lib.rs

1//! Xous supports sending Messages from Clients to Servers. If a message is
2//! a `MemoryMessage`, then the Server may respond by updating the buffer
3//! with a response message and returning the buffer.
4//!
5//! A number of serialization options are available, ranging from sending
6//! raw arrays of bytes all the way to sending full Protobuf messages.
7//!
8//! This crate takes a middle road and allows for sending rich Rust types
9//! such as full enums and structs without doing extensive checks. This is
10//! based on the theory that in normal operating systems, ABIs do not contain
11//! any sort of verification, and it is undefined behaviour to send a malformed
12//! request to a loaded module.
13//!
14//! An object can be made into an IPC object by implementing the `Ipc` trait.
15//! The primary method of doing this is by adding `#[derive(flatipc::Ipc)]`
16//! to the definition. Such an object may be included in both the Client and
17//! the Server so that they share the same view of the object.
18//!
19//! Any object may be made into an IPC object provided it follows the following
20//! requirements:
21//!
22//! - **The object is `#[repr(C)]`** - This is required to ensure the objecty
23//!   has a well-defined layout in memory. Other representations may shift
24//!   depending on optimizations.
25//! - **The object only contains fields that are `IpcSafe`** - This trait is
26//!   implemented on primitive types that are able to be sent across an IPC
27//!   boundary. This includes all integers, floats, and booleans. It also
28//!   includes arrays of `IpcSafe` types, `Option<T>` where `T` is `IpcSafe`,
29//!   and `Result<T, E>` where `T` and `E` are `IpcSafe`. Pointers and
30//!   references are not `IpcSafe` and may not be used.
31//!
32//! When deriving `Ipc`, a new type will be created with the same name as
33//! the original type prefixed with `Ipc`. For example, if you derive `Ipc`
34//! on a type named `Foo`, the new type will be named `IpcFoo`.
35//!
36//! Objects that implement `Ipc` must be page-aligned and must be a full multiple
37//! of a page size in length. This is to ensure that the object can be mapped
38//! transparently between the Client and the Server without dragging any extra
39//! memory along with it.
40//!
41//! `Ipc` objects implement `Deref` and `DerefMut` to the original object, so
42//! they can be used in place of the original object in most cases by derefencing
43//! the `Ipc` object.
44//!
45//! Because `String` and `Vec` contain pointers, they are not `IpcSafe`. As such,
46//! replacements are made available in this crate that are `IpcSafe`.
47//!
48//! <br>
49//!
50//! # Example of a Derived Ipc Object
51//!
52//! ```ignore
53//! #[repr(C)]
54//! #[derive(flatipc::Ipc)]
55//! pub struct Foo {
56//!   a: u32,
57//!   b: u64,
58//! }
59//!
60//! // Connect to an example server
61//! let conn = xous::connect(xous::SID::from_bytes(b"example---server").unwrap())?;
62//!
63//! // Construct our object
64//! let foo = Foo { a: 1, b: 1234567890 };
65//!
66//! // Create an IPC representation of the original object, consuming
67//! // the original object in the process.
68//! let mut foo_ipc = foo.into_ipc();
69//!
70//! // Lend the object to the server with opcode 42.
71//! foo_ipc.lend(conn, 42)?;
72//!
73//! // Note that we can still access the original object.
74//! println!("a: {}", foo_ipc.a);
75//!
76//! // When we're done with the IPC object, we can get the original object back.
77//! let foo = foo_ipc.into_original();
78//! ```
79//!
80//! # Example of Server Usage
81//!
82//! ```ignore
83//! // Ideally this comes from a common `API` file that both the
84//! // client and server share.
85//! #[repr(C)]
86//! #[derive(flatipc::Ipc)]
87//! pub struct Foo {
88//!   a: u32,
89//!   b: u64,
90//! }
91//!
92//! let mut msg_opt = None;
93//! let mut server = xous::create_server_with_sid(b"example---server").unwrap();
94//! loop {
95//!     let envelope = xous::reply_and_receive_next(server, &mut msg_opt).unwrap();
96//!     let Some(msg) = msg_opt else { continue };
97//!
98//!     // Take the memory portion of the message, continuing if it's not a memory message.
99//!     let Some(msg_memory) = msg.memory_message() else { continue };
100//!
101//!     // Turn the `MemoryMessage` into an `IpcFoo` object. Note that this is the `Ipc`-prefixed
102//!     // version of the original object. If the object is not the correct type, `None` will be
103//!     // returned and the message will be returned to the sender in ghe next loop.
104//!     let Some(foo) = IpcFoo::from_memory_message(msg_slice, signature) else { continue };
105//!
106//!     // Do something with the object.
107//! }
108//! ```
109
110/// An object is Sendable if it is guaranteed to be flat and contains no pointers.
111/// This trait can be placed on objects that have invalid representations such as
112/// bools (which can only be 0 or 1) but it is up to the implementer to ensure that
113/// the correct object arrives on the other side.
114pub unsafe trait IpcSafe {}
115
116// Enable calling this crate as `flatipc` in tests.
117extern crate self as flatipc;
118
119// Allow doing `#[derive(flatipc::Ipc)]` instead of `#[derive(flatipc_derive::Ipc)]`
120pub use flatipc_derive::{Ipc, IpcSafe};
121#[cfg(feature = "xous")]
122mod backend {
123    pub use xous::Error;
124    pub use xous::CID;
125}
126
127#[cfg(not(feature = "xous"))]
128mod backend {
129    pub mod mock;
130    pub use mock::CID;
131
132    #[derive(Debug)]
133    pub enum Error {
134        Unimplemented,
135    }
136}
137
138pub use backend::{Error, CID};
139
140pub mod string;
141pub use string::String;
142
143pub mod vec;
144pub use vec::Vec;
145
146unsafe impl IpcSafe for i8 {}
147unsafe impl IpcSafe for i16 {}
148unsafe impl IpcSafe for i32 {}
149unsafe impl IpcSafe for i64 {}
150unsafe impl IpcSafe for i128 {}
151unsafe impl IpcSafe for u8 {}
152unsafe impl IpcSafe for u16 {}
153unsafe impl IpcSafe for u32 {}
154unsafe impl IpcSafe for u64 {}
155unsafe impl IpcSafe for u128 {}
156unsafe impl IpcSafe for f32 {}
157unsafe impl IpcSafe for f64 {}
158unsafe impl IpcSafe for bool {}
159unsafe impl IpcSafe for usize {}
160unsafe impl IpcSafe for isize {}
161unsafe impl IpcSafe for char {}
162unsafe impl<T, const N: usize> IpcSafe for [T; N] where T: IpcSafe {}
163unsafe impl<T> IpcSafe for Option<T> where T: IpcSafe {}
164unsafe impl<T, E> IpcSafe for Result<T, E>
165where
166    T: IpcSafe,
167    E: IpcSafe,
168{
169}
170
171/// An object that can be sent across an IPC boundary, and can be reconstituted
172/// on the other side without copying. An object with this trait must be page-aligned,
173/// must be a multiple of the page size in length, and must not contain any pointers.
174pub unsafe trait Ipc {
175    /// What this memory message is a representation of. This is used to turn
176    /// this object back into the original object.
177    type Original;
178
179    /// Create an Ipc variant from the original object. Succeeds only if
180    /// the signature passed in matches the signature of `Original`.
181    fn from_slice<'a>(data: &'a [u8], signature: usize) -> Option<&'a Self>;
182
183    /// Unconditionally create a new memory message from the original object.
184    /// It is up to the caller to that `data` contains a valid representation of `Self`.
185    unsafe fn from_buffer_unchecked<'a>(data: &'a [u8]) -> &'a Self;
186
187    /// Create a mutable IPC variant from the original object. Succeeds only if
188    /// the signature passed in matches the signature of `Original`.
189    fn from_slice_mut<'a>(data: &'a mut [u8], signature: usize) -> Option<&'a mut Self>;
190
191    /// Unconditionally create a new mutable memory message from the original object.
192    /// It is up to the caller to that `data` contains a valid representation of `Self`.
193    unsafe fn from_buffer_mut_unchecked<'a>(data: &'a mut [u8]) -> &'a mut Self;
194
195    /// Return a reference to the original object while keeping the
196    /// memory version alive.
197    fn as_original(&self) -> &Self::Original;
198
199    /// Return a reference to the original object while keeping the
200    /// memory version alive.
201    fn as_original_mut(&mut self) -> &mut Self::Original;
202
203    /// Consume the memory version and return the original object.
204    fn into_original(self) -> Self::Original;
205
206    /// Lend the buffer to the specified server. The connection should already be
207    /// open and the server should be ready to receive the buffer.
208    fn lend(&self, connection: CID, opcode: usize) -> Result<(), backend::Error>;
209
210    /// Try to lend the buffer to the specified server, returning an error
211    /// if the lend failed.
212    fn try_lend(&self, connection: CID, opcode: usize) -> Result<(), backend::Error>;
213
214    /// Lend the buffer to the specified server, and allow the server to
215    /// modify the buffer.
216    fn lend_mut(&mut self, connection: CID, opcode: usize) -> Result<(), backend::Error>;
217
218    /// Lend the buffer to the specified server, and allow the server to
219    /// modify the buffer. Return an error if the lend failed.
220    fn try_lend_mut(&mut self, connection: CID, opcode: usize) -> Result<(), backend::Error>;
221
222    /// Return the signature of this memory message. Useful for verifying
223    /// that the correct message is being received.
224    fn signature(&self) -> usize;
225
226    #[cfg(feature = "xous")]
227    /// Build an `Ipc` object from a `xous::MemoryMessage`. Verifies the signature and
228    /// returns `None` if there is no match.
229    fn from_memory_message<'a>(msg: &'a xous::MemoryMessage) -> Option<&'a Self>;
230
231    #[cfg(feature = "xous")]
232    /// Build a mutable `Ipc` object from a mutable `xous::MemoryMessage`. Verifies the
233    /// signature and returns `None` if there is no match. The returned object has a
234    /// lifetime that's tied to the `MemoryMessage`.
235    fn from_memory_message_mut<'a>(msg: &'a mut xous::MemoryMessage) -> Option<&'a mut Self>;
236}
237
238/// Objects that have `IntoIpc` may be turned into an object that can be passed
239/// across an IPC barrier. This consumes the object and returns a new object that
240/// may be dereferenced to the original object.
241pub trait IntoIpc {
242    type IpcType;
243    fn into_ipc(self) -> Self::IpcType;
244}
245
246#[cfg(test)]
247mod test;