1use air_sys_types::fd::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd};
10use core::num::NonZeroI32;
11use core::time::Duration;
12
13use air_sys_types::{
14 Clock, Errno, Instant, SleepDeadline, SleepError, TimerFdFlags, TimerFdSpecification,
15 TimerSetFlags,
16};
17
18#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
19compile_error!("air-sys-syscall::time supporte uniquement x86_64 et aarch64 (ADR-014).");
20
21const NANOS_PER_SECOND_U32: u32 = 1_000_000_000;
22const TIMER_ABSTIME: i32 = 1;
23
24#[repr(C)]
25#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
26struct KernelTimespec {
27 tv_sec: i64,
28 tv_nsec: i64,
29}
30
31#[repr(C)]
32#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
33struct KernelItimerspec {
34 it_interval: KernelTimespec,
35 it_value: KernelTimespec,
36}
37
38#[derive(Debug)]
40pub struct TimerFd(OwnedFd);
41
42impl TimerFd {
43 #[must_use]
45 #[inline]
46 pub const fn from_owned_fd(fd: OwnedFd) -> Self {
47 Self(fd)
48 }
49
50 #[must_use]
52 pub fn as_fd(&self) -> BorrowedFd<'_> {
53 use air_sys_types::fd::AsFd;
54 self.0.as_fd()
55 }
56
57 #[must_use]
59 pub fn into_fd(self) -> OwnedFd {
60 self.0
61 }
62
63 pub fn arm(
71 &self,
72 spec: &TimerFdSpecification,
73 flags: TimerSetFlags,
74 ) -> Result<TimerFdSpecification, Errno> {
75 let new_value = kernel_itimerspec_from_spec(*spec)?;
76 let mut old_value = KernelItimerspec::default();
77 let ret = unsafe {
81 raw_syscall_timerfd_settime(
82 self.0.as_raw_fd(),
83 flags.bits(),
84 (&new_value as *const KernelItimerspec) as u64,
85 (&mut old_value as *mut KernelItimerspec) as u64,
86 )
87 };
88 if ret < 0 {
89 return Err(errno_from_negative_syscall_ret(ret));
90 }
91 kernel_itimerspec_to_spec(old_value)
92 }
93
94 pub fn disarm(&self) -> Result<TimerFdSpecification, Errno> {
102 self.arm(
103 &TimerFdSpecification {
104 initial: Duration::ZERO,
105 interval: Duration::ZERO,
106 },
107 TimerSetFlags::empty(),
108 )
109 }
110
111 pub fn current(&self) -> Result<TimerFdSpecification, Errno> {
119 let mut current = KernelItimerspec::default();
120 let ret = unsafe {
124 raw_syscall_timerfd_gettime(
125 self.0.as_raw_fd(),
126 (&mut current as *mut KernelItimerspec) as u64,
127 )
128 };
129 if ret < 0 {
130 return Err(errno_from_negative_syscall_ret(ret));
131 }
132 kernel_itimerspec_to_spec(current)
133 }
134
135 pub fn read(&self) -> Result<u64, Errno> {
149 let mut count = 0_u64;
150 let ret = unsafe {
154 raw_syscall_read(
155 self.0.as_raw_fd(),
156 (&mut count as *mut u64) as u64,
157 u64::try_from(core::mem::size_of::<u64>()).expect("u64 size fits in u64"),
158 )
159 };
160 if ret < 0 {
161 return Err(errno_from_negative_syscall_ret(ret));
162 }
163 debug_assert_eq!(ret, 8);
164 Ok(count)
165 }
166}
167
168pub fn clock_gettime(clock: Clock) -> Result<Instant, Errno> {
175 let ts = syscall_clock_gettime(clock.as_raw())?;
176 kernel_timespec_to_instant(clock, ts)
177}
178
179pub fn clock_settime(clock: Clock, instant: Instant) -> Result<(), Errno> {
188 if instant.clock() != clock {
189 return Err(Errno::EINVAL);
190 }
191 let ts = kernel_timespec_from_instant(instant);
192 let ret =
208 unsafe { raw_syscall_clock_settime(clock.as_raw(), (&ts as *const KernelTimespec) as u64) };
209 if ret < 0 {
210 return Err(errno_from_negative_syscall_ret(ret));
211 }
212 Ok(())
213}
214
215pub fn clock_getres(clock: Clock) -> Result<Duration, Errno> {
221 let ts = syscall_clock_getres(clock.as_raw())?;
222 kernel_timespec_to_duration(ts)
223}
224
225pub fn clock_nanosleep(clock: Clock, deadline: SleepDeadline) -> Result<(), SleepError> {
227 let mut rem = KernelTimespec::default();
228 let (flags, req, rem_ptr) = match deadline {
229 SleepDeadline::Relative(duration) => {
230 let req = match kernel_timespec_from_duration(duration) {
231 Ok(req) => req,
232 Err(err) => return Err(SleepError::Other(err)),
233 };
234 (0_i32, req, (&mut rem as *mut KernelTimespec) as u64)
235 }
236 SleepDeadline::AbsoluteInstant(instant) => {
237 if instant.clock() != clock {
238 return Err(SleepError::Other(Errno::EINVAL));
239 }
240 (TIMER_ABSTIME, kernel_timespec_from_instant(instant), 0_u64)
241 }
242 };
243
244 let ret = unsafe {
248 raw_syscall_clock_nanosleep(
249 clock.as_raw(),
250 flags,
251 (&req as *const KernelTimespec) as u64,
252 rem_ptr,
253 )
254 };
255 decode_clock_nanosleep_result(deadline, rem, ret)
256}
257
258pub fn timerfd_create(clock: Clock, flags: TimerFdFlags) -> Result<TimerFd, Errno> {
272 let kernel_flags = flags.bits() | TimerFdFlags::CLOEXEC.bits();
273 let owned = syscall_timerfd_create(clock.as_raw(), kernel_flags)?;
274 Ok(TimerFd::from_owned_fd(owned))
275}
276
277fn syscall_clock_gettime(clock: i32) -> Result<KernelTimespec, Errno> {
278 let mut ts = KernelTimespec::default();
279 let ret = unsafe { raw_syscall_clock_gettime(clock, (&mut ts as *mut KernelTimespec) as u64) };
282 if ret < 0 {
283 return Err(errno_from_negative_syscall_ret(ret));
284 }
285 Ok(ts)
286}
287
288fn syscall_clock_getres(clock: i32) -> Result<KernelTimespec, Errno> {
289 let mut ts = KernelTimespec::default();
290 let ret = unsafe { raw_syscall_clock_getres(clock, (&mut ts as *mut KernelTimespec) as u64) };
293 if ret < 0 {
294 return Err(errno_from_negative_syscall_ret(ret));
295 }
296 Ok(ts)
297}
298
299fn syscall_timerfd_create(clock: i32, flags: i32) -> Result<OwnedFd, Errno> {
300 let ret = unsafe { raw_syscall_timerfd_create(clock, flags) };
303 if ret < 0 {
304 return Err(errno_from_negative_syscall_ret(ret));
305 }
306 #[allow(clippy::cast_possible_truncation)]
307 let fd = ret as i32;
308 let owned = unsafe { OwnedFd::from_raw_fd(fd) };
311 Ok(owned)
312}
313
314fn decode_clock_nanosleep_result(
315 deadline: SleepDeadline,
316 rem: KernelTimespec,
317 ret: i64,
318) -> Result<(), SleepError> {
319 if ret == 0 {
320 return Ok(());
321 }
322 let errno = errno_from_negative_syscall_ret(ret);
323 if errno == Errno::EINTR
324 && let SleepDeadline::Relative(_) = deadline
325 {
326 let remaining = kernel_timespec_to_duration(rem).map_err(SleepError::Other)?;
327 return Err(SleepError::Interrupted { remaining });
328 }
329 Err(SleepError::Other(errno))
330}
331
332fn kernel_timespec_from_duration(duration: Duration) -> Result<KernelTimespec, Errno> {
333 let secs = i64::try_from(duration.as_secs()).map_err(|_| Errno::EINVAL)?;
334 Ok(KernelTimespec {
335 tv_sec: secs,
336 tv_nsec: i64::from(duration.subsec_nanos()),
337 })
338}
339
340fn kernel_timespec_to_duration(ts: KernelTimespec) -> Result<Duration, Errno> {
341 if ts.tv_sec < 0 || ts.tv_nsec < 0 || ts.tv_nsec >= i64::from(NANOS_PER_SECOND_U32) {
342 return Err(Errno::EINVAL);
343 }
344 let secs = u64::try_from(ts.tv_sec).map_err(|_| Errno::EINVAL)?;
345 let nanos = u32::try_from(ts.tv_nsec).map_err(|_| Errno::EINVAL)?;
346 Ok(Duration::new(secs, nanos))
347}
348
349fn kernel_timespec_from_instant(instant: Instant) -> KernelTimespec {
350 KernelTimespec {
351 tv_sec: instant.seconds(),
352 tv_nsec: i64::from(instant.nanoseconds()),
353 }
354}
355
356fn kernel_timespec_to_instant(clock: Clock, ts: KernelTimespec) -> Result<Instant, Errno> {
357 if ts.tv_nsec < 0 || ts.tv_nsec >= i64::from(NANOS_PER_SECOND_U32) {
358 return Err(Errno::EINVAL);
359 }
360 let nanos = u32::try_from(ts.tv_nsec).map_err(|_| Errno::EINVAL)?;
361 Instant::try_new(clock, ts.tv_sec, nanos).ok_or(Errno::EINVAL)
362}
363
364fn kernel_itimerspec_from_spec(spec: TimerFdSpecification) -> Result<KernelItimerspec, Errno> {
365 Ok(KernelItimerspec {
366 it_interval: kernel_timespec_from_duration(spec.interval)?,
367 it_value: kernel_timespec_from_duration(spec.initial)?,
368 })
369}
370
371fn kernel_itimerspec_to_spec(spec: KernelItimerspec) -> Result<TimerFdSpecification, Errno> {
372 Ok(TimerFdSpecification {
373 interval: kernel_timespec_to_duration(spec.it_interval)?,
374 initial: kernel_timespec_to_duration(spec.it_value)?,
375 })
376}
377
378fn errno_from_negative_syscall_ret(ret: i64) -> Errno {
379 debug_assert!(ret < 0 && ret > -4096);
380 #[allow(clippy::cast_possible_truncation)]
381 let raw = ret.wrapping_neg() as i32;
382 let nz = NonZeroI32::new(raw).expect("errno strictement positif par construction");
383 Errno::from_nonzero(nz)
384}
385
386#[cfg(target_arch = "x86_64")]
387#[inline]
388unsafe fn raw_syscall_clock_gettime(clock: i32, ts: u64) -> i64 {
389 let ret: i64;
390 unsafe {
394 core::arch::asm!(
395 "syscall",
396 in("rax") 228_i64,
397 in("rdi") i64::from(clock),
398 in("rsi") ts,
399 lateout("rax") ret,
400 lateout("rcx") _,
401 lateout("r11") _,
402 options(nostack, preserves_flags),
403 );
404 }
405 ret
406}
407
408#[cfg(target_arch = "x86_64")]
409#[inline]
410unsafe fn raw_syscall_clock_settime(clock: i32, ts: u64) -> i64 {
411 let ret: i64;
412 unsafe {
416 core::arch::asm!(
417 "syscall",
418 in("rax") 227_i64,
419 in("rdi") i64::from(clock),
420 in("rsi") ts,
421 lateout("rax") ret,
422 lateout("rcx") _,
423 lateout("r11") _,
424 options(nostack, preserves_flags, readonly),
425 );
426 }
427 ret
428}
429
430#[cfg(target_arch = "x86_64")]
431#[inline]
432unsafe fn raw_syscall_clock_getres(clock: i32, ts: u64) -> i64 {
433 let ret: i64;
434 unsafe {
438 core::arch::asm!(
439 "syscall",
440 in("rax") 229_i64,
441 in("rdi") i64::from(clock),
442 in("rsi") ts,
443 lateout("rax") ret,
444 lateout("rcx") _,
445 lateout("r11") _,
446 options(nostack, preserves_flags),
447 );
448 }
449 ret
450}
451
452#[cfg(target_arch = "x86_64")]
453#[inline]
454unsafe fn raw_syscall_clock_nanosleep(clock: i32, flags: i32, req: u64, rem: u64) -> i64 {
455 let ret: i64;
456 unsafe {
462 core::arch::asm!(
463 "syscall",
464 in("rax") 230_i64,
465 in("rdi") i64::from(clock),
466 in("rsi") i64::from(flags),
467 in("rdx") req,
468 in("r10") rem,
469 lateout("rax") ret,
470 lateout("rcx") _,
471 lateout("r11") _,
472 options(nostack, preserves_flags),
473 );
474 }
475 ret
476}
477
478#[cfg(target_arch = "x86_64")]
479#[inline]
480unsafe fn raw_syscall_timerfd_create(clock: i32, flags: i32) -> i64 {
481 let ret: i64;
482 unsafe {
486 core::arch::asm!(
487 "syscall",
488 in("rax") 283_i64,
489 in("rdi") i64::from(clock),
490 in("rsi") i64::from(flags),
491 lateout("rax") ret,
492 lateout("rcx") _,
493 lateout("r11") _,
494 options(nostack, preserves_flags, readonly),
495 );
496 }
497 ret
498}
499
500#[cfg(target_arch = "x86_64")]
501#[inline]
502unsafe fn raw_syscall_timerfd_settime(fd: i32, flags: i32, new_value: u64, old_value: u64) -> i64 {
503 let ret: i64;
504 unsafe {
510 core::arch::asm!(
511 "syscall",
512 in("rax") 286_i64,
513 in("rdi") i64::from(fd),
514 in("rsi") i64::from(flags),
515 in("rdx") new_value,
516 in("r10") old_value,
517 lateout("rax") ret,
518 lateout("rcx") _,
519 lateout("r11") _,
520 options(nostack, preserves_flags),
521 );
522 }
523 ret
524}
525
526#[cfg(target_arch = "x86_64")]
527#[inline]
528unsafe fn raw_syscall_timerfd_gettime(fd: i32, curr_value: u64) -> i64 {
529 let ret: i64;
530 unsafe {
534 core::arch::asm!(
535 "syscall",
536 in("rax") 287_i64,
537 in("rdi") i64::from(fd),
538 in("rsi") curr_value,
539 lateout("rax") ret,
540 lateout("rcx") _,
541 lateout("r11") _,
542 options(nostack, preserves_flags),
543 );
544 }
545 ret
546}
547
548#[cfg(target_arch = "x86_64")]
549#[inline]
550unsafe fn raw_syscall_read(fd: i32, buffer: u64, count: u64) -> i64 {
551 let ret: i64;
552 unsafe {
556 core::arch::asm!(
557 "syscall",
558 in("rax") 0_i64,
559 in("rdi") i64::from(fd),
560 in("rsi") buffer,
561 in("rdx") count,
562 lateout("rax") ret,
563 lateout("rcx") _,
564 lateout("r11") _,
565 options(nostack, preserves_flags),
566 );
567 }
568 ret
569}
570
571#[cfg(target_arch = "aarch64")]
572#[inline]
573unsafe fn raw_syscall_clock_gettime(clock: i32, ts: u64) -> i64 {
574 let ret: i64;
575 unsafe {
579 core::arch::asm!(
580 "svc 0",
581 in("x8") 113_i64,
582 inout("x0") i64::from(clock) => ret,
583 in("x1") ts,
584 options(nostack, preserves_flags),
585 );
586 }
587 ret
588}
589
590#[cfg(target_arch = "aarch64")]
591#[inline]
592unsafe fn raw_syscall_clock_settime(clock: i32, ts: u64) -> i64 {
593 let ret: i64;
594 unsafe {
598 core::arch::asm!(
599 "svc 0",
600 in("x8") 112_i64,
601 inout("x0") i64::from(clock) => ret,
602 in("x1") ts,
603 options(nostack, preserves_flags, readonly),
604 );
605 }
606 ret
607}
608
609#[cfg(target_arch = "aarch64")]
610#[inline]
611unsafe fn raw_syscall_clock_getres(clock: i32, ts: u64) -> i64 {
612 let ret: i64;
613 unsafe {
617 core::arch::asm!(
618 "svc 0",
619 in("x8") 114_i64,
620 inout("x0") i64::from(clock) => ret,
621 in("x1") ts,
622 options(nostack, preserves_flags),
623 );
624 }
625 ret
626}
627
628#[cfg(target_arch = "aarch64")]
629#[inline]
630unsafe fn raw_syscall_clock_nanosleep(clock: i32, flags: i32, req: u64, rem: u64) -> i64 {
631 let ret: i64;
632 unsafe {
638 core::arch::asm!(
639 "svc 0",
640 in("x8") 115_i64,
641 inout("x0") i64::from(clock) => ret,
642 in("x1") i64::from(flags),
643 in("x2") req,
644 in("x3") rem,
645 options(nostack, preserves_flags),
646 );
647 }
648 ret
649}
650
651#[cfg(target_arch = "aarch64")]
652#[inline]
653unsafe fn raw_syscall_timerfd_create(clock: i32, flags: i32) -> i64 {
654 let ret: i64;
655 unsafe {
659 core::arch::asm!(
660 "svc 0",
661 in("x8") 85_i64,
662 inout("x0") i64::from(clock) => ret,
663 in("x1") i64::from(flags),
664 options(nostack, preserves_flags, readonly),
665 );
666 }
667 ret
668}
669
670#[cfg(target_arch = "aarch64")]
671#[inline]
672unsafe fn raw_syscall_timerfd_settime(fd: i32, flags: i32, new_value: u64, old_value: u64) -> i64 {
673 let ret: i64;
674 unsafe {
680 core::arch::asm!(
681 "svc 0",
682 in("x8") 86_i64,
683 inout("x0") i64::from(fd) => ret,
684 in("x1") i64::from(flags),
685 in("x2") new_value,
686 in("x3") old_value,
687 options(nostack, preserves_flags),
688 );
689 }
690 ret
691}
692
693#[cfg(target_arch = "aarch64")]
694#[inline]
695unsafe fn raw_syscall_timerfd_gettime(fd: i32, curr_value: u64) -> i64 {
696 let ret: i64;
697 unsafe {
701 core::arch::asm!(
702 "svc 0",
703 in("x8") 87_i64,
704 inout("x0") i64::from(fd) => ret,
705 in("x1") curr_value,
706 options(nostack, preserves_flags),
707 );
708 }
709 ret
710}
711
712#[cfg(target_arch = "aarch64")]
713#[inline]
714unsafe fn raw_syscall_read(fd: i32, buffer: u64, count: u64) -> i64 {
715 let ret: i64;
716 unsafe {
720 core::arch::asm!(
721 "svc 0",
722 in("x8") 63_i64,
723 inout("x0") i64::from(fd) => ret,
724 in("x1") buffer,
725 in("x2") count,
726 options(nostack, preserves_flags),
727 );
728 }
729 ret
730}
731
732#[cfg(test)]
733mod tests;