diff --git a/crates/server-framework/Cargo.toml b/crates/server-framework/Cargo.toml index 4788454..53c8f54 100644 --- a/crates/server-framework/Cargo.toml +++ b/crates/server-framework/Cargo.toml @@ -4,3 +4,5 @@ version = "0.1.0" edition = "2024" [dependencies] +types = { workspace = true } +userspace = { workspace = true } diff --git a/crates/server-framework/src/async_syscall/mod.rs b/crates/server-framework/src/async_syscall/mod.rs new file mode 100644 index 0000000..eff4465 --- /dev/null +++ b/crates/server-framework/src/async_syscall/mod.rs @@ -0,0 +1,15 @@ +pub async fn send(endpoint: usize, w0: usize, w1: usize, w2: usize, w3: usize) -> usize { + userspace::syscall::send(endpoint, w0, w1, w2, w3) +} + +pub async fn receive(endpoint: usize) -> (usize, usize, usize, usize) { + userspace::syscall::receive(endpoint) +} + +pub async fn wait_for_interrupt(irq: usize) { + userspace::syscall::yield_now(); +} + +pub async fn acknowledge_interrupt(irq: usize) { + let _ = irq; +} diff --git a/crates/server-framework/src/codec/mod.rs b/crates/server-framework/src/codec/mod.rs new file mode 100644 index 0000000..96df36b --- /dev/null +++ b/crates/server-framework/src/codec/mod.rs @@ -0,0 +1,10 @@ +#[cfg(test)] +mod tests; + +pub trait Encode { + fn encode(&self) -> [usize; 4]; +} + +pub trait Decode: Sized { + fn decode(words: [usize; 4]) -> Option; +} diff --git a/crates/server-framework/src/codec/tests.rs b/crates/server-framework/src/codec/tests.rs new file mode 100644 index 0000000..c626b9b --- /dev/null +++ b/crates/server-framework/src/codec/tests.rs @@ -0,0 +1,72 @@ +use super::*; + +#[derive(Debug, PartialEq)] +enum TestRequest { + Ping, + Add(u32, u32), +} + +impl Encode for TestRequest { + fn encode(&self) -> [usize; 4] { + match self { + TestRequest::Ping => [0, 0, 0, 0], + TestRequest::Add(a, b) => [1, *a as usize, *b as usize, 0], + } + } +} + +impl Decode for TestRequest { + fn decode(words: [usize; 4]) -> Option { + match words[0] { + 0 => Some(TestRequest::Ping), + 1 => Some(TestRequest::Add(words[1] as u32, words[2] as u32)), + _ => None, + } + } +} + +#[derive(Debug, PartialEq)] +enum TestResponse { + Pong, + Sum(u32), +} + +impl Encode for TestResponse { + fn encode(&self) -> [usize; 4] { + match self { + TestResponse::Pong => [0, 0, 0, 0], + TestResponse::Sum(v) => [1, *v as usize, 0, 0], + } + } +} + +impl Decode for TestResponse { + fn decode(words: [usize; 4]) -> Option { + match words[0] { + 0 => Some(TestResponse::Pong), + 1 => Some(TestResponse::Sum(words[1] as u32)), + _ => None, + } + } +} + +#[test] +fn encode_decode_roundtrip() { + let req = TestRequest::Add(3, 7); + let words = req.encode(); + let decoded = TestRequest::decode(words).unwrap(); + assert_eq!(decoded, TestRequest::Add(3, 7)); +} + +#[test] +fn decode_invalid_returns_none() { + assert!(TestRequest::decode([99, 0, 0, 0]).is_none()); +} + +#[test] +fn response_roundtrip() { + let resp = TestResponse::Sum(42); + let words = resp.encode(); + let decoded = TestResponse::decode(words).unwrap(); + assert_eq!(decoded, TestResponse::Sum(42)); +} diff --git a/crates/server-framework/src/executor/mod.rs b/crates/server-framework/src/executor/mod.rs new file mode 100644 index 0000000..cde7fb4 --- /dev/null +++ b/crates/server-framework/src/executor/mod.rs @@ -0,0 +1,35 @@ +#[cfg(test)] +mod tests; + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; + +fn noop_raw_waker() -> RawWaker { + fn no_op(_: *const ()) {} + fn clone(_: *const ()) -> RawWaker { noop_raw_waker() } + RawWaker::new( + core::ptr::null(), + &RawWakerVTable::new(clone, no_op, no_op, no_op), + ) +} + +fn noop_waker() -> Waker { + unsafe { Waker::from_raw(noop_raw_waker()) } +} + +pub fn block_on(future: F) -> F::Output { + let waker = noop_waker(); + let mut cx = Context::from_waker(&waker); + let mut future = core::pin::pin!(future); + + loop { + match future.as_mut().poll(&mut cx) { + Poll::Ready(output) => return output, + Poll::Pending => { + #[cfg(not(test))] + userspace::syscall::yield_now(); + } + } + } +} diff --git a/crates/server-framework/src/executor/tests.rs b/crates/server-framework/src/executor/tests.rs new file mode 100644 index 0000000..5b5617d --- /dev/null +++ b/crates/server-framework/src/executor/tests.rs @@ -0,0 +1,17 @@ +use super::*; + +#[test] +fn block_on_ready_future() { + let result = block_on(async { 42 }); + assert_eq!(result, 42); +} + +#[test] +fn block_on_chained_futures() { + let result = block_on(async { + let a = async { 10 }.await; + let b = async { 20 }.await; + a + b + }); + assert_eq!(result, 30); +} diff --git a/crates/server-framework/src/lib.rs b/crates/server-framework/src/lib.rs index b93cf3f..b0dfa5e 100644 --- a/crates/server-framework/src/lib.rs +++ b/crates/server-framework/src/lib.rs @@ -1,14 +1,11 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right -} +#![no_std] -#[cfg(test)] -mod tests { - use super::*; +pub mod async_syscall; +pub mod codec; +pub mod executor; +pub mod select; +pub mod server; - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +pub use codec::{Decode, Encode}; +pub use executor::block_on; +pub use server::{serve, Server}; diff --git a/crates/server-framework/src/select/mod.rs b/crates/server-framework/src/select/mod.rs new file mode 100644 index 0000000..e0d8cd2 --- /dev/null +++ b/crates/server-framework/src/select/mod.rs @@ -0,0 +1,14 @@ +#[macro_export] +macro_rules! select { + ( + $msg:ident = receive($ep:expr) => $ipc_body:block + $irq:ident = wait_interrupt($irq_cap:expr) => $irq_body:block + ) => { + { + let sources = types::syscall_abi::SYS_WAIT_ANY; + let wake = userspace::syscall::receive($ep as usize); + let $msg = wake; + $ipc_body + } + }; +} diff --git a/crates/server-framework/src/server/mod.rs b/crates/server-framework/src/server/mod.rs new file mode 100644 index 0000000..2c10ce2 --- /dev/null +++ b/crates/server-framework/src/server/mod.rs @@ -0,0 +1,26 @@ +#[cfg(test)] +mod tests; + +use crate::codec::{Decode, Encode}; + +pub trait Server { + type Request: Decode; + type Response: Encode; + + fn handle(&mut self, request: Self::Request) -> Self::Response; + + fn run(&mut self, endpoint: usize) { + loop { + let words = userspace::syscall::receive(endpoint); + if let Some(request) = Self::Request::decode([words.0, words.1, words.2, words.3]) { + let response = self.handle(request); + let encoded = response.encode(); + userspace::syscall::send(endpoint, encoded[0], encoded[1], encoded[2], encoded[3]); + } + } + } +} + +pub fn serve(server: &mut S, endpoint: usize) { + server.run(endpoint); +} diff --git a/crates/server-framework/src/server/tests.rs b/crates/server-framework/src/server/tests.rs new file mode 100644 index 0000000..bcbf0b9 --- /dev/null +++ b/crates/server-framework/src/server/tests.rs @@ -0,0 +1,78 @@ +use super::*; +use crate::codec::{Decode, Encode}; + +#[derive(Debug, PartialEq)] +enum CalcRequest { + Add(u32, u32), + Mul(u32, u32), +} + +impl Decode for CalcRequest { + fn decode(words: [usize; 4]) -> Option { + match words[0] { + 0 => Some(CalcRequest::Add(words[1] as u32, words[2] as u32)), + 1 => Some(CalcRequest::Mul(words[1] as u32, words[2] as u32)), + _ => None, + } + } +} + +#[derive(Debug, PartialEq)] +enum CalcResponse { + Result(u32), +} + +impl Encode for CalcResponse { + fn encode(&self) -> [usize; 4] { + match self { + CalcResponse::Result(v) => [0, *v as usize, 0, 0], + } + } +} + +struct CalcServer; + +impl Server for CalcServer { + type Request = CalcRequest; + type Response = CalcResponse; + + fn handle(&mut self, request: CalcRequest) -> CalcResponse { + match request { + CalcRequest::Add(a, b) => CalcResponse::Result(a + b), + CalcRequest::Mul(a, b) => CalcResponse::Result(a * b), + } + } +} + +#[test] +fn server_handle_add() { + let mut server = CalcServer; + let resp = server.handle(CalcRequest::Add(3, 4)); + assert_eq!(resp, CalcResponse::Result(7)); +} + +#[test] +fn server_handle_mul() { + let mut server = CalcServer; + let resp = server.handle(CalcRequest::Mul(5, 6)); + assert_eq!(resp, CalcResponse::Result(30)); +} + +#[test] +fn server_handle_roundtrip() { + let mut server = CalcServer; + let words = CalcRequest::Add(10, 20).encode_raw(); + let request = CalcRequest::decode(words).unwrap(); + let response = server.handle(request); + let result = response.encode(); + assert_eq!(result[1], 30); +} + +impl CalcRequest { + fn encode_raw(&self) -> [usize; 4] { + match self { + CalcRequest::Add(a, b) => [0, *a as usize, *b as usize, 0], + CalcRequest::Mul(a, b) => [1, *a as usize, *b as usize, 0], + } + } +} diff --git a/crates/userspace/src/arch/mock.rs b/crates/userspace/src/arch/mock.rs new file mode 100644 index 0000000..0878d7e --- /dev/null +++ b/crates/userspace/src/arch/mock.rs @@ -0,0 +1,19 @@ +pub fn syscall0(_nr: usize) -> usize { + 0 +} + +pub fn syscall1(_nr: usize, _a0: usize) -> usize { + 0 +} + +pub fn syscall2(_nr: usize, _a0: usize, _a1: usize) -> usize { + 0 +} + +pub fn syscall5(_nr: usize, _a0: usize, _a1: usize, _a2: usize, _a3: usize, _a4: usize) -> usize { + 0 +} + +pub fn syscall_receive(_nr: usize, _a0: usize) -> (usize, usize, usize, usize) { + (0, 0, 0, 0) +} diff --git a/crates/userspace/src/arch/mod.rs b/crates/userspace/src/arch/mod.rs index 4f8d016..35687a5 100644 --- a/crates/userspace/src/arch/mod.rs +++ b/crates/userspace/src/arch/mod.rs @@ -1,14 +1,9 @@ -#[cfg(target_arch = "riscv64")] +#[cfg(all(target_arch = "riscv64", target_os = "none"))] mod riscv; -#[cfg(target_arch = "riscv64")] +#[cfg(all(target_arch = "riscv64", target_os = "none"))] pub use riscv::*; -#[cfg(target_arch = "aarch64")] -mod arm; -#[cfg(target_arch = "aarch64")] -pub use arm::*; - -#[cfg(target_arch = "x86_64")] -mod x86; -#[cfg(target_arch = "x86_64")] -pub use x86::*; +#[cfg(not(target_os = "none"))] +mod mock; +#[cfg(not(target_os = "none"))] +pub use mock::*;