shadow_rs/host/syscall/handler/
time.rs
1use linux_api::errno::Errno;
2use linux_api::time::{ClockId, ClockNanosleepFlags, ITimerId};
3use log::*;
4use shadow_shim_helper_rs::emulated_time::EmulatedTime;
5use shadow_shim_helper_rs::simulation_time::SimulationTime;
6use shadow_shim_helper_rs::syscall_types::ForeignPtr;
7
8use crate::core::worker::Worker;
9use crate::host::syscall::handler::{SyscallContext, SyscallHandler};
10use crate::host::syscall::types::SyscallError;
11use crate::host::timer::Timer;
12
13fn kernel_old_itimerval_from_timer(timer: &Timer) -> linux_api::time::kernel_old_itimerval {
14 linux_api::time::kernel_old_itimerval {
15 it_interval: timer
16 .expire_interval()
17 .unwrap_or(SimulationTime::ZERO)
18 .try_into()
19 .unwrap(),
20 it_value: timer
21 .remaining_time()
22 .unwrap_or(SimulationTime::ZERO)
23 .try_into()
24 .unwrap(),
25 }
26}
27
28impl SyscallHandler {
29 log_syscall!(
30 getitimer,
31 std::ffi::c_int,
32 linux_api::time::ITimerId,
33 *const linux_api::time::kernel_old_itimerval,
34 );
35 pub fn getitimer(
36 ctx: &mut SyscallContext,
37 which: std::ffi::c_int,
38 curr_value_ptr: ForeignPtr<linux_api::time::kernel_old_itimerval>,
39 ) -> Result<(), SyscallError> {
40 let Ok(which) = ITimerId::try_from(which) else {
41 debug!("Bad itimerid {which}");
42 return Err(Errno::EINVAL.into());
43 };
44
45 if which != ITimerId::ITIMER_REAL {
46 warn_once_then_debug!("Timer type {which:?} unsupported");
47 return Err(Errno::EINVAL.into());
48 }
49
50 let itimerval = kernel_old_itimerval_from_timer(&ctx.objs.process.realtime_timer_borrow());
51 ctx.objs
52 .process
53 .memory_borrow_mut()
54 .write(curr_value_ptr, &itimerval)?;
55
56 Ok(())
57 }
58
59 log_syscall!(
60 setitimer,
61 std::ffi::c_int,
62 linux_api::time::ITimerId,
63 *const linux_api::time::kernel_old_itimerval,
64 *const linux_api::time::kernel_old_itimerval,
65 );
66 pub fn setitimer(
67 ctx: &mut SyscallContext,
68 which: std::ffi::c_int,
69 new_value_ptr: ForeignPtr<linux_api::time::kernel_old_itimerval>,
70 old_value_ptr: ForeignPtr<linux_api::time::kernel_old_itimerval>,
71 ) -> Result<(), SyscallError> {
72 let Ok(which) = ITimerId::try_from(which) else {
73 debug!("Bad itimerid {which}");
74 return Err(Errno::EINVAL.into());
75 };
76
77 if which != ITimerId::ITIMER_REAL {
78 warn_once_then_debug!("Timer type {which:?} unsupported");
79 return Err(Errno::EINVAL.into());
80 }
81
82 if !old_value_ptr.is_null() {
83 let itimerval =
84 kernel_old_itimerval_from_timer(&ctx.objs.process.realtime_timer_borrow());
85 ctx.objs
86 .process
87 .memory_borrow_mut()
88 .write(old_value_ptr, &itimerval)?;
89 }
90
91 let new_value = ctx.objs.process.memory_borrow().read(new_value_ptr)?;
92 let new_value_value =
93 SimulationTime::try_from(new_value.it_value).map_err(|_| Errno::EINVAL)?;
94 let new_value_interval =
95 SimulationTime::try_from(new_value.it_interval).map_err(|_| Errno::EINVAL)?;
96
97 if new_value_value == SimulationTime::ZERO {
98 ctx.objs.process.realtime_timer_borrow_mut().disarm();
99 } else {
100 ctx.objs.process.realtime_timer_borrow_mut().arm(
101 ctx.objs.host,
102 Worker::current_time().unwrap() + new_value_value,
103 new_value_interval
104 .is_positive()
105 .then_some(new_value_interval),
106 );
107 }
108
109 Ok(())
110 }
111
112 log_syscall!(
113 alarm,
114 std::ffi::c_uint,
115 std::ffi::c_uint,
116 );
117 pub fn alarm(
118 ctx: &mut SyscallContext,
119 seconds: std::ffi::c_uint,
120 ) -> Result<std::ffi::c_uint, SyscallError> {
121 let prev_remaining = ctx.objs.process.realtime_timer_borrow().remaining_time();
122 let prev_remaining_secs = match prev_remaining {
123 Some(t) => {
124 let t = std::time::Duration::from(t);
125 if t.as_secs() == 0 {
126 1
131 } else if t.subsec_millis() > 500 {
132 t.as_secs() + 1
134 } else {
135 t.as_secs()
137 }
138 }
139 None => 0,
140 };
141 let prev_remaining_secs: u32 = u32::try_from(prev_remaining_secs).unwrap_or_else(|_| {
143 debug!("Couldn't convert remaining time {prev_remaining:?} to u32; using u32::MAX");
147 u32::MAX
148 });
149
150 if seconds == 0 {
151 ctx.objs.process.realtime_timer_borrow_mut().disarm();
153 } else {
154 ctx.objs.process.realtime_timer_borrow_mut().arm(
157 ctx.objs.host,
158 Worker::current_time().unwrap() + SimulationTime::from_secs(seconds.into()),
159 None,
160 );
161 }
162
163 Ok(prev_remaining_secs)
164 }
165
166 log_syscall!(
167 clock_getres,
168 std::ffi::c_int,
169 linux_api::time::ClockId,
170 *const std::ffi::c_void,
171 );
172 pub fn clock_getres(
173 ctx: &mut SyscallContext,
174 clock_id: linux_api::time::linux___kernel_clockid_t,
175 res_ptr: ForeignPtr<linux_api::time::timespec>,
176 ) -> Result<(), SyscallError> {
177 ClockId::try_from(clock_id).map_err(|_| Errno::EINVAL)?;
179
180 if !res_ptr.is_null() {
182 let res_time = linux_api::time::timespec::try_from(SimulationTime::NANOSECOND).unwrap();
183 ctx.objs
184 .process
185 .memory_borrow_mut()
186 .write(res_ptr, &res_time)?;
187 }
188
189 Ok(())
190 }
191
192 log_syscall!(
193 clock_nanosleep,
194 std::ffi::c_int,
195 linux_api::time::ClockId,
196 linux_api::time::ClockNanosleepFlags,
197 *const linux_api::time::timespec,
198 *const std::ffi::c_void,
199 );
200 pub fn clock_nanosleep(
201 ctx: &mut SyscallContext,
202 clock_id: linux_api::time::linux___kernel_clockid_t,
203 flags: std::ffi::c_int,
204 request_ptr: ForeignPtr<linux_api::time::timespec>,
205 remain_ptr: ForeignPtr<linux_api::time::timespec>,
206 ) -> Result<(), SyscallError> {
207 let clock_id = ClockId::try_from(clock_id).map_err(|_| Errno::EINVAL)?;
208
209 if [
211 ClockId::CLOCK_MONOTONIC,
212 ClockId::CLOCK_REALTIME,
213 ClockId::CLOCK_BOOTTIME,
214 ClockId::CLOCK_TAI,
215 ClockId::CLOCK_REALTIME_ALARM,
216 ClockId::CLOCK_BOOTTIME_ALARM,
217 ]
218 .contains(&clock_id)
219 {
220 let allow_unspec_bitflags =
222 ![ClockId::CLOCK_REALTIME_ALARM, ClockId::CLOCK_BOOTTIME_ALARM].contains(&clock_id);
223 Self::nanosleep_helper(ctx, flags, request_ptr, remain_ptr, allow_unspec_bitflags)
224 } else if [ClockId::CLOCK_THREAD_CPUTIME_ID].contains(&clock_id) {
225 log::debug!("Invalid clock id {clock_id:?}.",);
227 Err(Errno::EINVAL.into())
228 } else if [
229 ClockId::CLOCK_MONOTONIC_RAW,
230 ClockId::CLOCK_REALTIME_COARSE,
231 ClockId::CLOCK_MONOTONIC_COARSE,
232 ]
233 .contains(&clock_id)
234 {
235 log::debug!("Clock id {clock_id:?} unsupported for clock_nanosleep.",);
237 Err(Errno::ENOTSUP.into())
238 } else if [ClockId::CLOCK_PROCESS_CPUTIME_ID].contains(&clock_id) {
239 warn_once_then_debug!("Clock id {clock_id:?} unsupported in Shadow.",);
241 Err(Errno::ENOTSUP.into())
242 } else {
243 log::debug!("Unknown clock id {clock_id:?}.");
244 Err(Errno::EINVAL.into())
245 }
246 }
247
248 log_syscall!(
249 nanosleep,
250 std::ffi::c_int,
251 *const linux_api::time::timespec,
252 *const std::ffi::c_void,
253 );
254 pub fn nanosleep(
255 ctx: &mut SyscallContext,
256 req: ForeignPtr<linux_api::time::timespec>,
257 rem: ForeignPtr<linux_api::time::timespec>,
258 ) -> Result<(), SyscallError> {
259 Self::nanosleep_helper(ctx, 0, req, rem, false)
260 }
261
262 fn nanosleep_helper(
263 ctx: &mut SyscallContext,
264 flags: std::ffi::c_int,
265 request_ptr: ForeignPtr<linux_api::time::timespec>,
266 remain_ptr: ForeignPtr<linux_api::time::timespec>,
267 allow_unspec_bitflags: bool,
268 ) -> Result<(), SyscallError> {
269 let request = ctx.objs.process.memory_borrow().read(request_ptr)?;
270 let request_time = SimulationTime::try_from(request).or(Err(Errno::EINVAL))?;
271 let flags = if allow_unspec_bitflags {
272 ClockNanosleepFlags::from_bits_truncate(flags)
273 } else {
274 ClockNanosleepFlags::from_bits(flags).ok_or(Errno::EINVAL)?
275 };
276
277 let now = Worker::current_time().unwrap();
278
279 let abs_wakeup_time = if flags.contains(ClockNanosleepFlags::TIMER_ABSTIME) {
281 EmulatedTime::UNIX_EPOCH + request_time
282 } else {
283 now + request_time
284 };
285
286 if abs_wakeup_time <= now {
288 return Ok(());
289 }
290
291 let Some(cond) = ctx.objs.thread.syscall_condition() else {
293 return Err(SyscallError::new_blocked_until(abs_wakeup_time, false));
295 };
296
297 let expected_wakeup_time = cond.timeout().unwrap();
299
300 if expected_wakeup_time <= now {
301 Ok(())
303 } else {
304 if !remain_ptr.is_null() && !flags.contains(ClockNanosleepFlags::TIMER_ABSTIME) {
306 let remain_time =
307 linux_api::time::timespec::try_from(expected_wakeup_time - now).unwrap();
308 ctx.objs
309 .process
310 .memory_borrow_mut()
311 .write(remain_ptr, &remain_time)?;
312 }
313
314 Err(SyscallError::new_interrupted(false))
316 }
317 }
318}