From 3eea4d811aa7a052c2d9e934e0ec147ef3c95d2a Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Fri, 26 Jul 2024 23:36:07 -0700 Subject: [PATCH] Example yunq implementation for one Yellowstone endpoint. --- rust/Cargo.lock | 8 +++ rust/Cargo.toml | 2 +- rust/lib/mammoth/src/lib.rs | 2 +- rust/lib/mammoth/src/syscall.rs | 13 +++- rust/lib/yunq/Cargo.toml | 7 ++ rust/lib/yunq/src/buffer.rs | 60 +++++++++++++++++ rust/lib/yunq/src/client.rs | 60 +++++++++++++++++ rust/lib/yunq/src/lib.rs | 113 ++++++++++++++++++++++++++++++++ rust/lib/yunq/src/message.rs | 23 +++++++ rust/usr/testbed/Cargo.toml | 1 + rust/usr/testbed/src/main.rs | 15 +++++ 11 files changed, 301 insertions(+), 3 deletions(-) create mode 100644 rust/lib/yunq/Cargo.toml create mode 100644 rust/lib/yunq/src/buffer.rs create mode 100644 rust/lib/yunq/src/client.rs create mode 100644 rust/lib/yunq/src/lib.rs create mode 100644 rust/lib/yunq/src/message.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index f0b3f19..85502d3 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -52,6 +52,14 @@ dependencies = [ [[package]] name = "testbed" version = "0.1.0" +dependencies = [ + "mammoth", + "yunq", +] + +[[package]] +name = "yunq" +version = "0.1.0" dependencies = [ "mammoth", ] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index b1ec6be..0eb17a7 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,7 +1,7 @@ [workspace] members = [ - "lib/mammoth", "usr/testbed", + "lib/mammoth", "lib/yunq", "usr/testbed", ] # the profile used for `cargo build` diff --git a/rust/lib/mammoth/src/lib.rs b/rust/lib/mammoth/src/lib.rs index d55a2a1..3d8b7da 100644 --- a/rust/lib/mammoth/src/lib.rs +++ b/rust/lib/mammoth/src/lib.rs @@ -15,7 +15,7 @@ const Z_INIT_ENDPOINT: u64 = 0x4100_0000; static mut SELF_PROC_CAP: syscall::zcap = 0; static mut SELF_VMAS_CAP: syscall::zcap = 0; -static mut INIT_ENDPOINT: syscall::zcap = 0; +pub static mut INIT_ENDPOINT: syscall::zcap = 0; pub fn parse_init_port(port_cap: syscall::zcap) { loop { diff --git a/rust/lib/mammoth/src/syscall.rs b/rust/lib/mammoth/src/syscall.rs index 3c7b912..9f16f13 100644 --- a/rust/lib/mammoth/src/syscall.rs +++ b/rust/lib/mammoth/src/syscall.rs @@ -59,7 +59,18 @@ impl From for ZError { impl fmt::Debug for ZError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("ZError") + let str = match self { + ZError::INVALID_ARGUMENT => "INVALID_ARGUMENT", + ZError::BUFFER_SIZE => "BUFFER_SIZE", + ZError::INTERNAL => "INTERNAL", + ZError::UNIMPLEMENTED => "UNIMPLEMENTED", + ZError::INVALID_RESPONSE => "INVALID_RESPONSE", + ZError::CAP_NOT_FOUND => "CAP_NOT_FOUND", + ZError::CAP_WRONG_TYPE => "CAP_WRONG_TYPE", + ZError::CAP_PERMISSION_DENIED => "CAP_PERMISSION_DENIED", + _ => "ZError", + }; + f.write_str(str) } } diff --git a/rust/lib/yunq/Cargo.toml b/rust/lib/yunq/Cargo.toml new file mode 100644 index 0000000..cdb0249 --- /dev/null +++ b/rust/lib/yunq/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "yunq" +version = "0.1.0" +edition = "2021" + +[dependencies] +mammoth = {path = "../mammoth"} diff --git a/rust/lib/yunq/src/buffer.rs b/rust/lib/yunq/src/buffer.rs new file mode 100644 index 0000000..a070901 --- /dev/null +++ b/rust/lib/yunq/src/buffer.rs @@ -0,0 +1,60 @@ +use alloc::boxed::Box; +use mammoth::syscall::ZError; + +pub struct ByteBuffer { + buffer: Box<[u8; N]>, +} + +impl ByteBuffer { + pub fn new() -> Self { + Self { + buffer: Box::new([0; N]), + } + } + pub fn size(&self) -> u64 { + N as u64 + } + + pub fn raw_ptr(&self) -> *const u8 { + self.buffer.as_ptr() + } + + pub fn mut_ptr(&mut self) -> *mut u8 { + self.buffer.as_mut_ptr() + } + + pub fn write_at(&mut self, offset: usize, obj: T) -> Result<(), ZError> { + if (size_of::() + offset) > N { + return Err(ZError::BUFFER_SIZE); + } + unsafe { + *(self.buffer[offset..].as_mut_ptr() as *mut T) = obj; + } + Ok(()) + } + + pub fn write_str_at(&mut self, offset: usize, s: &str) -> Result<(), ZError> { + if (s.len() + offset) > N { + return Err(ZError::BUFFER_SIZE); + } + for i in 0..s.len() { + self.buffer[offset + i] = s.as_bytes()[i]; + } + Ok(()) + } + + pub fn at(&self, offset: usize) -> Result { + if (size_of::() + offset) > N { + return Err(ZError::BUFFER_SIZE); + } + unsafe { Ok(*(self.buffer[offset..].as_ptr() as *const T)) } + } + + pub fn str_at(&self, offset: usize, len: usize) -> Result<&str, ZError> { + if (len + offset) > N { + return Err(ZError::BUFFER_SIZE); + } + Ok(alloc::str::from_utf8(&self.buffer[offset..offset + len]) + .map_err(|_| ZError::INVALID_ARGUMENT)?) + } +} diff --git a/rust/lib/yunq/src/client.rs b/rust/lib/yunq/src/client.rs new file mode 100644 index 0000000..51795ed --- /dev/null +++ b/rust/lib/yunq/src/client.rs @@ -0,0 +1,60 @@ +use crate::buffer::ByteBuffer; +use crate::message::YunqMessage; +use alloc::vec::Vec; +use core::ffi::c_void; +use mammoth::syscall::z_cap_t; +use mammoth::syscall::ZError; + +const SENTINEL: u32 = 0xBEEFDEAD; + +pub fn call_endpoint( + req: &Req, + byte_buffer: &mut ByteBuffer, + endpoint_cap: z_cap_t, +) -> Result { + byte_buffer.write_at(0, SENTINEL)?; + byte_buffer.write_at(8, 1 as u64)?; + + let mut cap_buffer = Vec::new(); + + let length = req.serialize(byte_buffer, 16, &mut cap_buffer)?; + + byte_buffer.write_at(4, (16 + length) as u32)?; + + let mut reply_port_cap: u64 = 0; + let send_req = mammoth::syscall::ZEndpointSendReq { + caps: cap_buffer.as_ptr(), + num_caps: cap_buffer.len() as u64, + endpoint_cap, + data: byte_buffer.raw_ptr() as *const c_void, + num_bytes: 16 + length as u64, + reply_port_cap: &mut reply_port_cap as *mut z_cap_t, + }; + + mammoth::syscall::syscall(mammoth::syscall::kZionEndpointSend, &send_req)?; + // FIXME: Add a way to zero out the byte buffer. + + let mut buffer_size = byte_buffer.size(); + let mut num_caps: u64 = 10; + cap_buffer = vec![0; num_caps as usize]; + let recv_req = mammoth::syscall::ZReplyPortRecvReq { + reply_port_cap, + caps: cap_buffer.as_mut_ptr(), + num_caps: &mut num_caps as *mut u64, + data: byte_buffer.mut_ptr() as *mut c_void, + num_bytes: &mut buffer_size as *mut u64, + }; + + mammoth::syscall::syscall(mammoth::syscall::kZionReplyPortRecv, &recv_req)?; + + if byte_buffer.at::(0)? != SENTINEL { + return Err(ZError::INVALID_RESPONSE); + } + + let resp_code: u64 = byte_buffer.at(8)?; + if resp_code != 0 { + return Err(ZError::from(resp_code)); + } + + Ok(Resp::parse(&byte_buffer, 16, &cap_buffer)?) +} diff --git a/rust/lib/yunq/src/lib.rs b/rust/lib/yunq/src/lib.rs new file mode 100644 index 0000000..900cfd7 --- /dev/null +++ b/rust/lib/yunq/src/lib.rs @@ -0,0 +1,113 @@ +#![no_std] + +#[macro_use] +extern crate alloc; + +mod buffer; +mod client; +mod message; + +use alloc::string::String; +use alloc::vec::Vec; +pub use buffer::ByteBuffer; +use mammoth::syscall::z_cap_t; +use mammoth::syscall::ZError; +pub use message::YunqMessage; + +const MESSAGE_HEADER_SIZE: usize = 24; // 4x uint32, 1x uint64 + +pub struct GetEndpointRequest { + pub endpoint_name: String, +} + +impl YunqMessage for GetEndpointRequest { + fn serialize( + &self, + buf: &mut buffer::ByteBuffer, + offset: usize, + _caps: &mut Vec, + ) -> Result { + let core_size: u32 = (MESSAGE_HEADER_SIZE + 8 * 1) as u32; + let mut next_extension = core_size; + + let endpoint_name_offset = next_extension; + let endpoint_name_length = self.endpoint_name.len() as u32; + + buf.write_str_at(offset + next_extension as usize, &self.endpoint_name)?; + next_extension += endpoint_name_length; + + buf.write_at(field_offset(offset, 0), endpoint_name_offset)?; + buf.write_at(field_offset(offset, 0) + 4, endpoint_name_length)?; + + buf.write_at(offset + 0, message::MESSAGE_IDENT)?; + buf.write_at(offset + 4, core_size)?; + buf.write_at(offset + 8, next_extension)?; + buf.write_at(offset + 12, 0 as u32)?; + Ok(next_extension as usize) + } + + fn parse( + _buf: &ByteBuffer, + _offset: usize, + _caps: &Vec, + ) -> Result + where + Self: Sized, + { + todo!() + } +} + +pub struct Endpoint { + pub endpoint: z_cap_t, +} + +fn field_offset(offset: usize, field_index: usize) -> usize { + offset + MESSAGE_HEADER_SIZE + (8 * field_index) +} + +impl YunqMessage for Endpoint { + fn parse( + buf: &ByteBuffer, + offset: usize, + caps: &Vec, + ) -> Result { + if buf.at::(offset + 0)? != message::MESSAGE_IDENT { + return Err(ZError::INVALID_ARGUMENT); + } + // TODO: Parse core size. + // TODO: Parse extension size. + // TODO: Check CRC32 + // TODO: Parse options. + + let endpoint_ptr = buf.at::(field_offset(offset, 0))?; + let endpoint = caps[endpoint_ptr as usize]; + Ok(Endpoint { endpoint }) + } + + fn serialize( + &self, + _buf: &mut buffer::ByteBuffer, + _offset: usize, + _caps: &mut Vec, + ) -> Result { + todo!() + } +} + +pub struct YellowstoneClient { + endpoint_cap: z_cap_t, + byte_buffer: ByteBuffer<0x1000>, +} + +impl YellowstoneClient { + pub fn new(endpoint_cap: z_cap_t) -> Self { + Self { + endpoint_cap, + byte_buffer: ByteBuffer::new(), + } + } + pub fn get_endpoint(&mut self, req: &GetEndpointRequest) -> Result { + client::call_endpoint(req, &mut self.byte_buffer, self.endpoint_cap) + } +} diff --git a/rust/lib/yunq/src/message.rs b/rust/lib/yunq/src/message.rs new file mode 100644 index 0000000..82b2c42 --- /dev/null +++ b/rust/lib/yunq/src/message.rs @@ -0,0 +1,23 @@ +use crate::buffer::ByteBuffer; +use alloc::vec::Vec; +use mammoth::syscall::z_cap_t; +use mammoth::syscall::ZError; + +pub const MESSAGE_IDENT: u32 = 0x33441122; + +pub trait YunqMessage { + fn parse( + buf: &ByteBuffer, + offset: usize, + caps: &Vec, + ) -> Result + where + Self: Sized; + + fn serialize( + &self, + buf: &mut ByteBuffer, + offset: usize, + caps: &mut Vec, + ) -> Result; +} diff --git a/rust/usr/testbed/Cargo.toml b/rust/usr/testbed/Cargo.toml index 999e3eb..65e5146 100644 --- a/rust/usr/testbed/Cargo.toml +++ b/rust/usr/testbed/Cargo.toml @@ -5,3 +5,4 @@ edition = "2021" [dependencies] mammoth = { path = "../../lib/mammoth" } +yunq = { path = "../../lib/yunq" } diff --git a/rust/usr/testbed/src/main.rs b/rust/usr/testbed/src/main.rs index 0452781..ab46cf9 100644 --- a/rust/usr/testbed/src/main.rs +++ b/rust/usr/testbed/src/main.rs @@ -4,6 +4,7 @@ extern crate alloc; use alloc::boxed::Box; +use alloc::string::ToString; use mammoth::debug; use mammoth::define_entry; use mammoth::syscall::debug; @@ -25,5 +26,19 @@ pub extern "C" fn main() -> z_err_t { }; mammoth::syscall::syscall(mammoth::syscall::kZionMemoryObjectCreate, &obj_req) .expect("Failed to create memory object"); + + let mut yellowstone; + unsafe { + yellowstone = yunq::YellowstoneClient::new(mammoth::INIT_ENDPOINT); + } + + let endpoint = yellowstone + .get_endpoint(&yunq::GetEndpointRequest { + endpoint_name: "denali".to_string(), + }) + .expect("Failed to get endpoint"); + + debug!("Got endpoint w/ cap: {:#x}", endpoint.endpoint); + 0 }