use linux_api::signal::{sigaction, siginfo_t, sigset_t, stack_t, Signal};
use shadow_shmem::allocator::{ShMemBlock, ShMemBlockSerialized};
use vasi::VirtualAddressSpaceIndependent;
use vasi_sync::scmutex::SelfContainedMutex;
use crate::option::FfiOption;
use crate::HostId;
use crate::{
emulated_time::{AtomicEmulatedTime, EmulatedTime},
rootedcell::{refcell::RootedRefCell, Root},
simulation_time::SimulationTime,
};
macro_rules! assert_shmem_safe {
($t:ty, $testfnname:ident) => {
static_assertions::assert_impl_all!($t: Sync);
static_assertions::assert_impl_all!($t: VirtualAddressSpaceIndependent);
#[deny(improper_ctypes_definitions)]
unsafe extern "C-unwind" fn $testfnname(_: $t) {}
};
}
#[derive(VirtualAddressSpaceIndependent)]
#[repr(C)]
pub struct ManagerShmem {
pub log_start_time_micros: i64,
}
#[derive(VirtualAddressSpaceIndependent)]
#[repr(C)]
pub struct HostShmem {
pub host_id: HostId,
pub protected: SelfContainedMutex<HostShmemProtected>,
pub model_unblocked_syscall_latency: bool,
pub max_unapplied_cpu_latency: SimulationTime,
pub unblocked_syscall_latency: SimulationTime,
pub unblocked_vdso_latency: SimulationTime,
pub shadow_pid: libc::pid_t,
pub tsc_hz: u64,
pub sim_time: AtomicEmulatedTime,
pub shim_log_level: logger::LogLevel,
pub manager_shmem: ShMemBlockSerialized,
}
assert_shmem_safe!(HostShmem, _hostshmem_test_fn);
impl HostShmem {
#[allow(clippy::too_many_arguments)]
pub fn new(
host_id: HostId,
model_unblocked_syscall_latency: bool,
max_unapplied_cpu_latency: SimulationTime,
unblocked_syscall_latency: SimulationTime,
unblocked_vdso_latency: SimulationTime,
shadow_pid: libc::pid_t,
tsc_hz: u64,
shim_log_level: ::logger::LogLevel,
manager_shmem: &ShMemBlock<ManagerShmem>,
) -> Self {
Self {
host_id,
protected: SelfContainedMutex::new(HostShmemProtected {
host_id,
root: Root::new(),
unapplied_cpu_latency: SimulationTime::ZERO,
max_runahead_time: EmulatedTime::MIN,
}),
model_unblocked_syscall_latency,
max_unapplied_cpu_latency,
unblocked_syscall_latency,
unblocked_vdso_latency,
shadow_pid,
tsc_hz,
sim_time: AtomicEmulatedTime::new(EmulatedTime::MIN),
shim_log_level,
manager_shmem: manager_shmem.serialize(),
}
}
pub fn protected(&self) -> &SelfContainedMutex<HostShmemProtected> {
&self.protected
}
}
#[derive(VirtualAddressSpaceIndependent)]
#[repr(C)]
pub struct HostShmemProtected {
pub host_id: HostId,
pub root: Root,
pub unapplied_cpu_latency: SimulationTime,
pub max_runahead_time: EmulatedTime,
}
#[derive(VirtualAddressSpaceIndependent)]
#[repr(C)]
pub struct ProcessShmem {
host_id: HostId,
pub host_shmem: ShMemBlockSerialized,
pub strace_fd: FfiOption<libc::c_int>,
pub protected: RootedRefCell<ProcessShmemProtected>,
}
assert_shmem_safe!(ProcessShmem, _test_processshmem_fn);
impl ProcessShmem {
pub fn new(
host_root: &Root,
host_shmem: ShMemBlockSerialized,
host_id: HostId,
strace_fd: Option<libc::c_int>,
) -> Self {
Self {
host_id,
host_shmem,
strace_fd: strace_fd.into(),
protected: RootedRefCell::new(
host_root,
ProcessShmemProtected {
host_id,
pending_signals: sigset_t::EMPTY,
pending_standard_siginfos: [siginfo_t::default();
Signal::STANDARD_MAX.as_i32() as usize],
signal_actions: [sigaction::default(); Signal::MAX.as_i32() as usize],
},
),
}
}
}
#[derive(VirtualAddressSpaceIndependent)]
#[repr(C)]
pub struct ProcessShmemProtected {
pub host_id: HostId,
pub pending_signals: sigset_t,
#[unsafe_assume_virtual_address_space_independent]
pending_standard_siginfos: [siginfo_t; Signal::STANDARD_MAX.as_i32() as usize],
#[unsafe_assume_virtual_address_space_independent]
signal_actions: [sigaction; Signal::MAX.as_i32() as usize],
}
fn signal_idx(signal: Signal) -> usize {
(i32::from(signal) - 1) as usize
}
impl ProcessShmemProtected {
pub fn pending_standard_siginfo(&self, signal: Signal) -> Option<&siginfo_t> {
if self.pending_signals.has(signal) {
Some(&self.pending_standard_siginfos[signal_idx(signal)])
} else {
None
}
}
pub fn set_pending_standard_siginfo(&mut self, signal: Signal, info: &siginfo_t) {
assert!(self.pending_signals.has(signal));
self.pending_standard_siginfos[signal_idx(signal)] = *info;
}
pub unsafe fn clone_signal_actions(&mut self, src: &Self) {
self.signal_actions = src.signal_actions
}
pub unsafe fn signal_action(&self, signal: Signal) -> &sigaction {
&self.signal_actions[signal_idx(signal)]
}
pub unsafe fn signal_action_mut(&mut self, signal: Signal) -> &mut sigaction {
&mut self.signal_actions[signal_idx(signal)]
}
pub fn clear_pending_signals(&mut self) {
self.pending_signals = sigset_t::EMPTY;
}
pub fn take_pending_unblocked_signal(
&mut self,
thread: &ThreadShmemProtected,
) -> Option<(Signal, siginfo_t)> {
let pending_unblocked_signals = self.pending_signals & !thread.blocked_signals;
if pending_unblocked_signals.is_empty() {
None
} else {
let signal = pending_unblocked_signals.lowest().unwrap();
let info = *self.pending_standard_siginfo(signal).unwrap();
self.pending_signals.del(signal);
Some((signal, info))
}
}
}
#[derive(VirtualAddressSpaceIndependent)]
#[repr(C)]
pub struct ThreadShmem {
pub host_id: HostId,
pub tid: libc::pid_t,
pub protected: RootedRefCell<ThreadShmemProtected>,
}
assert_shmem_safe!(ThreadShmem, _test_threadshmem_fn);
impl ThreadShmem {
pub fn new(host: &HostShmemProtected, tid: libc::pid_t) -> Self {
Self {
host_id: host.host_id,
tid,
protected: RootedRefCell::new(
&host.root,
ThreadShmemProtected {
host_id: host.host_id,
pending_signals: sigset_t::EMPTY,
pending_standard_siginfos: [siginfo_t::default();
Signal::STANDARD_MAX.as_i32() as usize],
blocked_signals: sigset_t::EMPTY,
sigaltstack: StackWrapper(stack_t {
ss_sp: std::ptr::null_mut(),
ss_flags: libc::SS_DISABLE,
ss_size: 0,
}),
},
),
}
}
pub fn clone(&self, root: &Root) -> Self {
Self {
host_id: self.host_id,
tid: self.tid,
protected: RootedRefCell::new(root, *self.protected.borrow(root)),
}
}
}
#[derive(VirtualAddressSpaceIndependent, Copy, Clone)]
#[repr(C)]
pub struct ThreadShmemProtected {
pub host_id: HostId,
pub pending_signals: sigset_t,
#[unsafe_assume_virtual_address_space_independent]
pending_standard_siginfos: [siginfo_t; Signal::STANDARD_MAX.as_i32() as usize],
pub blocked_signals: sigset_t,
sigaltstack: StackWrapper,
}
impl ThreadShmemProtected {
pub fn pending_standard_siginfo(&self, signal: Signal) -> Option<&siginfo_t> {
if self.pending_signals.has(signal) {
Some(&self.pending_standard_siginfos[signal_idx(signal)])
} else {
None
}
}
pub fn set_pending_standard_siginfo(&mut self, signal: Signal, info: &siginfo_t) {
assert!(self.pending_signals.has(signal));
self.pending_standard_siginfos[signal_idx(signal)] = *info;
}
pub unsafe fn sigaltstack(&self) -> &stack_t {
&self.sigaltstack.0
}
pub unsafe fn sigaltstack_mut(&mut self) -> &mut stack_t {
&mut self.sigaltstack.0
}
pub fn take_pending_unblocked_signal(&mut self) -> Option<(Signal, siginfo_t)> {
let pending_unblocked_signals = self.pending_signals & !self.blocked_signals;
if pending_unblocked_signals.is_empty() {
None
} else {
let signal = pending_unblocked_signals.lowest().unwrap();
let info = *self.pending_standard_siginfo(signal).unwrap();
self.pending_signals.del(signal);
Some((signal, info))
}
}
}
#[derive(Copy, Clone)]
#[repr(transparent)]
struct StackWrapper(stack_t);
unsafe impl Send for StackWrapper {}
unsafe impl VirtualAddressSpaceIndependent for StackWrapper {}
pub fn take_pending_unblocked_signal(
lock: &HostShmemProtected,
process: &ProcessShmem,
thread: &ThreadShmem,
) -> Option<(Signal, siginfo_t)> {
let mut thread_protected = thread.protected.borrow_mut(&lock.root);
thread_protected
.take_pending_unblocked_signal()
.or_else(|| {
let mut process_protected = process.protected.borrow_mut(&lock.root);
process_protected.take_pending_unblocked_signal(&thread_protected)
})
}
pub mod export {
use std::sync::atomic::Ordering;
use bytemuck::TransparentWrapper;
use linux_api::signal::{linux_sigaction, linux_sigset_t, linux_stack_t};
use vasi_sync::scmutex::SelfContainedMutexGuard;
use super::*;
use crate::{emulated_time::CEmulatedTime, simulation_time::CSimulationTime};
pub type ShimShmemManager = ManagerShmem;
pub type ShimShmemHost = HostShmem;
pub type ShimShmemHostLock = HostShmemProtected;
pub type ShimShmemProcess = ProcessShmem;
pub type ShimShmemThread = ThreadShmem;
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmemhost_lock(
host: *const ShimShmemHost,
) -> *mut ShimShmemHostLock {
let host = unsafe { host.as_ref().unwrap() };
let mut guard: SelfContainedMutexGuard<ShimShmemHostLock> = host.protected().lock();
let lock: &mut ShimShmemHostLock = &mut guard;
let lock = std::ptr::from_mut(lock);
guard.disconnect();
lock
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmemhost_unlock(
host: *const ShimShmemHost,
lock: *mut *mut ShimShmemHostLock,
) {
let host = unsafe { host.as_ref().unwrap() };
let guard = SelfContainedMutexGuard::reconnect(&host.protected);
assert_eq!(host.host_id, guard.host_id);
let p_lock = unsafe { lock.as_mut().unwrap() };
*p_lock = std::ptr::null_mut();
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_getShadowPid(
host_mem: *const ShimShmemHost,
) -> libc::pid_t {
let host_mem = unsafe { host_mem.as_ref().unwrap() };
host_mem.shadow_pid
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_getTscHz(host_mem: *const ShimShmemHost) -> u64 {
let host_mem = unsafe { host_mem.as_ref().unwrap() };
host_mem.tsc_hz
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_getLogLevel(
host_mem: *const ShimShmemHost,
) -> ::logger::LogLevel {
let host_mem = unsafe { host_mem.as_ref().unwrap() };
host_mem.shim_log_level
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_getEmulatedTime(
host_mem: *const ShimShmemHost,
) -> CEmulatedTime {
let host_mem = unsafe { host_mem.as_ref().unwrap() };
EmulatedTime::to_c_emutime(Some(host_mem.sim_time.load(Ordering::Relaxed)))
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_setEmulatedTime(
host_mem: *const ShimShmemHost,
t: CEmulatedTime,
) {
let host_mem = unsafe { host_mem.as_ref().unwrap() };
host_mem
.sim_time
.store(EmulatedTime::from_c_emutime(t).unwrap(), Ordering::Relaxed);
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_getMaxRunaheadTime(
host_mem: *const ShimShmemHostLock,
) -> CEmulatedTime {
let host_mem = unsafe { host_mem.as_ref().unwrap() };
EmulatedTime::to_c_emutime(Some(host_mem.max_runahead_time))
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_setMaxRunaheadTime(
host_mem: *mut ShimShmemHostLock,
t: CEmulatedTime,
) {
let host_mem = unsafe { host_mem.as_mut().unwrap() };
host_mem.max_runahead_time = EmulatedTime::from_c_emutime(t).unwrap();
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_getProcessStraceFd(
process: *const ShimShmemProcess,
) -> libc::c_int {
let process_mem = unsafe { process.as_ref().unwrap() };
process_mem.strace_fd.unwrap_or(-1)
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_getSignalAction(
lock: *const ShimShmemHostLock,
process: *const ShimShmemProcess,
sig: i32,
) -> linux_sigaction {
let process_mem = unsafe { process.as_ref().unwrap() };
let lock = unsafe { lock.as_ref().unwrap() };
let protected = process_mem.protected.borrow(&lock.root);
unsafe { sigaction::peel(*protected.signal_action(Signal::try_from(sig).unwrap())) }
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_setSignalAction(
lock: *const ShimShmemHostLock,
process: *const ShimShmemProcess,
sig: i32,
action: *const linux_sigaction,
) {
let process_mem = unsafe { process.as_ref().unwrap() };
let lock = unsafe { lock.as_ref().unwrap() };
let action = sigaction::wrap_ref(unsafe { action.as_ref().unwrap() });
let mut protected = process_mem.protected.borrow_mut(&lock.root);
unsafe { *protected.signal_action_mut(Signal::try_from(sig).unwrap()) = *action };
}
#[no_mangle]
pub extern "C-unwind" fn shimshmemthread_size() -> usize {
std::mem::size_of::<ThreadShmem>()
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_getThreadId(
thread: *const ShimShmemThread,
) -> libc::pid_t {
let thread_mem = unsafe { thread.as_ref().unwrap() };
thread_mem.tid
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_getBlockedSignals(
lock: *const ShimShmemHostLock,
thread: *const ShimShmemThread,
) -> linux_sigset_t {
let thread_mem = unsafe { thread.as_ref().unwrap() };
let lock = unsafe { lock.as_ref().unwrap() };
let protected = thread_mem.protected.borrow(&lock.root);
sigset_t::peel(protected.blocked_signals)
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_setBlockedSignals(
lock: *const ShimShmemHostLock,
thread: *const ShimShmemThread,
s: linux_sigset_t,
) {
let thread_mem = unsafe { thread.as_ref().unwrap() };
let lock = unsafe { lock.as_ref().unwrap() };
let mut protected = thread_mem.protected.borrow_mut(&lock.root);
protected.blocked_signals = sigset_t::wrap(s);
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_getSigAltStack(
lock: *const ShimShmemHostLock,
thread: *const ShimShmemThread,
) -> linux_stack_t {
let thread_mem = unsafe { thread.as_ref().unwrap() };
let lock = unsafe { lock.as_ref().unwrap() };
let protected = thread_mem.protected.borrow(&lock.root);
*unsafe { protected.sigaltstack() }
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_setSigAltStack(
lock: *const ShimShmemHostLock,
thread: *const ShimShmemThread,
stack: linux_stack_t,
) {
let thread_mem = unsafe { thread.as_ref().unwrap() };
let lock = unsafe { lock.as_ref().unwrap() };
let mut protected = thread_mem.protected.borrow_mut(&lock.root);
*unsafe { protected.sigaltstack_mut() } = stack;
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_incrementUnappliedCpuLatency(
lock: *mut ShimShmemHostLock,
dt: CSimulationTime,
) {
let lock = unsafe { lock.as_mut().unwrap() };
lock.unapplied_cpu_latency += SimulationTime::from_c_simtime(dt).unwrap();
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_getUnappliedCpuLatency(
lock: *const ShimShmemHostLock,
) -> CSimulationTime {
let lock = unsafe { lock.as_ref().unwrap() };
SimulationTime::to_c_simtime(Some(lock.unapplied_cpu_latency))
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_resetUnappliedCpuLatency(
lock: *mut ShimShmemHostLock,
) {
let lock = unsafe { lock.as_mut().unwrap() };
lock.unapplied_cpu_latency = SimulationTime::ZERO;
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_getModelUnblockedSyscallLatency(
host: *const ShimShmemHost,
) -> bool {
let host = unsafe { host.as_ref().unwrap() };
host.model_unblocked_syscall_latency
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_maxUnappliedCpuLatency(
host: *const ShimShmemHost,
) -> CSimulationTime {
let host = unsafe { host.as_ref().unwrap() };
SimulationTime::to_c_simtime(Some(host.max_unapplied_cpu_latency))
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_unblockedSyscallLatency(
host: *const ShimShmemHost,
) -> CSimulationTime {
let host = unsafe { host.as_ref().unwrap() };
SimulationTime::to_c_simtime(Some(host.unblocked_syscall_latency))
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_unblockedVdsoLatency(
host: *const ShimShmemHost,
) -> CSimulationTime {
let host = unsafe { host.as_ref().unwrap() };
SimulationTime::to_c_simtime(Some(host.unblocked_vdso_latency))
}
#[no_mangle]
pub unsafe extern "C-unwind" fn shimshmem_getLoggingStartTime(
manager: *const ShimShmemManager,
) -> i64 {
let manager = unsafe { manager.as_ref().unwrap() };
manager.log_start_time_micros
}
}