diff mbox series

[RFC,XEN] docs/designs: Add a design document for 'xen' Rust crate

Message ID c0f88345e1ee870dc10a209e3840ae98b2ac1386.1732283549.git.teddy.astie@vates.tech (mailing list archive)
State New
Headers show
Series [RFC,XEN] docs/designs: Add a design document for 'xen' Rust crate | expand

Commit Message

Teddy Astie Nov. 22, 2024, 2:01 p.m. UTC
Add a design document for the 'xen' rust crate.

Signed-off-by: Teddy Astie <teddy.astie@vates.tech>
---
Follows Alejandro works with https://lore.kernel.org/xen-devel/D5SQEZIL2SZV.QR3X5MRVQJJP@cloud.com/T/#t
There is also a WIP branch at https://gitlab.com/TSnake41/xen/-/tree/rust-rfc
---
 docs/designs/xen-rust-crate.md | 199 +++++++++++++++++++++++++++++++++
 1 file changed, 199 insertions(+)
 create mode 100644 docs/designs/xen-rust-crate.md
diff mbox series

Patch

diff --git a/docs/designs/xen-rust-crate.md b/docs/designs/xen-rust-crate.md
new file mode 100644
index 0000000000..5eab603d4d
--- /dev/null
+++ b/docs/designs/xen-rust-crate.md
@@ -0,0 +1,199 @@ 
+# Rust 'xen' crate interface design
+
+See [1] for more context.
+
+This RFC proposes parts of the 'xen' crate interface that would directly or indirectly
+(through internal wrappers) be used by users.
+Those users could be a userland toolstack, a unikernel application (e.g XTF, Unikraft),
+some other freestanding environment (e.g OVMF, Linux, Redox OS), ...
+
+These users can have a very different execution environment, this crate aims to provide
+a uniform interface while allowing flexibility for exposing platform-specific bits.
+
+## Design philosophy
+
+This crate should feel natural for a Rust developper, thus, any Rust developper with some
+understanding on the Xen hypercall operations should be able to use this crate.
+Moreover, we want this crate to be maintainable and feel "idiomatic", and not introduce
+confusing behavior onto the user. Note that this crate will heavily use unsafe code.
+
+Some principles are proposed :
+
+Use or provide idiomatic abstractions when relevant (reuse standard traits).
+
+Examples:
+  Provide (optional) Future<...> abstractions for event channels
+  Provide a iterator-based (Stream ? [2]) abstraction for guest console.
+
+Don't restrict features to some execution environment, use modular abstractions (e.g traits)
+to allow the user to specify the missing bits himself / provide its own implementation.
+Note that it doesn't prevent us from exposing the platform-specific bits onto the
+types themselves (e.g UnixXenEventChannel can still expose its file descriptor).
+
+Example:
+  If we provide a event channel abstraction based on hypercall but it doesn't implement Future<...>,
+  the user can still implement its own type on top of the hypercall implementation, and
+  use its own async runtime (e.g based on interrupts) to implement Future<...> himself.
+  There could be 2 traits for varying needs :
+    EventChannel (base trait) and AsyncEventChannel (await-able EventChannel)
+
+  We can have both RawEventChannel based on XenHypercall that only implements EventChannel
+  and another type TokioEventChannel that provides both EventChannel and AsyncEventChannel
+  and integrates with tokio runtime.
+
+Safe wrappers must be "sound" and unsafe code shall not cause undefined behavior.
+- safe wrappers must not cause undefined behavior on their own
+- unsafe code should not cause undefined behavior if properly used
+
+This is a bit tricky due to some Xen specificities, but we expect hypercall to be well
+formed (we can add validation tools for that) including have its parameter indirectly
+respect the aliasing rules [3].
+Although, we assume that Xen is well-behaving regarding its ABI.
+We don't define misuse of a hypercall that can harm the guest himself, but we care
+about not causing a undefined behavior (e.g by passing a buggy pointer) through the
+hypercall interface that can overwrite unrelated/arbitrary kernel memory.
+
+## Hypercall interface
+
+We introduce a XenHypercall trait that exposes a raw primitive for making hypercalls.
+This interface supposes nothing on the ABI used in Xen, and its the responsibility
+of the user of such interface (often safe wrappers) that the hypercall made is
+meaningful.
+
+This interface is mostly to only be used by the crate developpers to build safe
+wrappers on top, or by advanced user for using non-exposed/WIP hypercall interfaces
+or bypassing the safe wrappers.
+
+We can implement this interface for freestanding platforms using direct native hypercalls
+(e.g using inline assembly) for freestanding platforms or in userland using special devices
+like privcmd/xencall on most Unix platforms.
+
+```rust
+pub trait XenHypercall: Sized {
+    unsafe fn hypercall5(&self, cmd: usize, param: [usize; 5]) -> usize;
+
+    unsafe fn hypercall4(&self, cmd: usize, param: [usize; 4]) -> usize;
+    unsafe fn hypercall3(&self, cmd: usize, param: [usize; 3]) -> usize;
+    unsafe fn hypercall2(&self, cmd: usize, param: [usize; 2]) -> usize;
+    unsafe fn hypercall1(&self, cmd: usize, param: usize) -> usize;
+    unsafe fn hypercall0(&self, cmd: usize) -> usize;
+
+    /* ... */
+}
+```
+
+### Hypercall-safe buffers
+
+One difficulty is that in a freestanding environment, we need to use pointers to
+original data. But in a hosted environment, we need to make special buffers instead
+for that.
+
+We introduce the Xen{Const/Mut}Buffer generic trait that wraps a reference in a
+"hypercall-safe" buffer that may or may not be a bounce buffer.
+
+```rust
+/// Wrapper of a reference into a hypercall-safe buffer.
+pub trait XenConstBuffer<T> {
+    /// Get a hypercall-safe reference to underlying data.
+    fn as_hypercall_ptr(&self) -> *const T;
+}
+
+/// Wrapper of a mutable reference into a mutable hypercall-safe buffer.
+pub trait XenMutBuffer<T> {
+    /// Get a hypercall-safe mutable reference to underlying data.
+    fn as_hypercall_ptr(&mut self) -> *mut T;
+
+    /// Update original reference with new data.
+    unsafe fn update(&mut self);
+}
+
+// The user will make those wrappers using dedicated functions in XenHypercall trait.
+
+trait XenHypercall: Sized {
+    /* ... */
+    type Error;
+
+    fn make_const_object<T: Copy + ?Sized>(
+        &self,
+        buffer: &T,
+    ) -> Result<impl XenConstBuffer<T>, Self::Error>;
+
+    fn make_mut_buffer<T: Copy + ?Sized>(
+        &self,
+        buffer: &mut T,
+    ) -> Result<impl XenMutBuffer<T>, Self::Error>;
+
+    fn make_const_slice<T: Copy + Sized>(
+        &self,
+        slice: &[T],
+    ) -> Result<impl XenConstBuffer<T>, Self::Error>;
+
+    fn make_mut_slice<T: Copy + Sized>(
+        &self,
+        slice: &mut [T],
+    ) -> Result<impl XenMutBuffer<T>, Self::Error>;
+}
+```
+
+Example use:
+
+```rust
+fn demo_hypercall<H: XenHypercall>(interface: &H, buffer: &mut [u8]) -> Result<(), H::Error> {
+    let buffer_size = buffer.len();
+    // make a hypercall-safe wrapper of `buffer`
+    let hyp_buffer = interface.make_mut_slice(buffer)?;
+
+    let op = SomeHypercallStruct {
+        buffer: hyp_buffer.as_hypercall_ptr(),
+        buffer_size: buffer_size as _,
+    };
+    // Do the same for hyp_op
+    let hyp_op = interface.make_const_object(&op)?;
+
+    unsafe {
+        interface.hypercall1(SOME_CMD, hyp_op.as_hypercall_ptr().addr());
+        // assume success
+        hyp_buffer.update(); // update buffer back
+    }
+
+    Ok(())
+}
+```
+
+Note that freestanding case, we can use a thin zero-copy wrapper :
+```rust
+/// Constant xen buffer that passes the reference as-is.
+pub(super) struct DirectConstXenBuffer<'a, T>(&'a T);
+
+impl<T> XenConstBuffer<T> for DirectConstXenBuffer<'_, T> {
+    fn as_hypercall_ptr(&self) -> *const T {
+        self.0
+    }
+}
+// ...
+```
+
+TODO:
+Do we need to clarify the lifetimes (e.g should trait indicate a lifetime binding with
+original data) ? Try to answer with RPITIT and Rust 2024 capture rules [4].
+
+Try to unify make_const_object and make_const_slice (along with mut variant). `*const [T]`
+is a bit more subtle to create and we cannot trivially cast a address into a pointer and
+need to use special functions for that (`core::ptr::slice_from_raw_parts` ?).
+But for that, we need to know that T is actually a slice before using this function.
+
+## Event channels
+
+TODO
+
+[1] - Interfacing Rust with Xen - Alejandro Vallejo, XenServer BU, Cloud Software Group
+https://youtu.be/iFh4n2kbAwM
+
+[2] - The Stream Trait
+https://rust-lang.github.io/async-book/05_streams/01_chapter.html
+
+[3] - Aliasing
+https://doc.rust-lang.org/nomicon/aliasing.html
+
+[4] - https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-traits.html
+https://rust-lang.github.io/rfcs/3498-lifetime-capture-rules-2024.html