| 1 | // Copyright (C) 2024 Intel Corporation. |
| 2 | // Author(s): Zhao Liu <zhao1.liu@intel.com> |
| 3 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 4 | |
| 5 | use std::{ |
| 6 | ffi::{c_int, c_void}, |
| 7 | pin::Pin, |
| 8 | }; |
| 9 | |
| 10 | use common::{callbacks::FnCall, Opaque}; |
| 11 | |
| 12 | use crate::bindings::{ |
| 13 | self, qemu_clock_get_ns, timer_del, timer_expire_time_ns, timer_init_full, timer_mod, |
| 14 | timer_mod_ns, QEMUClockType, |
| 15 | }; |
| 16 | |
| 17 | /// A safe wrapper around [`bindings::QEMUTimer`]. |
| 18 | #[repr(transparent)] |
| 19 | #[derive(Debug, common::Wrapper)] |
| 20 | pub struct Timer(Opaque<bindings::QEMUTimer>); |
| 21 | |
| 22 | unsafe impl Send for Timer {} |
| 23 | unsafe impl Sync for Timer {} |
| 24 | |
| 25 | #[repr(transparent)] |
| 26 | #[derive(common::Wrapper)] |
| 27 | pub struct TimerListGroup(Opaque<bindings::QEMUTimerListGroup>); |
| 28 | |
| 29 | unsafe impl Send for TimerListGroup {} |
| 30 | unsafe impl Sync for TimerListGroup {} |
| 31 | |
| 32 | impl Timer { |
| 33 | pub const MS: u32 = bindings::SCALE_MS; |
| 34 | pub const US: u32 = bindings::SCALE_US; |
| 35 | pub const NS: u32 = bindings::SCALE_NS; |
| 36 | |
| 37 | /// Create a `Timer` struct without initializing it. |
| 38 | /// |
| 39 | /// # Safety |
| 40 | /// |
| 41 | /// The timer must be initialized before it is armed with |
| 42 | /// [`modify`](Self::modify). |
| 43 | pub const unsafe fn new() -> Self { |
| 44 | // SAFETY: requirements relayed to callers of Timer::new |
| 45 | Self(unsafe { Opaque::zeroed() }) |
| 46 | } |
| 47 | |
| 48 | /// Create a new timer with the given attributes. |
| 49 | pub fn init_full<T, F>( |
| 50 | opaque: Pin<&mut T>, |
| 51 | timer_list_group: Option<&TimerListGroup>, |
| 52 | clk_type: ClockType, |
| 53 | scale: u32, |
| 54 | attributes: u32, |
| 55 | _cb: F, |
| 56 | field: impl FnOnce(&mut T) -> &mut Self, |
| 57 | ) where |
| 58 | F: for<'a> FnCall<(&'a T,)>, |
| 59 | { |
| 60 | const { assert!(F::IS_SOME) }; |
| 61 | |
| 62 | /// timer expiration callback |
| 63 | unsafe extern "C" fn rust_timer_handler<T, F: for<'a> FnCall<(&'a T,)>>( |
| 64 | opaque: *mut c_void, |
| 65 | ) { |
| 66 | // SAFETY: the opaque was passed as a reference to `T`. |
| 67 | F::call((unsafe { &*(opaque.cast::<T>()) },)) |
| 68 | } |
| 69 | |
| 70 | let timer_cb: unsafe extern "C" fn(*mut c_void) = rust_timer_handler::<T, F>; |
| 71 | |
| 72 | // SAFETY: the opaque outlives the timer |
| 73 | unsafe { |
| 74 | let opaque = Pin::into_inner_unchecked(opaque); |
| 75 | let timer = field(opaque).as_mut_ptr(); |
| 76 | timer_init_full( |
| 77 | timer, |
| 78 | if let Some(g) = timer_list_group { |
| 79 | g as *const TimerListGroup as *mut _ |
| 80 | } else { |
| 81 | ::core::ptr::null_mut() |
| 82 | }, |
| 83 | clk_type.id, |
| 84 | scale as c_int, |
| 85 | attributes as c_int, |
| 86 | Some(timer_cb), |
| 87 | (opaque as *mut T).cast::<c_void>(), |
| 88 | ) |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | pub fn expire_time_ns(&self) -> Option<i64> { |
| 93 | // SAFETY: the only way to obtain a Timer safely is via methods that |
| 94 | // take a Pin<&mut Self>, therefore the timer is pinned |
| 95 | let ret = unsafe { timer_expire_time_ns(self.as_ptr()) }; |
| 96 | i64::try_from(ret).ok() |
| 97 | } |
| 98 | |
| 99 | pub fn modify_ns(&self, expire_time: u64) { |
| 100 | // SAFETY: the only way to obtain a Timer safely is via methods that |
| 101 | // take a Pin<&mut Self>, therefore the timer is pinned |
| 102 | unsafe { timer_mod_ns(self.as_mut_ptr(), expire_time.try_into().unwrap()) } |
| 103 | } |
| 104 | |
| 105 | pub fn modify(&self, expire_time: u64) { |
| 106 | // SAFETY: the only way to obtain a Timer safely is via methods that |
| 107 | // take a Pin<&mut Self>, therefore the timer is pinned |
| 108 | unsafe { timer_mod(self.as_mut_ptr(), expire_time as i64) } |
| 109 | } |
| 110 | |
| 111 | pub fn delete(&self) { |
| 112 | // SAFETY: the only way to obtain a Timer safely is via methods that |
| 113 | // take a Pin<&mut Self>, therefore the timer is pinned |
| 114 | unsafe { timer_del(self.as_mut_ptr()) } |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | // FIXME: use something like PinnedDrop from the pinned_init crate |
| 119 | impl Drop for Timer { |
| 120 | fn drop(&mut self) { |
| 121 | self.delete() |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | pub struct ClockType { |
| 126 | id: QEMUClockType, |
| 127 | } |
| 128 | |
| 129 | impl ClockType { |
| 130 | pub fn get_ns(&self) -> u64 { |
| 131 | // SAFETY: cannot be created outside this module, therefore id |
| 132 | // is valid |
| 133 | (unsafe { qemu_clock_get_ns(self.id) }) as u64 |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | pub const CLOCK_VIRTUAL: ClockType = ClockType { |
| 138 | id: QEMUClockType::QEMU_CLOCK_VIRTUAL, |
| 139 | }; |
| 140 | |
| 141 | pub const NANOSECONDS_PER_SECOND: u64 = 1000000000; |