1use linux_api::signal::{Signal, sigaction, siginfo_t, sigset_t, stack_t};
2use shadow_shmem::allocator::{ShMemBlock, ShMemBlockSerialized};
3use vasi::VirtualAddressSpaceIndependent;
4use vasi_sync::scmutex::SelfContainedMutex;
5
6use crate::HostId;
7use crate::option::FfiOption;
8use crate::{
9 emulated_time::{AtomicEmulatedTime, EmulatedTime},
10 rootedcell::{Root, refcell::RootedRefCell},
11 simulation_time::SimulationTime,
12};
13
14macro_rules! assert_shmem_safe {
16 ($t:ty, $testfnname:ident) => {
17 static_assertions::assert_impl_all!($t: Sync);
20
21 static_assertions::assert_impl_all!($t: VirtualAddressSpaceIndependent);
24
25 #[deny(improper_ctypes_definitions)]
45 unsafe extern "C-unwind" fn $testfnname(_: $t) {}
46 };
47}
48
49#[derive(VirtualAddressSpaceIndependent)]
50#[repr(C)]
51pub struct NativePreemptionConfig {
52 pub native_duration: linux_api::time::kernel_old_timeval,
58 pub sim_duration: SimulationTime,
61}
62
63#[derive(VirtualAddressSpaceIndependent)]
64#[repr(C)]
65pub struct ManagerShmem {
66 pub log_start_time_micros: i64,
67 pub native_preemption_config: FfiOption<NativePreemptionConfig>,
68}
69
70#[derive(VirtualAddressSpaceIndependent)]
71#[repr(C)]
72pub struct HostShmem {
73 pub host_id: HostId,
74
75 pub protected: SelfContainedMutex<HostShmemProtected>,
76
77 pub model_unblocked_syscall_latency: bool,
80
81 pub max_unapplied_cpu_latency: SimulationTime,
86
87 pub unblocked_syscall_latency: SimulationTime,
92
93 pub unblocked_vdso_latency: SimulationTime,
98
99 pub shadow_pid: libc::pid_t,
101
102 pub tsc_hz: u64,
104
105 pub sim_time: AtomicEmulatedTime,
107
108 pub shim_log_level: logger::LogLevel,
109
110 pub manager_shmem: ShMemBlockSerialized,
111}
112assert_shmem_safe!(HostShmem, _hostshmem_test_fn);
113
114impl HostShmem {
115 #[allow(clippy::too_many_arguments)]
116 pub fn new(
117 host_id: HostId,
118 model_unblocked_syscall_latency: bool,
119 max_unapplied_cpu_latency: SimulationTime,
120 unblocked_syscall_latency: SimulationTime,
121 unblocked_vdso_latency: SimulationTime,
122 shadow_pid: libc::pid_t,
123 tsc_hz: u64,
124 shim_log_level: ::logger::LogLevel,
125 manager_shmem: &ShMemBlock<ManagerShmem>,
126 ) -> Self {
127 Self {
128 host_id,
129 protected: SelfContainedMutex::new(HostShmemProtected {
130 host_id,
131 root: Root::new(),
132 unapplied_cpu_latency: SimulationTime::ZERO,
133 max_runahead_time: EmulatedTime::MIN,
134 }),
135 model_unblocked_syscall_latency,
136 max_unapplied_cpu_latency,
137 unblocked_syscall_latency,
138 unblocked_vdso_latency,
139 shadow_pid,
140 tsc_hz,
141 sim_time: AtomicEmulatedTime::new(EmulatedTime::MIN),
142 shim_log_level,
143 manager_shmem: manager_shmem.serialize(),
144 }
145 }
146
147 pub fn protected(&self) -> &SelfContainedMutex<HostShmemProtected> {
148 &self.protected
149 }
150}
151
152#[derive(VirtualAddressSpaceIndependent)]
153#[repr(C)]
154pub struct HostShmemProtected {
155 pub host_id: HostId,
156
157 pub root: Root,
158
159 pub unapplied_cpu_latency: SimulationTime,
161
162 pub max_runahead_time: EmulatedTime,
165}
166
167#[derive(VirtualAddressSpaceIndependent)]
168#[repr(C)]
169pub struct ProcessShmem {
170 host_id: HostId,
171
172 pub host_shmem: ShMemBlockSerialized,
174 pub strace_fd: FfiOption<libc::c_int>,
175
176 pub protected: RootedRefCell<ProcessShmemProtected>,
177}
178assert_shmem_safe!(ProcessShmem, _test_processshmem_fn);
179
180impl ProcessShmem {
181 pub fn new(
182 host_root: &Root,
183 host_shmem: ShMemBlockSerialized,
184 host_id: HostId,
185 strace_fd: Option<libc::c_int>,
186 ) -> Self {
187 Self {
188 host_id,
189 host_shmem,
190 strace_fd: strace_fd.into(),
191 protected: RootedRefCell::new(
192 host_root,
193 ProcessShmemProtected {
194 host_id,
195 pending_signals: sigset_t::EMPTY,
196 pending_standard_siginfos: [siginfo_t::default();
197 Signal::STANDARD_MAX.as_i32() as usize],
198 signal_actions: [sigaction::default(); Signal::MAX.as_i32() as usize],
199 },
200 ),
201 }
202 }
203}
204
205#[derive(VirtualAddressSpaceIndependent)]
206#[repr(C)]
207pub struct ProcessShmemProtected {
208 pub host_id: HostId,
209
210 pub pending_signals: sigset_t,
212
213 #[unsafe_assume_virtual_address_space_independent]
217 pending_standard_siginfos: [siginfo_t; Signal::STANDARD_MAX.as_i32() as usize],
218
219 #[unsafe_assume_virtual_address_space_independent]
226 signal_actions: [sigaction; Signal::MAX.as_i32() as usize],
227}
228
229fn signal_idx(signal: Signal) -> usize {
231 (i32::from(signal) - 1) as usize
232}
233
234impl ProcessShmemProtected {
235 pub fn pending_standard_siginfo(&self, signal: Signal) -> Option<&siginfo_t> {
236 if self.pending_signals.has(signal) {
237 Some(&self.pending_standard_siginfos[signal_idx(signal)])
238 } else {
239 None
240 }
241 }
242
243 pub fn set_pending_standard_siginfo(&mut self, signal: Signal, info: &siginfo_t) {
244 assert!(self.pending_signals.has(signal));
245 self.pending_standard_siginfos[signal_idx(signal)] = *info;
246 }
247
248 pub unsafe fn clone_signal_actions(&mut self, src: &Self) {
253 self.signal_actions = src.signal_actions
254 }
255
256 pub unsafe fn signal_action(&self, signal: Signal) -> &sigaction {
262 &self.signal_actions[signal_idx(signal)]
263 }
264
265 pub unsafe fn signal_action_mut(&mut self, signal: Signal) -> &mut sigaction {
271 &mut self.signal_actions[signal_idx(signal)]
272 }
273
274 pub fn clear_pending_signals(&mut self) {
276 self.pending_signals = sigset_t::EMPTY;
277 }
278
279 pub fn take_pending_unblocked_signal(
280 &mut self,
281 thread: &ThreadShmemProtected,
282 ) -> Option<(Signal, siginfo_t)> {
283 let pending_unblocked_signals = self.pending_signals & !thread.blocked_signals;
284 if pending_unblocked_signals.is_empty() {
285 None
286 } else {
287 let signal = pending_unblocked_signals.lowest().unwrap();
288 let info = *self.pending_standard_siginfo(signal).unwrap();
289 self.pending_signals.del(signal);
290 Some((signal, info))
291 }
292 }
293}
294
295#[derive(VirtualAddressSpaceIndependent)]
296#[repr(C)]
297pub struct ThreadShmem {
298 pub host_id: HostId,
299 pub tid: libc::pid_t,
300
301 pub protected: RootedRefCell<ThreadShmemProtected>,
302}
303assert_shmem_safe!(ThreadShmem, _test_threadshmem_fn);
304
305impl ThreadShmem {
306 pub fn new(host: &HostShmemProtected, tid: libc::pid_t) -> Self {
307 Self {
308 host_id: host.host_id,
309 tid,
310 protected: RootedRefCell::new(
311 &host.root,
312 ThreadShmemProtected {
313 host_id: host.host_id,
314 pending_signals: sigset_t::EMPTY,
315 pending_standard_siginfos: [siginfo_t::default();
316 Signal::STANDARD_MAX.as_i32() as usize],
317 blocked_signals: sigset_t::EMPTY,
318 sigaltstack: StackWrapper(stack_t {
319 ss_sp: std::ptr::null_mut(),
320 ss_flags: libc::SS_DISABLE,
321 ss_size: 0,
322 }),
323 },
324 ),
325 }
326 }
327
328 pub fn clone(&self, root: &Root) -> Self {
331 Self {
332 host_id: self.host_id,
333 tid: self.tid,
334 protected: RootedRefCell::new(root, *self.protected.borrow(root)),
335 }
336 }
337}
338
339#[derive(VirtualAddressSpaceIndependent, Copy, Clone)]
340#[repr(C)]
341pub struct ThreadShmemProtected {
342 pub host_id: HostId,
343
344 pub pending_signals: sigset_t,
346
347 #[unsafe_assume_virtual_address_space_independent]
351 pending_standard_siginfos: [siginfo_t; Signal::STANDARD_MAX.as_i32() as usize],
352
353 pub blocked_signals: sigset_t,
357
358 sigaltstack: StackWrapper,
360}
361
362impl ThreadShmemProtected {
363 pub fn pending_standard_siginfo(&self, signal: Signal) -> Option<&siginfo_t> {
364 if self.pending_signals.has(signal) {
365 Some(&self.pending_standard_siginfos[signal_idx(signal)])
366 } else {
367 None
368 }
369 }
370
371 pub fn set_pending_standard_siginfo(&mut self, signal: Signal, info: &siginfo_t) {
372 assert!(self.pending_signals.has(signal));
373 self.pending_standard_siginfos[signal_idx(signal)] = *info;
374 }
375
376 pub unsafe fn sigaltstack(&self) -> &stack_t {
381 &self.sigaltstack.0
382 }
383
384 pub unsafe fn sigaltstack_mut(&mut self) -> &mut stack_t {
390 &mut self.sigaltstack.0
391 }
392
393 pub fn take_pending_unblocked_signal(&mut self) -> Option<(Signal, siginfo_t)> {
394 let pending_unblocked_signals = self.pending_signals & !self.blocked_signals;
395 if pending_unblocked_signals.is_empty() {
396 None
397 } else {
398 let signal = pending_unblocked_signals.lowest().unwrap();
399 let info = *self.pending_standard_siginfo(signal).unwrap();
400 self.pending_signals.del(signal);
401 Some((signal, info))
402 }
403 }
404}
405
406#[derive(Copy, Clone)]
407#[repr(transparent)]
408struct StackWrapper(stack_t);
409
410unsafe impl Send for StackWrapper {}
413
414unsafe impl VirtualAddressSpaceIndependent for StackWrapper {}
417
418pub fn take_pending_unblocked_signal(
420 lock: &HostShmemProtected,
421 process: &ProcessShmem,
422 thread: &ThreadShmem,
423) -> Option<(Signal, siginfo_t)> {
424 let mut thread_protected = thread.protected.borrow_mut(&lock.root);
425 thread_protected
426 .take_pending_unblocked_signal()
427 .or_else(|| {
428 let mut process_protected = process.protected.borrow_mut(&lock.root);
429 process_protected.take_pending_unblocked_signal(&thread_protected)
430 })
431}
432
433pub mod export {
434 use std::sync::atomic::Ordering;
435
436 use bytemuck::TransparentWrapper;
437 use linux_api::signal::{linux_sigaction, linux_sigset_t, linux_stack_t};
438 use vasi_sync::scmutex::SelfContainedMutexGuard;
439
440 use super::*;
441 use crate::{emulated_time::CEmulatedTime, simulation_time::CSimulationTime};
442
443 pub type ShimShmemManager = ManagerShmem;
446 pub type ShimShmemHost = HostShmem;
447 pub type ShimShmemHostLock = HostShmemProtected;
448 pub type ShimShmemProcess = ProcessShmem;
449 pub type ShimShmemThread = ThreadShmem;
450
451 #[unsafe(no_mangle)]
455 pub unsafe extern "C-unwind" fn shimshmemhost_lock(
456 host: *const ShimShmemHost,
457 ) -> *mut ShimShmemHostLock {
458 let host = unsafe { host.as_ref().unwrap() };
459 let mut guard: SelfContainedMutexGuard<ShimShmemHostLock> = host.protected().lock();
460 let lock: &mut ShimShmemHostLock = &mut guard;
461 let lock = std::ptr::from_mut(lock);
462 guard.disconnect();
463 lock
464 }
465
466 #[unsafe(no_mangle)]
470 pub unsafe extern "C-unwind" fn shimshmemhost_unlock(
471 host: *const ShimShmemHost,
472 lock: *mut *mut ShimShmemHostLock,
473 ) {
474 let host = unsafe { host.as_ref().unwrap() };
475 let guard = SelfContainedMutexGuard::reconnect(&host.protected);
476 assert_eq!(host.host_id, guard.host_id);
477
478 let p_lock = unsafe { lock.as_mut().unwrap() };
479 *p_lock = std::ptr::null_mut();
480 }
481
482 #[unsafe(no_mangle)]
486 pub unsafe extern "C-unwind" fn shimshmem_getShadowPid(
487 host_mem: *const ShimShmemHost,
488 ) -> libc::pid_t {
489 let host_mem = unsafe { host_mem.as_ref().unwrap() };
490 host_mem.shadow_pid
491 }
492
493 #[unsafe(no_mangle)]
497 pub unsafe extern "C-unwind" fn shimshmem_getTscHz(host_mem: *const ShimShmemHost) -> u64 {
498 let host_mem = unsafe { host_mem.as_ref().unwrap() };
499 host_mem.tsc_hz
500 }
501
502 #[unsafe(no_mangle)]
506 pub unsafe extern "C-unwind" fn shimshmem_getLogLevel(
507 host_mem: *const ShimShmemHost,
508 ) -> ::logger::LogLevel {
509 let host_mem = unsafe { host_mem.as_ref().unwrap() };
510 host_mem.shim_log_level
511 }
512
513 #[unsafe(no_mangle)]
517 pub unsafe extern "C-unwind" fn shimshmem_getEmulatedTime(
518 host_mem: *const ShimShmemHost,
519 ) -> CEmulatedTime {
520 let host_mem = unsafe { host_mem.as_ref().unwrap() };
521 EmulatedTime::to_c_emutime(Some(host_mem.sim_time.load(Ordering::Relaxed)))
522 }
523
524 #[unsafe(no_mangle)]
528 pub unsafe extern "C-unwind" fn shimshmem_setEmulatedTime(
529 host_mem: *const ShimShmemHost,
530 t: CEmulatedTime,
531 ) {
532 let host_mem = unsafe { host_mem.as_ref().unwrap() };
533 host_mem
534 .sim_time
535 .store(EmulatedTime::from_c_emutime(t).unwrap(), Ordering::Relaxed);
536 }
537
538 #[unsafe(no_mangle)]
542 pub unsafe extern "C-unwind" fn shimshmem_getMaxRunaheadTime(
543 host_mem: *const ShimShmemHostLock,
544 ) -> CEmulatedTime {
545 let host_mem = unsafe { host_mem.as_ref().unwrap() };
546 EmulatedTime::to_c_emutime(Some(host_mem.max_runahead_time))
547 }
548
549 #[unsafe(no_mangle)]
553 pub unsafe extern "C-unwind" fn shimshmem_setMaxRunaheadTime(
554 host_mem: *mut ShimShmemHostLock,
555 t: CEmulatedTime,
556 ) {
557 let host_mem = unsafe { host_mem.as_mut().unwrap() };
558 host_mem.max_runahead_time = EmulatedTime::from_c_emutime(t).unwrap();
559 }
560
561 #[unsafe(no_mangle)]
565 pub unsafe extern "C-unwind" fn shimshmem_getProcessStraceFd(
566 process: *const ShimShmemProcess,
567 ) -> libc::c_int {
568 let process_mem = unsafe { process.as_ref().unwrap() };
569 process_mem.strace_fd.unwrap_or(-1)
570 }
571
572 #[unsafe(no_mangle)]
576 pub unsafe extern "C-unwind" fn shimshmem_getSignalAction(
577 lock: *const ShimShmemHostLock,
578 process: *const ShimShmemProcess,
579 sig: i32,
580 ) -> linux_sigaction {
581 let process_mem = unsafe { process.as_ref().unwrap() };
582 let lock = unsafe { lock.as_ref().unwrap() };
583 let protected = process_mem.protected.borrow(&lock.root);
584 unsafe { sigaction::peel(*protected.signal_action(Signal::try_from(sig).unwrap())) }
585 }
586
587 #[unsafe(no_mangle)]
591 pub unsafe extern "C-unwind" fn shimshmem_setSignalAction(
592 lock: *const ShimShmemHostLock,
593 process: *const ShimShmemProcess,
594 sig: i32,
595 action: *const linux_sigaction,
596 ) {
597 let process_mem = unsafe { process.as_ref().unwrap() };
598 let lock = unsafe { lock.as_ref().unwrap() };
599 let action = sigaction::wrap_ref(unsafe { action.as_ref().unwrap() });
600 let mut protected = process_mem.protected.borrow_mut(&lock.root);
601 unsafe { *protected.signal_action_mut(Signal::try_from(sig).unwrap()) = *action };
602 }
603
604 #[unsafe(no_mangle)]
605 pub extern "C-unwind" fn shimshmemthread_size() -> usize {
606 std::mem::size_of::<ThreadShmem>()
607 }
608
609 #[unsafe(no_mangle)]
613 pub unsafe extern "C-unwind" fn shimshmem_getThreadId(
614 thread: *const ShimShmemThread,
615 ) -> libc::pid_t {
616 let thread_mem = unsafe { thread.as_ref().unwrap() };
617 thread_mem.tid
618 }
619
620 #[unsafe(no_mangle)]
624 pub unsafe extern "C-unwind" fn shimshmem_getBlockedSignals(
625 lock: *const ShimShmemHostLock,
626 thread: *const ShimShmemThread,
627 ) -> linux_sigset_t {
628 let thread_mem = unsafe { thread.as_ref().unwrap() };
629 let lock = unsafe { lock.as_ref().unwrap() };
630 let protected = thread_mem.protected.borrow(&lock.root);
631 sigset_t::peel(protected.blocked_signals)
632 }
633
634 #[unsafe(no_mangle)]
640 pub unsafe extern "C-unwind" fn shimshmem_setBlockedSignals(
641 lock: *const ShimShmemHostLock,
642 thread: *const ShimShmemThread,
643 s: linux_sigset_t,
644 ) {
645 let thread_mem = unsafe { thread.as_ref().unwrap() };
646 let lock = unsafe { lock.as_ref().unwrap() };
647 let mut protected = thread_mem.protected.borrow_mut(&lock.root);
648 protected.blocked_signals = sigset_t::wrap(s);
649 }
650
651 #[unsafe(no_mangle)]
657 pub unsafe extern "C-unwind" fn shimshmem_getSigAltStack(
658 lock: *const ShimShmemHostLock,
659 thread: *const ShimShmemThread,
660 ) -> linux_stack_t {
661 let thread_mem = unsafe { thread.as_ref().unwrap() };
662 let lock = unsafe { lock.as_ref().unwrap() };
663 let protected = thread_mem.protected.borrow(&lock.root);
664 *unsafe { protected.sigaltstack() }
665 }
666
667 #[unsafe(no_mangle)]
673 pub unsafe extern "C-unwind" fn shimshmem_setSigAltStack(
674 lock: *const ShimShmemHostLock,
675 thread: *const ShimShmemThread,
676 stack: linux_stack_t,
677 ) {
678 let thread_mem = unsafe { thread.as_ref().unwrap() };
679 let lock = unsafe { lock.as_ref().unwrap() };
680 let mut protected = thread_mem.protected.borrow_mut(&lock.root);
681 *unsafe { protected.sigaltstack_mut() } = stack;
682 }
683
684 #[unsafe(no_mangle)]
688 pub unsafe extern "C-unwind" fn shimshmem_incrementUnappliedCpuLatency(
689 lock: *mut ShimShmemHostLock,
690 dt: CSimulationTime,
691 ) {
692 let lock = unsafe { lock.as_mut().unwrap() };
693 lock.unapplied_cpu_latency += SimulationTime::from_c_simtime(dt).unwrap();
694 }
695
696 #[unsafe(no_mangle)]
700 pub unsafe extern "C-unwind" fn shimshmem_getUnappliedCpuLatency(
701 lock: *const ShimShmemHostLock,
702 ) -> CSimulationTime {
703 let lock = unsafe { lock.as_ref().unwrap() };
704 SimulationTime::to_c_simtime(Some(lock.unapplied_cpu_latency))
705 }
706
707 #[unsafe(no_mangle)]
711 pub unsafe extern "C-unwind" fn shimshmem_resetUnappliedCpuLatency(
712 lock: *mut ShimShmemHostLock,
713 ) {
714 let lock = unsafe { lock.as_mut().unwrap() };
715 lock.unapplied_cpu_latency = SimulationTime::ZERO;
716 }
717
718 #[unsafe(no_mangle)]
724 pub unsafe extern "C-unwind" fn shimshmem_getModelUnblockedSyscallLatency(
725 host: *const ShimShmemHost,
726 ) -> bool {
727 let host = unsafe { host.as_ref().unwrap() };
728 host.model_unblocked_syscall_latency
729 }
730
731 #[unsafe(no_mangle)]
738 pub unsafe extern "C-unwind" fn shimshmem_maxUnappliedCpuLatency(
739 host: *const ShimShmemHost,
740 ) -> CSimulationTime {
741 let host = unsafe { host.as_ref().unwrap() };
742 SimulationTime::to_c_simtime(Some(host.max_unapplied_cpu_latency))
743 }
744
745 #[unsafe(no_mangle)]
751 pub unsafe extern "C-unwind" fn shimshmem_unblockedSyscallLatency(
752 host: *const ShimShmemHost,
753 ) -> CSimulationTime {
754 let host = unsafe { host.as_ref().unwrap() };
755 SimulationTime::to_c_simtime(Some(host.unblocked_syscall_latency))
756 }
757
758 #[unsafe(no_mangle)]
764 pub unsafe extern "C-unwind" fn shimshmem_unblockedVdsoLatency(
765 host: *const ShimShmemHost,
766 ) -> CSimulationTime {
767 let host = unsafe { host.as_ref().unwrap() };
768 SimulationTime::to_c_simtime(Some(host.unblocked_vdso_latency))
769 }
770
771 #[unsafe(no_mangle)]
777 pub unsafe extern "C-unwind" fn shimshmem_getLoggingStartTime(
778 manager: *const ShimShmemManager,
779 ) -> i64 {
780 let manager = unsafe { manager.as_ref().unwrap() };
781 manager.log_start_time_micros
782 }
783}