squib_virtio/devices/
boot_timer.rs1use std::{sync::Arc, time::Instant};
16
17use parking_lot::Mutex;
18use squib_core::GuestMemory;
19
20use crate::{
21 device::{ActivateError, VirtioDevice},
22 device_id::VirtioDeviceType,
23 interrupt::IrqLine,
24 queue::Queue,
25};
26
27#[derive(Debug)]
29pub struct BootTimerDevice {
30 avail: u64,
31 acked: u64,
32 queues: Vec<Queue>,
33 boot_start: Instant,
38 state: Arc<Mutex<ActiveState>>,
39}
40
41#[derive(Debug, Default)]
42struct ActiveState {
43 activated: bool,
44}
45
46impl BootTimerDevice {
47 #[must_use]
49 pub fn new() -> Self {
50 Self {
51 avail: 0,
52 acked: 0,
53 queues: Vec::new(),
54 boot_start: Instant::now(),
55 state: Arc::new(Mutex::new(ActiveState::default())),
56 }
57 }
58
59 fn elapsed_us(&self) -> u32 {
61 let elapsed_us = self.boot_start.elapsed().as_micros();
62 u32::try_from(elapsed_us.min(u128::from(u32::MAX))).unwrap_or(u32::MAX)
63 }
64}
65
66impl Default for BootTimerDevice {
67 fn default() -> Self {
68 Self::new()
69 }
70}
71
72impl VirtioDevice for BootTimerDevice {
73 fn device_type(&self) -> VirtioDeviceType {
74 VirtioDeviceType::BootTimer
75 }
76 fn avail_features(&self) -> u64 {
77 self.avail
78 }
79 fn acked_features(&self) -> u64 {
80 self.acked
81 }
82 fn set_acked_features(&mut self, value: u64) {
83 self.acked = value;
84 }
85 fn queue_max_sizes(&self) -> &[u16] {
86 &[]
87 }
88 fn queues(&self) -> &[Queue] {
89 &self.queues
90 }
91 fn queues_mut(&mut self) -> &mut [Queue] {
92 &mut self.queues
93 }
94 fn read_config(&self, offset: u64, data: &mut [u8]) {
95 if offset == 0 && data.len() == 4 {
99 data.copy_from_slice(&self.elapsed_us().to_le_bytes());
100 return;
101 }
102 for b in data.iter_mut() {
103 *b = 0;
104 }
105 }
106 fn write_config(&mut self, _offset: u64, _data: &[u8]) {
107 }
110 fn activate(&mut self, _mem: Arc<dyn GuestMemory>, _irq: IrqLine) -> Result<(), ActivateError> {
111 self.state.lock().activated = true;
112 Ok(())
113 }
114 fn is_activated(&self) -> bool {
115 self.state.lock().activated
116 }
117 fn process_queue(&mut self, _queue_index: u16) {
118 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn test_should_have_zero_queues() {
128 let dev = BootTimerDevice::new();
129 assert_eq!(dev.queue_max_sizes().len(), 0);
130 assert_eq!(dev.queues().len(), 0);
131 }
132
133 #[test]
134 fn test_should_return_increasing_microseconds_on_consecutive_reads() {
135 let dev = BootTimerDevice::new();
136 let mut a = [0u8; 4];
137 let mut b = [0u8; 4];
138 dev.read_config(0, &mut a);
139 std::thread::sleep(std::time::Duration::from_millis(2));
140 dev.read_config(0, &mut b);
141 let av = u32::from_le_bytes(a);
142 let bv = u32::from_le_bytes(b);
143 assert!(bv >= av);
144 }
145
146 #[test]
147 fn test_should_return_zero_on_invalid_offset_or_length() {
148 let dev = BootTimerDevice::new();
149 let mut buf = [0xAAu8; 8];
150 dev.read_config(0x10, &mut buf);
151 assert_eq!(buf, [0u8; 8]);
152 }
153}