Example yunq implementation for one Yellowstone endpoint.

This commit is contained in:
Drew Galbraith 2024-07-26 23:36:07 -07:00
parent 51d40f6db6
commit 3eea4d811a
11 changed files with 301 additions and 3 deletions

8
rust/Cargo.lock generated
View File

@ -52,6 +52,14 @@ dependencies = [
[[package]]
name = "testbed"
version = "0.1.0"
dependencies = [
"mammoth",
"yunq",
]
[[package]]
name = "yunq"
version = "0.1.0"
dependencies = [
"mammoth",
]

View File

@ -1,7 +1,7 @@
[workspace]
members = [
"lib/mammoth", "usr/testbed",
"lib/mammoth", "lib/yunq", "usr/testbed",
]
# the profile used for `cargo build`

View File

@ -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 {

View File

@ -59,7 +59,18 @@ impl From<u64> 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)
}
}

7
rust/lib/yunq/Cargo.toml Normal file
View File

@ -0,0 +1,7 @@
[package]
name = "yunq"
version = "0.1.0"
edition = "2021"
[dependencies]
mammoth = {path = "../mammoth"}

View File

@ -0,0 +1,60 @@
use alloc::boxed::Box;
use mammoth::syscall::ZError;
pub struct ByteBuffer<const N: usize> {
buffer: Box<[u8; N]>,
}
impl<const N: usize> ByteBuffer<N> {
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<T>(&mut self, offset: usize, obj: T) -> Result<(), ZError> {
if (size_of::<T>() + 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<T: Copy>(&self, offset: usize) -> Result<T, ZError> {
if (size_of::<T>() + 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)?)
}
}

View File

@ -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: YunqMessage, Resp: YunqMessage, const N: usize>(
req: &Req,
byte_buffer: &mut ByteBuffer<N>,
endpoint_cap: z_cap_t,
) -> Result<Resp, ZError> {
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::<u32>(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)?)
}

113
rust/lib/yunq/src/lib.rs Normal file
View File

@ -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<const N: usize>(
&self,
buf: &mut buffer::ByteBuffer<N>,
offset: usize,
_caps: &mut Vec<z_cap_t>,
) -> Result<usize, ZError> {
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<const N: usize>(
_buf: &ByteBuffer<N>,
_offset: usize,
_caps: &Vec<z_cap_t>,
) -> Result<Self, ZError>
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<const N: usize>(
buf: &ByteBuffer<N>,
offset: usize,
caps: &Vec<z_cap_t>,
) -> Result<Self, ZError> {
if buf.at::<u32>(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::<u64>(field_offset(offset, 0))?;
let endpoint = caps[endpoint_ptr as usize];
Ok(Endpoint { endpoint })
}
fn serialize<const N: usize>(
&self,
_buf: &mut buffer::ByteBuffer<N>,
_offset: usize,
_caps: &mut Vec<z_cap_t>,
) -> Result<usize, ZError> {
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<Endpoint, ZError> {
client::call_endpoint(req, &mut self.byte_buffer, self.endpoint_cap)
}
}

View File

@ -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<const N: usize>(
buf: &ByteBuffer<N>,
offset: usize,
caps: &Vec<z_cap_t>,
) -> Result<Self, ZError>
where
Self: Sized;
fn serialize<const N: usize>(
&self,
buf: &mut ByteBuffer<N>,
offset: usize,
caps: &mut Vec<z_cap_t>,
) -> Result<usize, ZError>;
}

View File

@ -5,3 +5,4 @@ edition = "2021"
[dependencies]
mammoth = { path = "../../lib/mammoth" }
yunq = { path = "../../lib/yunq" }

View File

@ -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
}