Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prepare syscall logic #127

Merged
merged 11 commits into from
Aug 8, 2024
Merged
8 changes: 1 addition & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,5 @@ polling = "3.4"
itertools = "0.13"
once_cell = "1.19"
bytesize = "1.1"
bincode = "1.3"
serde = { version = "1.0", features = ["derive"] }
5 changes: 2 additions & 3 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,14 @@ procfs.workspace = true
polling.workspace = true
itertools.workspace = true
anyhow.workspace = true
bincode.workspace = true
serde.workspace = true

log = "0"
walkdir = "2.3"
serde = { version = "1.0", features = ["derive"] }
memfd = "0.6"
bincode = "1.3"
thiserror = "1.0"
bytesize = {workspace = true, features = ["serde"]}
byteorder = "1.4.3"
enum_primitive = "0.1"
ptr_meta = "0.2.0"

Expand Down
171 changes: 171 additions & 0 deletions core/src/syscall/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
//! Common definitions for the execution of system calls

use anyhow::Result;

pub const SYSCALL_SOCKET_PATH: &str = "/syscall-a653";

pub mod receiver;
pub mod sender;
pub mod syscalls;
mod ty;

pub use ty::SyscallType;

// This is the data type that is transferred to the hypervisor when a
// syscall request is made by a partition. The parameter data is stored as an
// already serialized `Vec<u8>`, so that the receiver can deserialize the
// SyscallType without knowing the parameter's types.
type SyscallRequest = (SyscallType, Vec<u8>);

// This is the data type that is returned from the hypervisor to the partition
// when a syscall was handled. In contrast to [`SyscallRequest`], a generic can
// be used for the return value's type.
type SyscallResponse<T> = Result<T, a653rs::bindings::ErrorReturnCode>;

#[cfg(test)]
mod tests {
use std::collections::VecDeque;
use std::os::unix::net::UnixDatagram;
use std::thread;
use std::time::Duration;

use a653rs::bindings::ApexSystemTime;
use a653rs::prelude::{QueueOverflow, QueuingPortId};

use super::SyscallType;
use crate::syscall::receiver::{self, SyscallReceiver};
use crate::syscall::sender::SyscallSender;
use crate::syscall::syscalls;

fn new_sender_receiver_pair() -> (SyscallSender, SyscallReceiver) {
let (sender, receiver) = UnixDatagram::pair().unwrap();
(
SyscallSender::from_datagram(sender),
SyscallReceiver::from_datagram(receiver),
)
}

#[test]
pub fn single_syscall() {
let (sender, receiver) = new_sender_receiver_pair();

let receiver_thread = thread::spawn(move || {
let syscall_handler = |ty: SyscallType, serialized_params: &[u8]| -> Vec<u8> {
assert_eq!(ty, SyscallType::SendQueuingMessage);

receiver::wrap_serialization::<syscalls::SendQueuingMessage, _>(
serialized_params,
|params| {
assert_eq!(
params,
(
0 as QueuingPortId,
[1u8, 2, 3].as_slice(),
0 as ApexSystemTime
)
);

Ok(())
},
)
.expect("serialization to succeed")
};

let syscall_was_handled = receiver
.receive_one(Some(Duration::from_secs(1)), syscall_handler)
.unwrap();

assert!(syscall_was_handled);
});

// Make a syscall
let response: Result<(), a653rs::bindings::ErrorReturnCode> = sender
.execute::<syscalls::SendQueuingMessage>((
0 as QueuingPortId,
&[1, 2, 3],
0 as ApexSystemTime,
))
.expect("sending and receiving a response to succeed");

assert_eq!(response, Ok(()));

// join the receiver thread just to be safe
receiver_thread.join().unwrap();
}

#[test]
pub fn two_syscalls() {
let (sender, receiver) = new_sender_receiver_pair();

// The receiver thread represents the hypervisor.
let receiver_thread = thread::spawn(move || {
// A simulated queuing port. This represents the hypervisor state.
let mut queuing_port_state: VecDeque<Vec<u8>> = VecDeque::new();

let mut syscall_handler = |ty: SyscallType, serialized_params: &[u8]| -> Vec<u8> {
match ty {
SyscallType::SendQueuingMessage => {
receiver::wrap_serialization::<syscalls::SendQueuingMessage, _>(
serialized_params,
|params| {
queuing_port_state.push_back(params.1.to_owned());

Ok(())
},
)
.expect("serialization to succeed")
}
SyscallType::ReceiveQueuingMessage => {
receiver::wrap_serialization::<syscalls::ReceiveQueuingMessage, _>(
serialized_params,
|_params| {
queuing_port_state
.pop_front()
.map(|msg| (false as QueueOverflow, msg))
.ok_or(a653rs::bindings::ErrorReturnCode::NotAvailable)
},
)
.expect("serialization to succeed")
}
_ => unimplemented!("this test only implements two syscalls"),
}
};

// Let's handle exactly three syscalls
for _ in 0..3 {
let syscall_was_handled = receiver
.receive_one(Some(Duration::from_secs(1)), &mut syscall_handler)
.unwrap(); // TODO log error and ignore syscall
assert!(syscall_was_handled);
}
});

// Send one message into the queuing port
let response = sender
.execute::<syscalls::SendQueuingMessage>((
0 as QueuingPortId,
&[4, 3, 2, 1],
0 as ApexSystemTime,
))
.unwrap();
assert_eq!(response, Ok(()));

// Receive the previous message from the queuing port
let response = sender
.execute::<syscalls::ReceiveQueuingMessage>((0 as QueuingPortId, 0 as ApexSystemTime))
.expect("sending and receiving a response to succeed");
assert_eq!(response, Ok((false as QueueOverflow, vec![4, 3, 2, 1])));

// Now the queuing port should be empty
let response = sender
.execute::<syscalls::ReceiveQueuingMessage>((0 as QueuingPortId, 0 as ApexSystemTime))
.expect("sending and receiving a response to succeed");
assert_eq!(
response,
Err(a653rs::bindings::ErrorReturnCode::NotAvailable)
);

// join the receiver thread just to be safe
receiver_thread.join().unwrap();
}
}
Loading
Loading