use async_trait::async_trait;
use reverie_syscalls::Syscall;
use serde::Serialize;
use serde::de::DeserializeOwned;
use crate::ExitStatus;
use crate::Pid;
use crate::Signal;
use crate::SignalEvent;
use crate::Subscription;
use crate::Tid;
use crate::error::Errno;
use crate::error::Error;
use crate::guest::Guest;
#[cfg(target_arch = "x86_64")]
use crate::rdtsc::Rdtsc;
#[cfg(target_arch = "x86_64")]
use crate::rdtsc::RdtscResult;
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
Default,
Serialize,
serde::Deserialize
)]
pub enum ThreadOwnership {
#[default]
Tool,
Host,
}
impl ThreadOwnership {
pub fn executes_on_tool(self) -> bool {
matches!(self, ThreadOwnership::Tool)
}
pub fn executes_on_host(self) -> bool {
matches!(self, ThreadOwnership::Host)
}
pub fn futex_is_host_owned(self) -> bool {
matches!(self, ThreadOwnership::Host)
}
}
#[async_trait]
pub trait GlobalTool: Send + Sync + Default {
type Request: Serialize + DeserializeOwned + Send;
type Response: Serialize + DeserializeOwned + Send;
type Config: Serialize + DeserializeOwned + Send + Sync + Clone + Default;
async fn init_global_state(_cfg: &Self::Config) -> Self {
Default::default()
}
fn install_backend_signal_control(
&self,
_control: Option<crate::BackendSignalControl>,
) -> Result<crate::BackendSignalControlMode, Error> {
Ok(crate::BackendSignalControlMode::Unchanged)
}
fn authorize_backend_signal_boundary(
&self,
_task: crate::SignalTaskIdentity,
) -> Result<Option<crate::SignalDeliveryPermit>, Error> {
Ok(None)
}
async fn on_backend_signal_boundary(
&self,
_receipt: crate::SignalBoundaryReceipt,
) -> Result<(), Error> {
Ok(())
}
fn on_backend_process_retired(&self, _event: BackendProcessRetirement) -> Result<(), Error> {
Ok(())
}
async fn receive_rpc(&self, _from: Tid, _message: Self::Request) -> Self::Response;
fn report_backend_failure(&self, _event: BackendFailure) {}
async fn wait_for_backend_failure(&self) {
std::future::pending::<()>().await
}
async fn on_backend_child_wait_event(
&self,
_event: BackendChildWaitEvent,
) -> Result<(), Error> {
Ok(())
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BackendProcessRetirement {
pub process: crate::SignalProcessId,
pub status: ExitStatus,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BackendFailure {
pub pid: Pid,
pub tid: Tid,
pub phase: &'static str,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BackendChildWaitState {
Exited {
status: ExitStatus,
waitable: bool,
uid: u32,
user_ticks: i64,
system_ticks: i64,
},
Stopped(i32),
Continued,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BackendChildWaitEvent {
pub parent: crate::SignalProcessId,
pub child: crate::SignalProcessId,
pub state: BackendChildWaitState,
}
impl BackendChildWaitEvent {
pub fn child_exit_completion(self) -> Option<crate::ChildExitCompletion> {
let BackendChildWaitState::Exited {
status,
waitable,
uid,
user_ticks,
system_ticks,
} = self.state
else {
return None;
};
Some(crate::ChildExitCompletion {
parent: self.parent,
child: self.child,
status,
waitable,
uid,
user_ticks,
system_ticks,
})
}
}
#[async_trait]
impl GlobalTool for () {
type Request = ();
type Response = ();
type Config = ();
async fn receive_rpc(&self, _from: Tid, _message: ()) {}
}
#[async_trait]
pub trait Tool: Send + Sync + Default {
type GlobalState: GlobalTool;
type ThreadState: Serialize + DeserializeOwned + Default + Send + Sync;
fn new(_pid: Pid, _cfg: &<Self::GlobalState as GlobalTool>::Config) -> Self {
Default::default()
}
fn subscriptions(_cfg: &<Self::GlobalState as GlobalTool>::Config) -> Subscription {
Subscription::all_syscalls()
}
fn thread_ownership(_cfg: &<Self::GlobalState as GlobalTool>::Config) -> ThreadOwnership {
ThreadOwnership::Tool
}
fn init_thread_state(
&self,
_child: Tid,
_parent: Option<(Tid, &Self::ThreadState)>,
) -> Self::ThreadState {
Default::default()
}
async fn handle_thread_start<T: Guest<Self>>(&self, _guest: &mut T) -> Result<(), Error> {
Ok(())
}
async fn handle_post_exec<T: Guest<Self>>(&self, _guest: &mut T) -> Result<(), Errno> {
Ok(())
}
async fn handle_syscall_event<T: Guest<Self>>(
&self,
guest: &mut T,
c: Syscall,
) -> Result<i64, Error> {
guest.tail_inject(c).await
}
#[cfg(target_arch = "x86_64")]
async fn handle_cpuid_event<T: Guest<Self>>(
&self,
_guest: &mut T,
eax: u32,
ecx: u32,
) -> Result<raw_cpuid::CpuIdResult, Errno> {
Ok(raw_cpuid::cpuid!(eax, ecx))
}
#[cfg(target_arch = "x86_64")]
async fn handle_rdtsc_event<T: Guest<Self>>(
&self,
_guest: &mut T,
request: Rdtsc,
) -> Result<RdtscResult, Errno> {
Ok(RdtscResult::new(request))
}
async fn handle_signal_event<T: Guest<Self>>(
&self,
_guest: &mut T,
signal: Signal,
) -> Result<Option<Signal>, Errno> {
Ok(Some(signal))
}
fn observe_signal_dequeues(_config: &<Self::GlobalState as GlobalTool>::Config) -> bool {
false
}
async fn handle_signal_dequeue<G: Guest<Self>>(
&self,
_guest: &mut G,
_dequeue: crate::SignalDequeue,
) -> Result<(), Errno> {
Ok(())
}
async fn handle_structured_signal_event<T: Guest<Self>>(
&self,
guest: &mut T,
event: SignalEvent,
) -> Result<Option<SignalEvent>, Errno> {
let signal = Signal::try_from(event.signal()).map_err(|_| Errno::ENOSYS)?;
match self.handle_signal_event(guest, signal).await? {
None => Ok(None),
Some(replacement) if replacement as i32 == event.signal() => Ok(Some(event)),
Some(_) => Err(Errno::ENOSYS),
}
}
async fn handle_timer_event<T: Guest<Self>>(&self, _guest: &mut T) {}
async fn on_exit_thread<G: GlobalRPC<Self::GlobalState>>(
&self,
_tid: Tid,
_global_state: &G,
_thread_state: Self::ThreadState,
_exit_status: ExitStatus,
) -> Result<(), Error> {
Ok(())
}
async fn on_exit_process<G: GlobalRPC<Self::GlobalState>>(
self,
_pid: Pid,
_global_state: &G,
_exit_status: ExitStatus,
) -> Result<(), Error> {
Ok(())
}
}
impl Tool for () {
type GlobalState = ();
type ThreadState = ();
fn subscriptions(_cfg: &()) -> Subscription {
Subscription::none()
}
}
#[async_trait]
pub trait GlobalRPC<G: GlobalTool>: Sync {
async fn send_rpc(&self, message: G::Request) -> G::Response;
fn config(&self) -> &G::Config;
}