diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 837436a..3f089b0 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -114,6 +114,7 @@ version = "0.1.0" dependencies = [ "mammoth", "yellowstone", + "yunq", ] [[package]] diff --git a/rust/lib/mammoth/src/bindings.rs b/rust/lib/mammoth/src/bindings.rs index aaffeb5..16309b3 100644 --- a/rust/lib/mammoth/src/bindings.rs +++ b/rust/lib/mammoth/src/bindings.rs @@ -508,7 +508,7 @@ pub struct ZReplyPortSendReq { pub num_bytes: u64, pub data: *const ::core::ffi::c_void, pub num_caps: u64, - pub caps: *mut z_cap_t, + pub caps: *const z_cap_t, } #[repr(C)] #[derive(Debug, Copy, Clone)] diff --git a/rust/lib/mammoth/src/syscall.rs b/rust/lib/mammoth/src/syscall.rs index 0346e7f..86765af 100644 --- a/rust/lib/mammoth/src/syscall.rs +++ b/rust/lib/mammoth/src/syscall.rs @@ -122,6 +122,17 @@ pub fn port_poll( Ok((num_bytes, num_caps)) } +pub fn endpoint_create() -> Result { + let mut endpoint_cap: z_cap_t = 0; + syscall( + zion::kZionEndpointCreate, + &zion::ZEndpointCreateReq { + endpoint_cap: &mut endpoint_cap, + }, + )?; + Ok(endpoint_cap) +} + pub fn endpoint_send( endpoint_cap: z_cap_t, bytes: &[u8], @@ -134,7 +145,7 @@ pub fn endpoint_send( endpoint_cap, data: bytes.as_ptr() as *const c_void, num_bytes: bytes.len() as u64, - reply_port_cap: &mut reply_port_cap as *mut z_cap_t, + reply_port_cap: &mut reply_port_cap, }; syscall(zion::kZionEndpointSend, &send_req)?; @@ -142,6 +153,45 @@ pub fn endpoint_send( Ok(reply_port_cap) } +pub fn endpoint_recv( + endpoint_cap: z_cap_t, + bytes: &mut [u8], + caps: &mut [z_cap_t], +) -> Result<(u64, u64, z_cap_t), ZError> { + let mut num_bytes = bytes.len() as u64; + let mut num_caps = caps.len() as u64; + let mut reply_port_cap = 0; + let recv_req = zion::ZEndpointRecvReq { + endpoint_cap, + data: bytes.as_mut_ptr() as *mut c_void, + num_bytes: &mut num_bytes, + caps: caps.as_mut_ptr(), + num_caps: &mut num_caps, + reply_port_cap: &mut reply_port_cap, + }; + + syscall(zion::kZionEndpointRecv, &recv_req)?; + + Ok((num_bytes, num_caps, reply_port_cap)) +} + +pub fn reply_port_send( + reply_port_cap: z_cap_t, + bytes: &[u8], + caps: &[z_cap_t], +) -> Result<(), ZError> { + syscall( + zion::kZionReplyPortSend, + &zion::ZReplyPortSendReq { + reply_port_cap, + data: bytes.as_ptr() as *const c_void, + num_bytes: bytes.len() as u64, + caps: caps.as_ptr(), + num_caps: caps.len() as u64, + }, + ) +} + pub fn reply_port_recv( reply_port_cap: z_cap_t, bytes: &mut [u8], @@ -152,9 +202,9 @@ pub fn reply_port_recv( let recv_req = zion::ZReplyPortRecvReq { reply_port_cap, caps: caps.as_mut_ptr(), - num_caps: &mut num_caps as *mut u64, + num_caps: &mut num_caps, data: bytes.as_mut_ptr() as *mut c_void, - num_bytes: &mut num_bytes as *mut u64, + num_bytes: &mut num_bytes, }; syscall(zion::kZionReplyPortRecv, &recv_req)?; diff --git a/rust/lib/mammoth/src/thread.rs b/rust/lib/mammoth/src/thread.rs index 1ddc85d..92fd086 100644 --- a/rust/lib/mammoth/src/thread.rs +++ b/rust/lib/mammoth/src/thread.rs @@ -2,39 +2,40 @@ use crate::syscall; use crate::zion; use crate::zion::z_cap_t; +use alloc::boxed::Box; use core::ffi::c_void; pub type ThreadEntry = fn(*const c_void) -> (); #[no_mangle] -extern "C" fn internal_entry_point(entry_ptr: *const ThreadEntry, arg1: *const c_void) -> ! { - let entry = unsafe { *entry_ptr }; +extern "C" fn internal_entry_point(thread_ptr: *const Thread, arg1: *const c_void) -> ! { + let thread: &Thread = unsafe { thread_ptr.as_ref().expect("Failed to unwrap thread ref") }; - entry(arg1); + (thread.entry)(arg1); syscall::thread_exit() } // TODO: Add a Drop implementation that kills this thread and drops its capability. -pub struct Thread<'a> { +pub struct Thread { cap: z_cap_t, // This field only exists to ensure that the entry reference will outlive the thread object // itself. - _entry: &'a ThreadEntry, + entry: ThreadEntry, } -impl<'a> Thread<'a> { - pub fn spawn(entry: &'a ThreadEntry, arg1: *const c_void) -> Result { +impl Thread { + pub fn spawn(entry: ThreadEntry, arg1: *const c_void) -> Result, zion::ZError> { let proc_cap = unsafe { crate::init::SELF_PROC_CAP }; let cap = syscall::thread_create(proc_cap)?; - + let thread = Box::new(Self { cap, entry }); syscall::thread_start( cap, internal_entry_point as u64, - entry as *const ThreadEntry as u64, + thread.as_ref() as *const Thread as u64, arg1 as u64, )?; - Ok(Self { cap, _entry: entry }) + Ok(thread) } pub fn join(&self) -> Result<(), zion::ZError> { diff --git a/rust/lib/yunq/src/client.rs b/rust/lib/yunq/src/client.rs index de8cada..ad3a761 100644 --- a/rust/lib/yunq/src/client.rs +++ b/rust/lib/yunq/src/client.rs @@ -28,5 +28,11 @@ pub fn call_endpoint( cap_buffer.as_mut_slice(), )?; + let resp_code: u64 = byte_buffer.at(8)?; + + if resp_code != 0 { + return Err(ZError::from(resp_code)); + } + Ok(Resp::parse_from_request(&byte_buffer, &cap_buffer)?) } diff --git a/rust/lib/yunq/src/lib.rs b/rust/lib/yunq/src/lib.rs index 515760b..93166ac 100644 --- a/rust/lib/yunq/src/lib.rs +++ b/rust/lib/yunq/src/lib.rs @@ -6,6 +6,7 @@ extern crate alloc; mod buffer; pub mod client; pub mod message; +pub mod server; pub use buffer::ByteBuffer; pub use message::YunqMessage; diff --git a/rust/lib/yunq/src/message.rs b/rust/lib/yunq/src/message.rs index 73b37e4..7b84b8d 100644 --- a/rust/lib/yunq/src/message.rs +++ b/rust/lib/yunq/src/message.rs @@ -4,13 +4,23 @@ use mammoth::zion::z_cap_t; use mammoth::zion::ZError; pub const MESSAGE_IDENT: u32 = 0x33441122; -const SENTINEL: u32 = 0xBEEFDEAD; pub const MESSAGE_HEADER_SIZE: usize = 24; // 4x uint32, 1x uint64 +const SENTINEL: u32 = 0xBEEFDEAD; +const SERIALIZE_HEADER_SIZE: u32 = 0x10; pub fn field_offset(offset: usize, field_index: usize) -> usize { offset + MESSAGE_HEADER_SIZE + (8 * field_index) } +pub fn serialize_error(buf: &mut ByteBuffer, err: ZError) { + buf.write_at(0, SENTINEL) + .expect("Failed to serialize SENTINEL"); + buf.write_at(4, SERIALIZE_HEADER_SIZE) + .expect("Failed to serialize size"); + buf.write_at(8, err as u64) + .expect("Failed to serialize error"); +} + pub trait YunqMessage { fn parse( buf: &ByteBuffer, @@ -31,11 +41,6 @@ pub trait YunqMessage { return Err(ZError::INVALID_RESPONSE); } - let resp_code: u64 = buf.at(8)?; - if resp_code != 0 { - return Err(ZError::from(resp_code)); - } - Ok(Self::parse(&buf, 16, &caps)?) } diff --git a/rust/lib/yunq/src/server.rs b/rust/lib/yunq/src/server.rs new file mode 100644 index 0000000..c8c4f36 --- /dev/null +++ b/rust/lib/yunq/src/server.rs @@ -0,0 +1,46 @@ +use crate::buffer::ByteBuffer; +use alloc::vec::Vec; +use mammoth::syscall; +use mammoth::zion::z_cap_t; +use mammoth::zion::ZError; + +pub trait YunqServer { + fn server_loop(&self) { + loop { + let mut byte_buffer = ByteBuffer::<1024>::new(); + let mut cap_buffer = vec![0; 10]; + let (_, _, reply_port_cap) = syscall::endpoint_recv( + self.endpoint_cap(), + byte_buffer.mut_slice(), + &mut cap_buffer, + ) + .expect("Failed to call endpoint recv"); + + let method = byte_buffer + .at::(8) + .expect("Failed to access request length."); + let resp = self.handle_request(method, &mut byte_buffer, &mut cap_buffer); + match resp { + Ok(resp_len) => syscall::reply_port_send( + reply_port_cap, + byte_buffer.slice(resp_len), + &cap_buffer, + ) + .expect("Failed to reply"), + Err(err) => { + crate::message::serialize_error(&mut byte_buffer, err); + syscall::reply_port_send(reply_port_cap, &byte_buffer.slice(0x10), &[]) + .expect("Failed to reply w/ error") + } + } + } + } + + fn endpoint_cap(&self) -> z_cap_t; + fn handle_request( + &self, + method_number: u64, + byte_buffer: &mut ByteBuffer<1024>, + cap_buffer: &mut Vec, + ) -> Result; +} diff --git a/rust/usr/testbed/Cargo.toml b/rust/usr/testbed/Cargo.toml index 66b00a9..7934e6c 100644 --- a/rust/usr/testbed/Cargo.toml +++ b/rust/usr/testbed/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" [dependencies] mammoth = { path = "../../lib/mammoth" } yellowstone = { path = "../../lib/yellowstone" } +yunq = { path = "../../lib/yunq" } diff --git a/rust/usr/testbed/src/main.rs b/rust/usr/testbed/src/main.rs index 661c98b..1b27a05 100644 --- a/rust/usr/testbed/src/main.rs +++ b/rust/usr/testbed/src/main.rs @@ -8,11 +8,46 @@ use mammoth::debug; use mammoth::define_entry; use mammoth::thread; use mammoth::zion::z_err_t; +use mammoth::zion::ZError; use yellowstone::GetEndpointRequest; use yellowstone::YellowstoneClient; +use yellowstone::YellowstoneServer; +use yunq::server::YunqServer; define_entry!(); +struct YellowstoneImpl {} + +impl yellowstone::YellowstoneServerHandler for YellowstoneImpl { + fn register_endpoint(&self, req: &yellowstone::RegisterEndpointRequest) -> Result<(), ZError> { + debug!("{}", req.endpoint_name); + Ok(()) + } + + fn get_endpoint(&self, req: &GetEndpointRequest) -> Result { + debug!("{}", req.endpoint_name); + Ok(yellowstone::Endpoint { + endpoint: unsafe { mammoth::init::SELF_PROC_CAP }, + }) + } + + fn get_ahci_info(&self) -> Result { + todo!() + } + + fn get_xhci_info(&self) -> Result { + todo!() + } + + fn get_framebuffer_info(&self) -> Result { + todo!() + } + + fn get_denali(&self) -> Result { + todo!() + } +} + #[no_mangle] pub extern "C" fn main() -> z_err_t { debug!("Testing!"); @@ -33,9 +68,25 @@ pub extern "C" fn main() -> z_err_t { let e: thread::ThreadEntry = |_| { debug!("Testing 1 2 3"); }; - let t = thread::Thread::spawn(&e, core::ptr::null()).expect("Failed to spawn thread"); + let t = thread::Thread::spawn(e, core::ptr::null()).expect("Failed to spawn thread"); t.join().expect("Failed to wait."); + let server = YellowstoneServer::new(YellowstoneImpl {}).expect("Failed to create server"); + + let mut yellowstone = YellowstoneClient::new(server.endpoint_cap()); + + let t = server.run_server().expect("Failed to start server"); + + let endpoint = yellowstone + .get_endpoint(&GetEndpointRequest { + endpoint_name: "test".to_string(), + }) + .expect("Failed to get endpoint"); + + debug!("{:#x}", endpoint.endpoint); + + t.join().expect("Failed to wait"); + 0 } diff --git a/yunq/rust/src/codegen.rs b/yunq/rust/src/codegen.rs index 1f4bc64..31f61e4 100644 --- a/yunq/rust/src/codegen.rs +++ b/yunq/rust/src/codegen.rs @@ -50,7 +50,7 @@ fn generate_method(method: &Method) -> TokenStream { } } -fn generate_interface(interface: &Interface) -> TokenStream { +fn generate_client(interface: &Interface) -> TokenStream { let client_name = interface.name.clone() + "Client"; let name = ident(&client_name); let methods = interface.methods.iter().map(|m| generate_method(&m)); @@ -73,17 +73,144 @@ fn generate_interface(interface: &Interface) -> TokenStream { } } +fn generate_server_case(method: &Method) -> TokenStream { + let id = proc_macro2::Literal::u64_suffixed(method.number); + let name = ident(&method.name.to_case(Case::Snake)); + let maybe_req = method.request.clone().map(|r| ident(&r)); + let maybe_resp = method.response.clone().map(|r| ident(&r)); + match (maybe_req, maybe_resp) { + (Some(req), Some(_)) => quote! { + #id => { + let req = #req::parse_from_request(byte_buffer, cap_buffer)?; + let resp = self.handler.#name(&req)?; + cap_buffer.resize(0, 0); + let resp_len = resp.serialize_as_request(0, byte_buffer, cap_buffer)?; + Ok(resp_len) + }, + }, + (Some(req), None) => quote! { + #id => { + let req = #req::parse_from_request(byte_buffer, cap_buffer)?; + self.handler.#name(&req)?; + cap_buffer.resize(0, 0); + // TODO: Implement serialization for EmptyMessage so this is less hacky. + yunq::message::serialize_error(byte_buffer, ZError::from(0)); + Ok(0x10) + }, + }, + (None, Some(_)) => quote! { + #id => { + let resp = self.handler.#name()?; + cap_buffer.resize(0, 0); + let resp_len = resp.serialize_as_request(0, byte_buffer, cap_buffer)?; + Ok(resp_len) + }, + }, + _ => unreachable!(), + } +} + +fn generate_server_method(method: &Method) -> TokenStream { + let name = ident(&method.name.to_case(Case::Snake)); + let maybe_req = method.request.clone().map(|r| ident(&r)); + let maybe_resp = method.response.clone().map(|r| ident(&r)); + match (maybe_req, maybe_resp) { + (Some(req), Some(resp)) => quote! { + fn #name (&self, req: & #req) -> Result<#resp, ZError>; + }, + (Some(req), None) => quote! { + fn #name (&self, req: & #req) -> Result<(), ZError>; + }, + (None, Some(resp)) => quote! { + fn #name (&self) -> Result<#resp, ZError>; + }, + _ => unreachable!(), + } +} + +fn generate_server(interface: &Interface) -> TokenStream { + let server_name = ident(&(interface.name.clone() + "Server")); + let server_trait = ident(&(interface.name.clone() + "ServerHandler")); + let server_trait_methods = interface.methods.iter().map(|m| generate_server_method(&m)); + let server_match_cases = interface.methods.iter().map(|m| generate_server_case(&m)); + quote! { + pub trait #server_trait { + #(#server_trait_methods)* + } + + pub struct #server_name { + endpoint_cap: z_cap_t, + handler: T + } + + impl #server_name { + pub fn new(handler: T) -> Result { + Ok(Self { + endpoint_cap: syscall::endpoint_create()?, + handler, + }) + } + + pub fn run_server(&self) -> Result, ZError> { + let thread_entry = |server_ptr: *const c_void| { + let server = unsafe { (server_ptr as *const #server_name).as_ref().expect("Failed to convert to server") }; + server.server_loop(); + }; + thread::Thread::spawn( + thread_entry, + self as *const Self as *const core::ffi::c_void, + ) + } + } + + impl yunq::server::YunqServer for #server_name { + fn endpoint_cap(&self) -> z_cap_t { + self.endpoint_cap + } + + fn handle_request( + &self, + method_number: u64, + byte_buffer: &mut ByteBuffer<1024>, + cap_buffer: &mut Vec, + ) -> Result { + match method_number { + #(#server_match_cases)* + + _ => Err(ZError::UNIMPLEMENTED) + } + } + } + } +} + +fn generate_interface(interface: &Interface) -> TokenStream { + let client = generate_client(interface); + let server = generate_server(interface); + quote! { + #client + + #server + } +} + pub fn generate_code(ast: &Vec) -> String { let prelude = quote! { extern crate alloc; + use alloc::boxed::Box; use alloc::string::String; use alloc::string::ToString; + use alloc::vec::Vec; + use core::ffi::c_void; + use mammoth::syscall; + use mammoth::thread; use mammoth::zion::z_cap_t; use mammoth::zion::ZError; use yunq::ByteBuffer; use yunq::YunqMessage; + use yunq::server::YunqServer; use yunq_derive::YunqMessage; }; diff --git a/zion/include/zcall.h b/zion/include/zcall.h index c5f9ca6..31e45df 100644 --- a/zion/include/zcall.h +++ b/zion/include/zcall.h @@ -56,7 +56,7 @@ SYS6(EndpointSend, z_cap_t, endpoint_cap, uint64_t, num_bytes, const void*, SYS6(EndpointRecv, z_cap_t, endpoint_cap, uint64_t*, num_bytes, void*, data, uint64_t*, num_caps, z_cap_t*, caps, z_cap_t*, reply_port_cap); SYS5(ReplyPortSend, z_cap_t, reply_port_cap, uint64_t, num_bytes, const void*, - data, uint64_t, num_caps, z_cap_t*, caps); + data, uint64_t, num_caps, const z_cap_t*, caps); SYS5(ReplyPortRecv, z_cap_t, reply_port_cap, uint64_t*, num_bytes, void*, data, uint64_t*, num_caps, z_cap_t*, caps);