aboutsummaryrefslogtreecommitdiff
path: root/rust/qemu-api/src/timer.rs
blob: f0b04ef95d7ebc6c91c7a03c4f3148e140d6315b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// Copyright (C) 2024 Intel Corporation.
// Author(s): Zhao Liu <zhai1.liu@intel.com>
// SPDX-License-Identifier: GPL-2.0-or-later

use std::{
    os::raw::{c_int, c_void},
    pin::Pin,
};

use crate::{
    bindings::{self, qemu_clock_get_ns, timer_del, timer_init_full, timer_mod, QEMUClockType},
    callbacks::FnCall,
    cell::Opaque,
};

/// A safe wrapper around [`bindings::QEMUTimer`].
#[repr(transparent)]
#[derive(Debug, qemu_api_macros::Wrapper)]
pub struct Timer(Opaque<bindings::QEMUTimer>);

unsafe impl Send for Timer {}
unsafe impl Sync for Timer {}

#[repr(transparent)]
#[derive(qemu_api_macros::Wrapper)]
pub struct TimerListGroup(Opaque<bindings::QEMUTimerListGroup>);

unsafe impl Send for TimerListGroup {}
unsafe impl Sync for TimerListGroup {}

impl Timer {
    pub const MS: u32 = bindings::SCALE_MS;
    pub const US: u32 = bindings::SCALE_US;
    pub const NS: u32 = bindings::SCALE_NS;

    /// Create a `Timer` struct without initializing it.
    ///
    /// # Safety
    ///
    /// The timer must be initialized before it is armed with
    /// [`modify`](Self::modify).
    pub unsafe fn new() -> Self {
        // SAFETY: requirements relayed to callers of Timer::new
        Self(unsafe { Opaque::zeroed() })
    }

    /// Create a new timer with the given attributes.
    pub fn init_full<'timer, 'opaque: 'timer, T, F>(
        self: Pin<&'timer mut Self>,
        timer_list_group: Option<&TimerListGroup>,
        clk_type: ClockType,
        scale: u32,
        attributes: u32,
        _cb: F,
        opaque: &'opaque T,
    ) where
        F: for<'a> FnCall<(&'a T,)>,
    {
        let _: () = F::ASSERT_IS_SOME;

        /// timer expiration callback
        unsafe extern "C" fn rust_timer_handler<T, F: for<'a> FnCall<(&'a T,)>>(
            opaque: *mut c_void,
        ) {
            // SAFETY: the opaque was passed as a reference to `T`.
            F::call((unsafe { &*(opaque.cast::<T>()) },))
        }

        let timer_cb: unsafe extern "C" fn(*mut c_void) = rust_timer_handler::<T, F>;

        // SAFETY: the opaque outlives the timer
        unsafe {
            timer_init_full(
                self.as_mut_ptr(),
                if let Some(g) = timer_list_group {
                    g as *const TimerListGroup as *mut _
                } else {
                    ::core::ptr::null_mut()
                },
                clk_type.id,
                scale as c_int,
                attributes as c_int,
                Some(timer_cb),
                (opaque as *const T).cast::<c_void>() as *mut c_void,
            )
        }
    }

    pub fn modify(&self, expire_time: u64) {
        // SAFETY: the only way to obtain a Timer safely is via methods that
        // take a Pin<&mut Self>, therefore the timer is pinned
        unsafe { timer_mod(self.as_mut_ptr(), expire_time as i64) }
    }

    pub fn delete(&self) {
        // SAFETY: the only way to obtain a Timer safely is via methods that
        // take a Pin<&mut Self>, therefore the timer is pinned
        unsafe { timer_del(self.as_mut_ptr()) }
    }
}

// FIXME: use something like PinnedDrop from the pinned_init crate
impl Drop for Timer {
    fn drop(&mut self) {
        self.delete()
    }
}

pub struct ClockType {
    id: QEMUClockType,
}

impl ClockType {
    pub fn get_ns(&self) -> u64 {
        // SAFETY: cannot be created outside this module, therefore id
        // is valid
        (unsafe { qemu_clock_get_ns(self.id) }) as u64
    }
}

pub const CLOCK_VIRTUAL: ClockType = ClockType {
    id: QEMUClockType::QEMU_CLOCK_VIRTUAL,
};