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;