xref: /aosp_15_r20/external/crosvm/devices/src/vmwdt.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2022 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker //! vmwdt is a virtual watchdog memory mapped device which detects stalls
6*bb4ee6a4SAndroid Build Coastguard Worker //! on the vCPUs and resets the guest when no 'pet' events are received.
7*bb4ee6a4SAndroid Build Coastguard Worker //! <https://docs.google.com/document/d/1DYmk2roxlwHZsOfcJi8xDMdWOHAmomvs2SDh7KPud3Y/edit?usp=sharing&resourcekey=0-oSNabc-t040a1q0K4cyI8Q>
8*bb4ee6a4SAndroid Build Coastguard Worker 
9*bb4ee6a4SAndroid Build Coastguard Worker use std::collections::BTreeMap;
10*bb4ee6a4SAndroid Build Coastguard Worker use std::convert::TryFrom;
11*bb4ee6a4SAndroid Build Coastguard Worker use std::fs;
12*bb4ee6a4SAndroid Build Coastguard Worker use std::sync::Arc;
13*bb4ee6a4SAndroid Build Coastguard Worker use std::time::Duration;
14*bb4ee6a4SAndroid Build Coastguard Worker 
15*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::Context;
16*bb4ee6a4SAndroid Build Coastguard Worker use base::custom_serde::serialize_arc_mutex;
17*bb4ee6a4SAndroid Build Coastguard Worker use base::debug;
18*bb4ee6a4SAndroid Build Coastguard Worker use base::error;
19*bb4ee6a4SAndroid Build Coastguard Worker use base::warn;
20*bb4ee6a4SAndroid Build Coastguard Worker use base::AsRawDescriptor;
21*bb4ee6a4SAndroid Build Coastguard Worker use base::Descriptor;
22*bb4ee6a4SAndroid Build Coastguard Worker use base::Error as SysError;
23*bb4ee6a4SAndroid Build Coastguard Worker use base::Event;
24*bb4ee6a4SAndroid Build Coastguard Worker use base::EventToken;
25*bb4ee6a4SAndroid Build Coastguard Worker use base::SendTube;
26*bb4ee6a4SAndroid Build Coastguard Worker use base::Timer;
27*bb4ee6a4SAndroid Build Coastguard Worker use base::TimerTrait;
28*bb4ee6a4SAndroid Build Coastguard Worker use base::Tube;
29*bb4ee6a4SAndroid Build Coastguard Worker use base::VmEventType;
30*bb4ee6a4SAndroid Build Coastguard Worker use base::WaitContext;
31*bb4ee6a4SAndroid Build Coastguard Worker use base::WorkerThread;
32*bb4ee6a4SAndroid Build Coastguard Worker use serde::Deserialize;
33*bb4ee6a4SAndroid Build Coastguard Worker use serde::Serialize;
34*bb4ee6a4SAndroid Build Coastguard Worker use sync::Mutex;
35*bb4ee6a4SAndroid Build Coastguard Worker use vm_control::VmResponse;
36*bb4ee6a4SAndroid Build Coastguard Worker 
37*bb4ee6a4SAndroid Build Coastguard Worker use crate::pci::CrosvmDeviceId;
38*bb4ee6a4SAndroid Build Coastguard Worker use crate::BusAccessInfo;
39*bb4ee6a4SAndroid Build Coastguard Worker use crate::BusDevice;
40*bb4ee6a4SAndroid Build Coastguard Worker use crate::DeviceId;
41*bb4ee6a4SAndroid Build Coastguard Worker use crate::IrqEdgeEvent;
42*bb4ee6a4SAndroid Build Coastguard Worker use crate::Suspendable;
43*bb4ee6a4SAndroid Build Coastguard Worker 
44*bb4ee6a4SAndroid Build Coastguard Worker // Registers offsets
45*bb4ee6a4SAndroid Build Coastguard Worker const VMWDT_REG_STATUS: u32 = 0x00;
46*bb4ee6a4SAndroid Build Coastguard Worker const VMWDT_REG_LOAD_CNT: u32 = 0x04;
47*bb4ee6a4SAndroid Build Coastguard Worker const VMWDT_REG_CURRENT_CNT: u32 = 0x08;
48*bb4ee6a4SAndroid Build Coastguard Worker const VMWDT_REG_CLOCK_FREQ_HZ: u32 = 0x0C;
49*bb4ee6a4SAndroid Build Coastguard Worker 
50*bb4ee6a4SAndroid Build Coastguard Worker // Length of the registers
51*bb4ee6a4SAndroid Build Coastguard Worker const VMWDT_REG_LEN: u64 = 0x10;
52*bb4ee6a4SAndroid Build Coastguard Worker 
53*bb4ee6a4SAndroid Build Coastguard Worker pub const VMWDT_DEFAULT_TIMEOUT_SEC: u32 = 10;
54*bb4ee6a4SAndroid Build Coastguard Worker pub const VMWDT_DEFAULT_CLOCK_HZ: u32 = 2;
55*bb4ee6a4SAndroid Build Coastguard Worker 
56*bb4ee6a4SAndroid Build Coastguard Worker // Proc stat indexes
57*bb4ee6a4SAndroid Build Coastguard Worker const PROCSTAT_GUEST_TIME_INDX: usize = 42;
58*bb4ee6a4SAndroid Build Coastguard Worker 
59*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Serialize)]
60*bb4ee6a4SAndroid Build Coastguard Worker pub struct VmwdtPerCpu {
61*bb4ee6a4SAndroid Build Coastguard Worker     // Flag which indicated if the watchdog is started
62*bb4ee6a4SAndroid Build Coastguard Worker     is_enabled: bool,
63*bb4ee6a4SAndroid Build Coastguard Worker     // Timer used to generate periodic events at `timer_freq_hz` frequency
64*bb4ee6a4SAndroid Build Coastguard Worker     #[serde(skip_serializing)]
65*bb4ee6a4SAndroid Build Coastguard Worker     timer: Timer,
66*bb4ee6a4SAndroid Build Coastguard Worker     // The frequency of the `timer`
67*bb4ee6a4SAndroid Build Coastguard Worker     timer_freq_hz: u64,
68*bb4ee6a4SAndroid Build Coastguard Worker     // Timestamp measured in miliseconds of the last guest activity
69*bb4ee6a4SAndroid Build Coastguard Worker     last_guest_time_ms: i64,
70*bb4ee6a4SAndroid Build Coastguard Worker     // The thread_id of the thread this vcpu belongs to
71*bb4ee6a4SAndroid Build Coastguard Worker     thread_id: u32,
72*bb4ee6a4SAndroid Build Coastguard Worker     // The process id of the task this vcpu belongs to
73*bb4ee6a4SAndroid Build Coastguard Worker     process_id: u32,
74*bb4ee6a4SAndroid Build Coastguard Worker     // The pre-programmed one-shot expiration interval. If the guest runs in this
75*bb4ee6a4SAndroid Build Coastguard Worker     // interval but we don't receive a periodic event, the guest is stalled.
76*bb4ee6a4SAndroid Build Coastguard Worker     next_expiration_interval_ms: i64,
77*bb4ee6a4SAndroid Build Coastguard Worker     // Keep track if the watchdog PPI raised.
78*bb4ee6a4SAndroid Build Coastguard Worker     stall_evt_ppi_triggered: bool,
79*bb4ee6a4SAndroid Build Coastguard Worker     // Keep track if the time was armed with oneshot mode or with repeating interval
80*bb4ee6a4SAndroid Build Coastguard Worker     repeating_interval: Option<Duration>,
81*bb4ee6a4SAndroid Build Coastguard Worker }
82*bb4ee6a4SAndroid Build Coastguard Worker 
83*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Deserialize)]
84*bb4ee6a4SAndroid Build Coastguard Worker struct VmwdtPerCpuRestore {
85*bb4ee6a4SAndroid Build Coastguard Worker     is_enabled: bool,
86*bb4ee6a4SAndroid Build Coastguard Worker     timer_freq_hz: u64,
87*bb4ee6a4SAndroid Build Coastguard Worker     last_guest_time_ms: i64,
88*bb4ee6a4SAndroid Build Coastguard Worker     next_expiration_interval_ms: i64,
89*bb4ee6a4SAndroid Build Coastguard Worker     repeating_interval: Option<Duration>,
90*bb4ee6a4SAndroid Build Coastguard Worker }
91*bb4ee6a4SAndroid Build Coastguard Worker 
92*bb4ee6a4SAndroid Build Coastguard Worker pub struct Vmwdt {
93*bb4ee6a4SAndroid Build Coastguard Worker     vm_wdts: Arc<Mutex<Vec<VmwdtPerCpu>>>,
94*bb4ee6a4SAndroid Build Coastguard Worker     // The worker thread that waits on the timer fd
95*bb4ee6a4SAndroid Build Coastguard Worker     worker_thread: Option<WorkerThread<Tube>>,
96*bb4ee6a4SAndroid Build Coastguard Worker     // TODO: @sebastianene add separate reset event for the watchdog
97*bb4ee6a4SAndroid Build Coastguard Worker     // Reset source if the device is not responding
98*bb4ee6a4SAndroid Build Coastguard Worker     reset_evt_wrtube: SendTube,
99*bb4ee6a4SAndroid Build Coastguard Worker     activated: bool,
100*bb4ee6a4SAndroid Build Coastguard Worker     // Event to be used to interrupt the guest on detected stalls
101*bb4ee6a4SAndroid Build Coastguard Worker     stall_evt: IrqEdgeEvent,
102*bb4ee6a4SAndroid Build Coastguard Worker     vm_ctrl_tube: Option<Tube>,
103*bb4ee6a4SAndroid Build Coastguard Worker }
104*bb4ee6a4SAndroid Build Coastguard Worker 
105*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Serialize)]
106*bb4ee6a4SAndroid Build Coastguard Worker struct VmwdtSnapshot {
107*bb4ee6a4SAndroid Build Coastguard Worker     #[serde(serialize_with = "serialize_arc_mutex")]
108*bb4ee6a4SAndroid Build Coastguard Worker     vm_wdts: Arc<Mutex<Vec<VmwdtPerCpu>>>,
109*bb4ee6a4SAndroid Build Coastguard Worker     activated: bool,
110*bb4ee6a4SAndroid Build Coastguard Worker }
111*bb4ee6a4SAndroid Build Coastguard Worker 
112*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Deserialize)]
113*bb4ee6a4SAndroid Build Coastguard Worker struct VmwdtRestore {
114*bb4ee6a4SAndroid Build Coastguard Worker     vm_wdts: Vec<VmwdtPerCpuRestore>,
115*bb4ee6a4SAndroid Build Coastguard Worker     activated: bool,
116*bb4ee6a4SAndroid Build Coastguard Worker }
117*bb4ee6a4SAndroid Build Coastguard Worker 
118*bb4ee6a4SAndroid Build Coastguard Worker impl Vmwdt {
new( cpu_count: usize, reset_evt_wrtube: SendTube, evt: IrqEdgeEvent, vm_ctrl_tube: Tube, ) -> anyhow::Result<Vmwdt>119*bb4ee6a4SAndroid Build Coastguard Worker     pub fn new(
120*bb4ee6a4SAndroid Build Coastguard Worker         cpu_count: usize,
121*bb4ee6a4SAndroid Build Coastguard Worker         reset_evt_wrtube: SendTube,
122*bb4ee6a4SAndroid Build Coastguard Worker         evt: IrqEdgeEvent,
123*bb4ee6a4SAndroid Build Coastguard Worker         vm_ctrl_tube: Tube,
124*bb4ee6a4SAndroid Build Coastguard Worker     ) -> anyhow::Result<Vmwdt> {
125*bb4ee6a4SAndroid Build Coastguard Worker         let mut vec = Vec::new();
126*bb4ee6a4SAndroid Build Coastguard Worker         for _ in 0..cpu_count {
127*bb4ee6a4SAndroid Build Coastguard Worker             vec.push(VmwdtPerCpu {
128*bb4ee6a4SAndroid Build Coastguard Worker                 last_guest_time_ms: 0,
129*bb4ee6a4SAndroid Build Coastguard Worker                 thread_id: 0,
130*bb4ee6a4SAndroid Build Coastguard Worker                 process_id: 0,
131*bb4ee6a4SAndroid Build Coastguard Worker                 is_enabled: false,
132*bb4ee6a4SAndroid Build Coastguard Worker                 stall_evt_ppi_triggered: false,
133*bb4ee6a4SAndroid Build Coastguard Worker                 timer: Timer::new().context("failed to create Timer")?,
134*bb4ee6a4SAndroid Build Coastguard Worker                 timer_freq_hz: 0,
135*bb4ee6a4SAndroid Build Coastguard Worker                 next_expiration_interval_ms: 0,
136*bb4ee6a4SAndroid Build Coastguard Worker                 repeating_interval: None,
137*bb4ee6a4SAndroid Build Coastguard Worker             });
138*bb4ee6a4SAndroid Build Coastguard Worker         }
139*bb4ee6a4SAndroid Build Coastguard Worker         let vm_wdts = Arc::new(Mutex::new(vec));
140*bb4ee6a4SAndroid Build Coastguard Worker 
141*bb4ee6a4SAndroid Build Coastguard Worker         Ok(Vmwdt {
142*bb4ee6a4SAndroid Build Coastguard Worker             vm_wdts,
143*bb4ee6a4SAndroid Build Coastguard Worker             worker_thread: None,
144*bb4ee6a4SAndroid Build Coastguard Worker             reset_evt_wrtube,
145*bb4ee6a4SAndroid Build Coastguard Worker             activated: false,
146*bb4ee6a4SAndroid Build Coastguard Worker             stall_evt: evt,
147*bb4ee6a4SAndroid Build Coastguard Worker             vm_ctrl_tube: Some(vm_ctrl_tube),
148*bb4ee6a4SAndroid Build Coastguard Worker         })
149*bb4ee6a4SAndroid Build Coastguard Worker     }
150*bb4ee6a4SAndroid Build Coastguard Worker 
vmwdt_worker_thread( vm_wdts: Arc<Mutex<Vec<VmwdtPerCpu>>>, kill_evt: Event, reset_evt_wrtube: SendTube, stall_evt: IrqEdgeEvent, vm_ctrl_tube: Tube, worker_started_send: Option<SendTube>, ) -> anyhow::Result<Tube>151*bb4ee6a4SAndroid Build Coastguard Worker     pub fn vmwdt_worker_thread(
152*bb4ee6a4SAndroid Build Coastguard Worker         vm_wdts: Arc<Mutex<Vec<VmwdtPerCpu>>>,
153*bb4ee6a4SAndroid Build Coastguard Worker         kill_evt: Event,
154*bb4ee6a4SAndroid Build Coastguard Worker         reset_evt_wrtube: SendTube,
155*bb4ee6a4SAndroid Build Coastguard Worker         stall_evt: IrqEdgeEvent,
156*bb4ee6a4SAndroid Build Coastguard Worker         vm_ctrl_tube: Tube,
157*bb4ee6a4SAndroid Build Coastguard Worker         worker_started_send: Option<SendTube>,
158*bb4ee6a4SAndroid Build Coastguard Worker     ) -> anyhow::Result<Tube> {
159*bb4ee6a4SAndroid Build Coastguard Worker         let msg = vm_control::VmRequest::VcpuPidTid;
160*bb4ee6a4SAndroid Build Coastguard Worker         vm_ctrl_tube
161*bb4ee6a4SAndroid Build Coastguard Worker             .send(&msg)
162*bb4ee6a4SAndroid Build Coastguard Worker             .context("failed to send request to fetch Vcpus PID and TID")?;
163*bb4ee6a4SAndroid Build Coastguard Worker         let vcpus_pid_tid: BTreeMap<usize, (u32, u32)> = match vm_ctrl_tube
164*bb4ee6a4SAndroid Build Coastguard Worker             .recv()
165*bb4ee6a4SAndroid Build Coastguard Worker             .context("failed to receive vmwdt pids and tids")?
166*bb4ee6a4SAndroid Build Coastguard Worker         {
167*bb4ee6a4SAndroid Build Coastguard Worker             VmResponse::VcpuPidTidResponse { pid_tid_map } => pid_tid_map,
168*bb4ee6a4SAndroid Build Coastguard Worker             _ => {
169*bb4ee6a4SAndroid Build Coastguard Worker                 return Err(anyhow::anyhow!(
170*bb4ee6a4SAndroid Build Coastguard Worker                     "Receive incorrect message type when trying to get vcpu pid tid map"
171*bb4ee6a4SAndroid Build Coastguard Worker                 ));
172*bb4ee6a4SAndroid Build Coastguard Worker             }
173*bb4ee6a4SAndroid Build Coastguard Worker         };
174*bb4ee6a4SAndroid Build Coastguard Worker         {
175*bb4ee6a4SAndroid Build Coastguard Worker             let mut vm_wdts = vm_wdts.lock();
176*bb4ee6a4SAndroid Build Coastguard Worker             for (i, vmwdt) in (*vm_wdts).iter_mut().enumerate() {
177*bb4ee6a4SAndroid Build Coastguard Worker                 let pid_tid = vcpus_pid_tid
178*bb4ee6a4SAndroid Build Coastguard Worker                     .get(&i)
179*bb4ee6a4SAndroid Build Coastguard Worker                     .context("vmwdts empty, which could indicate no vcpus are initialized")?;
180*bb4ee6a4SAndroid Build Coastguard Worker                 vmwdt.process_id = pid_tid.0;
181*bb4ee6a4SAndroid Build Coastguard Worker                 vmwdt.thread_id = pid_tid.1;
182*bb4ee6a4SAndroid Build Coastguard Worker             }
183*bb4ee6a4SAndroid Build Coastguard Worker         }
184*bb4ee6a4SAndroid Build Coastguard Worker         if let Some(worker_started_send) = worker_started_send {
185*bb4ee6a4SAndroid Build Coastguard Worker             worker_started_send
186*bb4ee6a4SAndroid Build Coastguard Worker                 .send(&())
187*bb4ee6a4SAndroid Build Coastguard Worker                 .context("failed to send vmwdt worker started")?;
188*bb4ee6a4SAndroid Build Coastguard Worker         }
189*bb4ee6a4SAndroid Build Coastguard Worker         #[derive(EventToken)]
190*bb4ee6a4SAndroid Build Coastguard Worker         enum Token {
191*bb4ee6a4SAndroid Build Coastguard Worker             Kill,
192*bb4ee6a4SAndroid Build Coastguard Worker             Timer(usize),
193*bb4ee6a4SAndroid Build Coastguard Worker         }
194*bb4ee6a4SAndroid Build Coastguard Worker 
195*bb4ee6a4SAndroid Build Coastguard Worker         let wait_ctx: WaitContext<Token> =
196*bb4ee6a4SAndroid Build Coastguard Worker             WaitContext::new().context("Failed to create wait_ctx")?;
197*bb4ee6a4SAndroid Build Coastguard Worker         wait_ctx
198*bb4ee6a4SAndroid Build Coastguard Worker             .add(&kill_evt, Token::Kill)
199*bb4ee6a4SAndroid Build Coastguard Worker             .context("Failed to add Tokens to wait_ctx")?;
200*bb4ee6a4SAndroid Build Coastguard Worker 
201*bb4ee6a4SAndroid Build Coastguard Worker         let len = vm_wdts.lock().len();
202*bb4ee6a4SAndroid Build Coastguard Worker         for clock_id in 0..len {
203*bb4ee6a4SAndroid Build Coastguard Worker             let timer_fd = vm_wdts.lock()[clock_id].timer.as_raw_descriptor();
204*bb4ee6a4SAndroid Build Coastguard Worker             wait_ctx
205*bb4ee6a4SAndroid Build Coastguard Worker                 .add(&Descriptor(timer_fd), Token::Timer(clock_id))
206*bb4ee6a4SAndroid Build Coastguard Worker                 .context("Failed to link FDs to Tokens")?;
207*bb4ee6a4SAndroid Build Coastguard Worker         }
208*bb4ee6a4SAndroid Build Coastguard Worker 
209*bb4ee6a4SAndroid Build Coastguard Worker         loop {
210*bb4ee6a4SAndroid Build Coastguard Worker             let events = wait_ctx.wait().context("Failed to wait for events")?;
211*bb4ee6a4SAndroid Build Coastguard Worker             for event in events.iter().filter(|e| e.is_readable) {
212*bb4ee6a4SAndroid Build Coastguard Worker                 match event.token {
213*bb4ee6a4SAndroid Build Coastguard Worker                     Token::Kill => {
214*bb4ee6a4SAndroid Build Coastguard Worker                         return Ok(vm_ctrl_tube);
215*bb4ee6a4SAndroid Build Coastguard Worker                     }
216*bb4ee6a4SAndroid Build Coastguard Worker                     Token::Timer(cpu_id) => {
217*bb4ee6a4SAndroid Build Coastguard Worker                         let mut wdts_locked = vm_wdts.lock();
218*bb4ee6a4SAndroid Build Coastguard Worker                         let watchdog = &mut wdts_locked[cpu_id];
219*bb4ee6a4SAndroid Build Coastguard Worker                         match watchdog.timer.mark_waited() {
220*bb4ee6a4SAndroid Build Coastguard Worker                             Ok(true) => continue, // timer not actually ready
221*bb4ee6a4SAndroid Build Coastguard Worker                             Ok(false) => {}
222*bb4ee6a4SAndroid Build Coastguard Worker                             Err(e) => {
223*bb4ee6a4SAndroid Build Coastguard Worker                                 error!("error waiting for timer event on vcpu {cpu_id}: {e:#}");
224*bb4ee6a4SAndroid Build Coastguard Worker                                 continue;
225*bb4ee6a4SAndroid Build Coastguard Worker                             }
226*bb4ee6a4SAndroid Build Coastguard Worker                         }
227*bb4ee6a4SAndroid Build Coastguard Worker 
228*bb4ee6a4SAndroid Build Coastguard Worker                         let current_guest_time_ms =
229*bb4ee6a4SAndroid Build Coastguard Worker                             Vmwdt::get_guest_time_ms(watchdog.process_id, watchdog.thread_id)
230*bb4ee6a4SAndroid Build Coastguard Worker                                 .context("get_guest_time_ms failed")?;
231*bb4ee6a4SAndroid Build Coastguard Worker                         let remaining_time_ms = watchdog.next_expiration_interval_ms
232*bb4ee6a4SAndroid Build Coastguard Worker                             - (current_guest_time_ms - watchdog.last_guest_time_ms);
233*bb4ee6a4SAndroid Build Coastguard Worker 
234*bb4ee6a4SAndroid Build Coastguard Worker                         if remaining_time_ms > 0 {
235*bb4ee6a4SAndroid Build Coastguard Worker                             watchdog.next_expiration_interval_ms = remaining_time_ms;
236*bb4ee6a4SAndroid Build Coastguard Worker                             if let Err(e) = watchdog
237*bb4ee6a4SAndroid Build Coastguard Worker                                 .timer
238*bb4ee6a4SAndroid Build Coastguard Worker                                 .reset_oneshot(Duration::from_millis(remaining_time_ms as u64))
239*bb4ee6a4SAndroid Build Coastguard Worker                             {
240*bb4ee6a4SAndroid Build Coastguard Worker                                 error!(
241*bb4ee6a4SAndroid Build Coastguard Worker                                     "failed to reset internal timer on vcpu {}: {:#}",
242*bb4ee6a4SAndroid Build Coastguard Worker                                     cpu_id, e
243*bb4ee6a4SAndroid Build Coastguard Worker                                 );
244*bb4ee6a4SAndroid Build Coastguard Worker                             }
245*bb4ee6a4SAndroid Build Coastguard Worker                             watchdog.repeating_interval = None;
246*bb4ee6a4SAndroid Build Coastguard Worker                         } else {
247*bb4ee6a4SAndroid Build Coastguard Worker                             if watchdog.stall_evt_ppi_triggered {
248*bb4ee6a4SAndroid Build Coastguard Worker                                 if let Err(e) = reset_evt_wrtube
249*bb4ee6a4SAndroid Build Coastguard Worker                                     .send::<VmEventType>(&VmEventType::WatchdogReset)
250*bb4ee6a4SAndroid Build Coastguard Worker                                 {
251*bb4ee6a4SAndroid Build Coastguard Worker                                     error!("{} failed to send reset event from vcpu {}", e, cpu_id)
252*bb4ee6a4SAndroid Build Coastguard Worker                                 }
253*bb4ee6a4SAndroid Build Coastguard Worker                             }
254*bb4ee6a4SAndroid Build Coastguard Worker 
255*bb4ee6a4SAndroid Build Coastguard Worker                             stall_evt
256*bb4ee6a4SAndroid Build Coastguard Worker                                 .trigger()
257*bb4ee6a4SAndroid Build Coastguard Worker                                 .context("Failed to trigger stall event")?;
258*bb4ee6a4SAndroid Build Coastguard Worker                             watchdog.stall_evt_ppi_triggered = true;
259*bb4ee6a4SAndroid Build Coastguard Worker                             watchdog.last_guest_time_ms = current_guest_time_ms;
260*bb4ee6a4SAndroid Build Coastguard Worker                         }
261*bb4ee6a4SAndroid Build Coastguard Worker                     }
262*bb4ee6a4SAndroid Build Coastguard Worker                 }
263*bb4ee6a4SAndroid Build Coastguard Worker             }
264*bb4ee6a4SAndroid Build Coastguard Worker         }
265*bb4ee6a4SAndroid Build Coastguard Worker     }
266*bb4ee6a4SAndroid Build Coastguard Worker 
start(&mut self, worker_started_send: Option<SendTube>) -> anyhow::Result<()>267*bb4ee6a4SAndroid Build Coastguard Worker     fn start(&mut self, worker_started_send: Option<SendTube>) -> anyhow::Result<()> {
268*bb4ee6a4SAndroid Build Coastguard Worker         let vm_wdts = self.vm_wdts.clone();
269*bb4ee6a4SAndroid Build Coastguard Worker         let reset_evt_wrtube = self.reset_evt_wrtube.try_clone().unwrap();
270*bb4ee6a4SAndroid Build Coastguard Worker         let stall_event = self.stall_evt.try_clone().unwrap();
271*bb4ee6a4SAndroid Build Coastguard Worker         let vm_ctrl_tube = self
272*bb4ee6a4SAndroid Build Coastguard Worker             .vm_ctrl_tube
273*bb4ee6a4SAndroid Build Coastguard Worker             .take()
274*bb4ee6a4SAndroid Build Coastguard Worker             .context("missing vm control tube")?;
275*bb4ee6a4SAndroid Build Coastguard Worker 
276*bb4ee6a4SAndroid Build Coastguard Worker         self.activated = true;
277*bb4ee6a4SAndroid Build Coastguard Worker         self.worker_thread = Some(WorkerThread::start("vmwdt worker", |kill_evt| {
278*bb4ee6a4SAndroid Build Coastguard Worker             Vmwdt::vmwdt_worker_thread(
279*bb4ee6a4SAndroid Build Coastguard Worker                 vm_wdts,
280*bb4ee6a4SAndroid Build Coastguard Worker                 kill_evt,
281*bb4ee6a4SAndroid Build Coastguard Worker                 reset_evt_wrtube,
282*bb4ee6a4SAndroid Build Coastguard Worker                 stall_event,
283*bb4ee6a4SAndroid Build Coastguard Worker                 vm_ctrl_tube,
284*bb4ee6a4SAndroid Build Coastguard Worker                 worker_started_send,
285*bb4ee6a4SAndroid Build Coastguard Worker             )
286*bb4ee6a4SAndroid Build Coastguard Worker             .expect("failed to start vmwdt worker thread")
287*bb4ee6a4SAndroid Build Coastguard Worker         }));
288*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
289*bb4ee6a4SAndroid Build Coastguard Worker     }
290*bb4ee6a4SAndroid Build Coastguard Worker 
ensure_started(&mut self)291*bb4ee6a4SAndroid Build Coastguard Worker     fn ensure_started(&mut self) {
292*bb4ee6a4SAndroid Build Coastguard Worker         if self.worker_thread.is_some() {
293*bb4ee6a4SAndroid Build Coastguard Worker             return;
294*bb4ee6a4SAndroid Build Coastguard Worker         }
295*bb4ee6a4SAndroid Build Coastguard Worker 
296*bb4ee6a4SAndroid Build Coastguard Worker         let (worker_started_send, worker_started_recv) =
297*bb4ee6a4SAndroid Build Coastguard Worker             Tube::directional_pair().expect("failed to create vmwdt worker started tubes");
298*bb4ee6a4SAndroid Build Coastguard Worker         self.start(Some(worker_started_send))
299*bb4ee6a4SAndroid Build Coastguard Worker             .expect("failed to start Vmwdt");
300*bb4ee6a4SAndroid Build Coastguard Worker         worker_started_recv
301*bb4ee6a4SAndroid Build Coastguard Worker             .recv::<()>()
302*bb4ee6a4SAndroid Build Coastguard Worker             .expect("failed to receive vmwdt worker started");
303*bb4ee6a4SAndroid Build Coastguard Worker     }
304*bb4ee6a4SAndroid Build Coastguard Worker 
305*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(any(target_os = "linux", target_os = "android"))]
get_guest_time_ms(process_id: u32, thread_id: u32) -> Result<i64, SysError>306*bb4ee6a4SAndroid Build Coastguard Worker     pub fn get_guest_time_ms(process_id: u32, thread_id: u32) -> Result<i64, SysError> {
307*bb4ee6a4SAndroid Build Coastguard Worker         // TODO: @sebastianene check if we can avoid open-read-close on each call
308*bb4ee6a4SAndroid Build Coastguard Worker         let stat_path = format!("/proc/{}/task/{}/stat", process_id, thread_id);
309*bb4ee6a4SAndroid Build Coastguard Worker         let contents = fs::read_to_string(stat_path)?;
310*bb4ee6a4SAndroid Build Coastguard Worker 
311*bb4ee6a4SAndroid Build Coastguard Worker         let gtime_ticks = contents
312*bb4ee6a4SAndroid Build Coastguard Worker             .split_whitespace()
313*bb4ee6a4SAndroid Build Coastguard Worker             .nth(PROCSTAT_GUEST_TIME_INDX)
314*bb4ee6a4SAndroid Build Coastguard Worker             .and_then(|guest_time| guest_time.parse::<u64>().ok())
315*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap_or(0);
316*bb4ee6a4SAndroid Build Coastguard Worker 
317*bb4ee6a4SAndroid Build Coastguard Worker         // SAFETY:
318*bb4ee6a4SAndroid Build Coastguard Worker         // Safe because this just returns an integer
319*bb4ee6a4SAndroid Build Coastguard Worker         let ticks_per_sec = unsafe { libc::sysconf(libc::_SC_CLK_TCK) } as u64;
320*bb4ee6a4SAndroid Build Coastguard Worker         Ok((gtime_ticks * 1000 / ticks_per_sec) as i64)
321*bb4ee6a4SAndroid Build Coastguard Worker     }
322*bb4ee6a4SAndroid Build Coastguard Worker 
323*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(not(any(target_os = "linux", target_os = "android")))]
get_guest_time_ms(process_id: u32, thread_id: u32) -> Result<i64, SysError>324*bb4ee6a4SAndroid Build Coastguard Worker     pub fn get_guest_time_ms(process_id: u32, thread_id: u32) -> Result<i64, SysError> {
325*bb4ee6a4SAndroid Build Coastguard Worker         Ok(0)
326*bb4ee6a4SAndroid Build Coastguard Worker     }
327*bb4ee6a4SAndroid Build Coastguard Worker }
328*bb4ee6a4SAndroid Build Coastguard Worker 
329*bb4ee6a4SAndroid Build Coastguard Worker impl BusDevice for Vmwdt {
debug_label(&self) -> String330*bb4ee6a4SAndroid Build Coastguard Worker     fn debug_label(&self) -> String {
331*bb4ee6a4SAndroid Build Coastguard Worker         "Vmwdt".to_owned()
332*bb4ee6a4SAndroid Build Coastguard Worker     }
333*bb4ee6a4SAndroid Build Coastguard Worker 
device_id(&self) -> DeviceId334*bb4ee6a4SAndroid Build Coastguard Worker     fn device_id(&self) -> DeviceId {
335*bb4ee6a4SAndroid Build Coastguard Worker         CrosvmDeviceId::VmWatchdog.into()
336*bb4ee6a4SAndroid Build Coastguard Worker     }
337*bb4ee6a4SAndroid Build Coastguard Worker 
read(&mut self, _offset: BusAccessInfo, _data: &mut [u8])338*bb4ee6a4SAndroid Build Coastguard Worker     fn read(&mut self, _offset: BusAccessInfo, _data: &mut [u8]) {}
339*bb4ee6a4SAndroid Build Coastguard Worker 
write(&mut self, info: BusAccessInfo, data: &[u8])340*bb4ee6a4SAndroid Build Coastguard Worker     fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
341*bb4ee6a4SAndroid Build Coastguard Worker         let data_array = match <&[u8; 4]>::try_from(data) {
342*bb4ee6a4SAndroid Build Coastguard Worker             Ok(array) => array,
343*bb4ee6a4SAndroid Build Coastguard Worker             _ => {
344*bb4ee6a4SAndroid Build Coastguard Worker                 error!("Bad write size: {} for vmwdt", data.len());
345*bb4ee6a4SAndroid Build Coastguard Worker                 return;
346*bb4ee6a4SAndroid Build Coastguard Worker             }
347*bb4ee6a4SAndroid Build Coastguard Worker         };
348*bb4ee6a4SAndroid Build Coastguard Worker 
349*bb4ee6a4SAndroid Build Coastguard Worker         let reg_val = u32::from_ne_bytes(*data_array);
350*bb4ee6a4SAndroid Build Coastguard Worker         let cpu_index: usize = (info.offset / VMWDT_REG_LEN) as usize;
351*bb4ee6a4SAndroid Build Coastguard Worker         let reg_offset = (info.offset % VMWDT_REG_LEN) as u32;
352*bb4ee6a4SAndroid Build Coastguard Worker 
353*bb4ee6a4SAndroid Build Coastguard Worker         if cpu_index > self.vm_wdts.lock().len() {
354*bb4ee6a4SAndroid Build Coastguard Worker             error!("Bad write cpu_index {}", cpu_index);
355*bb4ee6a4SAndroid Build Coastguard Worker             return;
356*bb4ee6a4SAndroid Build Coastguard Worker         }
357*bb4ee6a4SAndroid Build Coastguard Worker 
358*bb4ee6a4SAndroid Build Coastguard Worker         match reg_offset {
359*bb4ee6a4SAndroid Build Coastguard Worker             VMWDT_REG_STATUS => {
360*bb4ee6a4SAndroid Build Coastguard Worker                 self.ensure_started();
361*bb4ee6a4SAndroid Build Coastguard Worker                 let mut wdts_locked = self.vm_wdts.lock();
362*bb4ee6a4SAndroid Build Coastguard Worker                 let cpu_watchdog = &mut wdts_locked[cpu_index];
363*bb4ee6a4SAndroid Build Coastguard Worker 
364*bb4ee6a4SAndroid Build Coastguard Worker                 cpu_watchdog.is_enabled = reg_val != 0;
365*bb4ee6a4SAndroid Build Coastguard Worker 
366*bb4ee6a4SAndroid Build Coastguard Worker                 if reg_val != 0 {
367*bb4ee6a4SAndroid Build Coastguard Worker                     let interval = Duration::from_millis(1000 / cpu_watchdog.timer_freq_hz);
368*bb4ee6a4SAndroid Build Coastguard Worker                     cpu_watchdog.repeating_interval = Some(interval);
369*bb4ee6a4SAndroid Build Coastguard Worker                     cpu_watchdog
370*bb4ee6a4SAndroid Build Coastguard Worker                         .timer
371*bb4ee6a4SAndroid Build Coastguard Worker                         .reset_repeating(interval)
372*bb4ee6a4SAndroid Build Coastguard Worker                         .expect("Failed to reset timer repeating interval");
373*bb4ee6a4SAndroid Build Coastguard Worker                 } else {
374*bb4ee6a4SAndroid Build Coastguard Worker                     cpu_watchdog.repeating_interval = None;
375*bb4ee6a4SAndroid Build Coastguard Worker                     cpu_watchdog
376*bb4ee6a4SAndroid Build Coastguard Worker                         .timer
377*bb4ee6a4SAndroid Build Coastguard Worker                         .clear()
378*bb4ee6a4SAndroid Build Coastguard Worker                         .expect("Failed to clear cpu watchdog timer");
379*bb4ee6a4SAndroid Build Coastguard Worker                 }
380*bb4ee6a4SAndroid Build Coastguard Worker             }
381*bb4ee6a4SAndroid Build Coastguard Worker             VMWDT_REG_LOAD_CNT => {
382*bb4ee6a4SAndroid Build Coastguard Worker                 self.ensure_started();
383*bb4ee6a4SAndroid Build Coastguard Worker                 let (process_id, thread_id) = {
384*bb4ee6a4SAndroid Build Coastguard Worker                     let mut wdts_locked = self.vm_wdts.lock();
385*bb4ee6a4SAndroid Build Coastguard Worker                     let cpu_watchdog = &mut wdts_locked[cpu_index];
386*bb4ee6a4SAndroid Build Coastguard Worker                     (cpu_watchdog.process_id, cpu_watchdog.thread_id)
387*bb4ee6a4SAndroid Build Coastguard Worker                 };
388*bb4ee6a4SAndroid Build Coastguard Worker                 let guest_time_ms = Vmwdt::get_guest_time_ms(process_id, thread_id)
389*bb4ee6a4SAndroid Build Coastguard Worker                     .expect("get_guest_time_ms failed");
390*bb4ee6a4SAndroid Build Coastguard Worker 
391*bb4ee6a4SAndroid Build Coastguard Worker                 let mut wdts_locked = self.vm_wdts.lock();
392*bb4ee6a4SAndroid Build Coastguard Worker                 let cpu_watchdog = &mut wdts_locked[cpu_index];
393*bb4ee6a4SAndroid Build Coastguard Worker                 let next_expiration_interval_ms =
394*bb4ee6a4SAndroid Build Coastguard Worker                     reg_val as u64 * 1000 / cpu_watchdog.timer_freq_hz;
395*bb4ee6a4SAndroid Build Coastguard Worker 
396*bb4ee6a4SAndroid Build Coastguard Worker                 cpu_watchdog.last_guest_time_ms = guest_time_ms;
397*bb4ee6a4SAndroid Build Coastguard Worker                 cpu_watchdog.stall_evt_ppi_triggered = false;
398*bb4ee6a4SAndroid Build Coastguard Worker                 cpu_watchdog.next_expiration_interval_ms = next_expiration_interval_ms as i64;
399*bb4ee6a4SAndroid Build Coastguard Worker 
400*bb4ee6a4SAndroid Build Coastguard Worker                 if cpu_watchdog.is_enabled {
401*bb4ee6a4SAndroid Build Coastguard Worker                     if let Err(_e) = cpu_watchdog
402*bb4ee6a4SAndroid Build Coastguard Worker                         .timer
403*bb4ee6a4SAndroid Build Coastguard Worker                         .reset_oneshot(Duration::from_millis(next_expiration_interval_ms))
404*bb4ee6a4SAndroid Build Coastguard Worker                     {
405*bb4ee6a4SAndroid Build Coastguard Worker                         error!("failed to reset one-shot vcpu time {}", cpu_index);
406*bb4ee6a4SAndroid Build Coastguard Worker                     }
407*bb4ee6a4SAndroid Build Coastguard Worker                     cpu_watchdog.repeating_interval = None;
408*bb4ee6a4SAndroid Build Coastguard Worker                 }
409*bb4ee6a4SAndroid Build Coastguard Worker             }
410*bb4ee6a4SAndroid Build Coastguard Worker             VMWDT_REG_CURRENT_CNT => {
411*bb4ee6a4SAndroid Build Coastguard Worker                 warn!("invalid write to read-only VMWDT_REG_CURRENT_CNT register");
412*bb4ee6a4SAndroid Build Coastguard Worker             }
413*bb4ee6a4SAndroid Build Coastguard Worker             VMWDT_REG_CLOCK_FREQ_HZ => {
414*bb4ee6a4SAndroid Build Coastguard Worker                 let mut wdts_locked = self.vm_wdts.lock();
415*bb4ee6a4SAndroid Build Coastguard Worker                 let cpu_watchdog = &mut wdts_locked[cpu_index];
416*bb4ee6a4SAndroid Build Coastguard Worker 
417*bb4ee6a4SAndroid Build Coastguard Worker                 debug!(
418*bb4ee6a4SAndroid Build Coastguard Worker                     "CPU:{:x} wrote VMWDT_REG_CLOCK_FREQ_HZ {:x}",
419*bb4ee6a4SAndroid Build Coastguard Worker                     cpu_index, reg_val
420*bb4ee6a4SAndroid Build Coastguard Worker                 );
421*bb4ee6a4SAndroid Build Coastguard Worker                 cpu_watchdog.timer_freq_hz = reg_val as u64;
422*bb4ee6a4SAndroid Build Coastguard Worker             }
423*bb4ee6a4SAndroid Build Coastguard Worker             _ => unreachable!(),
424*bb4ee6a4SAndroid Build Coastguard Worker         }
425*bb4ee6a4SAndroid Build Coastguard Worker     }
426*bb4ee6a4SAndroid Build Coastguard Worker }
427*bb4ee6a4SAndroid Build Coastguard Worker 
428*bb4ee6a4SAndroid Build Coastguard Worker impl Suspendable for Vmwdt {
sleep(&mut self) -> anyhow::Result<()>429*bb4ee6a4SAndroid Build Coastguard Worker     fn sleep(&mut self) -> anyhow::Result<()> {
430*bb4ee6a4SAndroid Build Coastguard Worker         if let Some(worker) = self.worker_thread.take() {
431*bb4ee6a4SAndroid Build Coastguard Worker             self.vm_ctrl_tube = Some(worker.stop());
432*bb4ee6a4SAndroid Build Coastguard Worker         }
433*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
434*bb4ee6a4SAndroid Build Coastguard Worker     }
435*bb4ee6a4SAndroid Build Coastguard Worker 
wake(&mut self) -> anyhow::Result<()>436*bb4ee6a4SAndroid Build Coastguard Worker     fn wake(&mut self) -> anyhow::Result<()> {
437*bb4ee6a4SAndroid Build Coastguard Worker         if self.activated {
438*bb4ee6a4SAndroid Build Coastguard Worker             // We do not pass a tube to notify that the worker thread has started on wake.
439*bb4ee6a4SAndroid Build Coastguard Worker             // At this stage, vm_control is blocked on resuming devices and cannot provide the vcpu
440*bb4ee6a4SAndroid Build Coastguard Worker             // PIDs/TIDs yet.
441*bb4ee6a4SAndroid Build Coastguard Worker             // At the same time, the Vcpus are still frozen, which means no MMIO will get
442*bb4ee6a4SAndroid Build Coastguard Worker             // processed, and write will not get triggered.
443*bb4ee6a4SAndroid Build Coastguard Worker             // The request to get PIDs/TIDs should get processed before any MMIO request occurs.
444*bb4ee6a4SAndroid Build Coastguard Worker             self.start(None)?;
445*bb4ee6a4SAndroid Build Coastguard Worker             let mut vm_wdts = self.vm_wdts.lock();
446*bb4ee6a4SAndroid Build Coastguard Worker             for vmwdt in vm_wdts.iter_mut() {
447*bb4ee6a4SAndroid Build Coastguard Worker                 if let Some(interval) = &vmwdt.repeating_interval {
448*bb4ee6a4SAndroid Build Coastguard Worker                     vmwdt
449*bb4ee6a4SAndroid Build Coastguard Worker                         .timer
450*bb4ee6a4SAndroid Build Coastguard Worker                         .reset_repeating(*interval)
451*bb4ee6a4SAndroid Build Coastguard Worker                         .context("failed to write repeating interval")?;
452*bb4ee6a4SAndroid Build Coastguard Worker                 } else if vmwdt.is_enabled {
453*bb4ee6a4SAndroid Build Coastguard Worker                     vmwdt
454*bb4ee6a4SAndroid Build Coastguard Worker                         .timer
455*bb4ee6a4SAndroid Build Coastguard Worker                         .reset_oneshot(Duration::from_millis(
456*bb4ee6a4SAndroid Build Coastguard Worker                             vmwdt.next_expiration_interval_ms as u64,
457*bb4ee6a4SAndroid Build Coastguard Worker                         ))
458*bb4ee6a4SAndroid Build Coastguard Worker                         .context("failed to write oneshot interval")?;
459*bb4ee6a4SAndroid Build Coastguard Worker                 }
460*bb4ee6a4SAndroid Build Coastguard Worker             }
461*bb4ee6a4SAndroid Build Coastguard Worker         }
462*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
463*bb4ee6a4SAndroid Build Coastguard Worker     }
464*bb4ee6a4SAndroid Build Coastguard Worker 
snapshot(&mut self) -> anyhow::Result<serde_json::Value>465*bb4ee6a4SAndroid Build Coastguard Worker     fn snapshot(&mut self) -> anyhow::Result<serde_json::Value> {
466*bb4ee6a4SAndroid Build Coastguard Worker         serde_json::to_value(&VmwdtSnapshot {
467*bb4ee6a4SAndroid Build Coastguard Worker             vm_wdts: self.vm_wdts.clone(),
468*bb4ee6a4SAndroid Build Coastguard Worker             activated: self.activated,
469*bb4ee6a4SAndroid Build Coastguard Worker         })
470*bb4ee6a4SAndroid Build Coastguard Worker         .context("failed to snapshot Vmwdt")
471*bb4ee6a4SAndroid Build Coastguard Worker     }
472*bb4ee6a4SAndroid Build Coastguard Worker 
restore(&mut self, data: serde_json::Value) -> anyhow::Result<()>473*bb4ee6a4SAndroid Build Coastguard Worker     fn restore(&mut self, data: serde_json::Value) -> anyhow::Result<()> {
474*bb4ee6a4SAndroid Build Coastguard Worker         let deser: VmwdtRestore =
475*bb4ee6a4SAndroid Build Coastguard Worker             serde_json::from_value(data).context("failed to deserialize Vmwdt")?;
476*bb4ee6a4SAndroid Build Coastguard Worker         let mut vm_wdts = self.vm_wdts.lock();
477*bb4ee6a4SAndroid Build Coastguard Worker         for (vmwdt_restore, vmwdt) in deser.vm_wdts.iter().zip(vm_wdts.iter_mut()) {
478*bb4ee6a4SAndroid Build Coastguard Worker             vmwdt.is_enabled = vmwdt_restore.is_enabled;
479*bb4ee6a4SAndroid Build Coastguard Worker             vmwdt.timer_freq_hz = vmwdt_restore.timer_freq_hz;
480*bb4ee6a4SAndroid Build Coastguard Worker             vmwdt.last_guest_time_ms = vmwdt_restore.last_guest_time_ms;
481*bb4ee6a4SAndroid Build Coastguard Worker             vmwdt.next_expiration_interval_ms = vmwdt_restore.next_expiration_interval_ms;
482*bb4ee6a4SAndroid Build Coastguard Worker             vmwdt.repeating_interval = vmwdt_restore.repeating_interval;
483*bb4ee6a4SAndroid Build Coastguard Worker         }
484*bb4ee6a4SAndroid Build Coastguard Worker         self.activated = deser.activated;
485*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
486*bb4ee6a4SAndroid Build Coastguard Worker     }
487*bb4ee6a4SAndroid Build Coastguard Worker }
488*bb4ee6a4SAndroid Build Coastguard Worker 
489*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)]
490*bb4ee6a4SAndroid Build Coastguard Worker mod tests {
491*bb4ee6a4SAndroid Build Coastguard Worker     use std::process;
492*bb4ee6a4SAndroid Build Coastguard Worker     use std::thread::sleep;
493*bb4ee6a4SAndroid Build Coastguard Worker 
494*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(any(target_os = "linux", target_os = "android"))]
495*bb4ee6a4SAndroid Build Coastguard Worker     use base::gettid;
496*bb4ee6a4SAndroid Build Coastguard Worker     use base::poll_assert;
497*bb4ee6a4SAndroid Build Coastguard Worker     use base::Tube;
498*bb4ee6a4SAndroid Build Coastguard Worker 
499*bb4ee6a4SAndroid Build Coastguard Worker     use super::*;
500*bb4ee6a4SAndroid Build Coastguard Worker 
501*bb4ee6a4SAndroid Build Coastguard Worker     const AARCH64_VMWDT_ADDR: u64 = 0x3000;
502*bb4ee6a4SAndroid Build Coastguard Worker     const TEST_VMWDT_CPU_NO: usize = 0x1;
503*bb4ee6a4SAndroid Build Coastguard Worker 
vmwdt_bus_address(offset: u64) -> BusAccessInfo504*bb4ee6a4SAndroid Build Coastguard Worker     fn vmwdt_bus_address(offset: u64) -> BusAccessInfo {
505*bb4ee6a4SAndroid Build Coastguard Worker         BusAccessInfo {
506*bb4ee6a4SAndroid Build Coastguard Worker             offset,
507*bb4ee6a4SAndroid Build Coastguard Worker             address: AARCH64_VMWDT_ADDR,
508*bb4ee6a4SAndroid Build Coastguard Worker             id: 0,
509*bb4ee6a4SAndroid Build Coastguard Worker         }
510*bb4ee6a4SAndroid Build Coastguard Worker     }
511*bb4ee6a4SAndroid Build Coastguard Worker 
512*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
test_watchdog_internal_timer()513*bb4ee6a4SAndroid Build Coastguard Worker     fn test_watchdog_internal_timer() {
514*bb4ee6a4SAndroid Build Coastguard Worker         let (vm_evt_wrtube, _vm_evt_rdtube) = Tube::directional_pair().unwrap();
515*bb4ee6a4SAndroid Build Coastguard Worker         let (vm_ctrl_wrtube, vm_ctrl_rdtube) = Tube::pair().unwrap();
516*bb4ee6a4SAndroid Build Coastguard Worker         let irq = IrqEdgeEvent::new().unwrap();
517*bb4ee6a4SAndroid Build Coastguard Worker         #[cfg(any(target_os = "linux", target_os = "android"))]
518*bb4ee6a4SAndroid Build Coastguard Worker         {
519*bb4ee6a4SAndroid Build Coastguard Worker             vm_ctrl_wrtube
520*bb4ee6a4SAndroid Build Coastguard Worker                 .send(&VmResponse::VcpuPidTidResponse {
521*bb4ee6a4SAndroid Build Coastguard Worker                     pid_tid_map: BTreeMap::from([(0, (process::id(), gettid() as u32))]),
522*bb4ee6a4SAndroid Build Coastguard Worker                 })
523*bb4ee6a4SAndroid Build Coastguard Worker                 .unwrap();
524*bb4ee6a4SAndroid Build Coastguard Worker         }
525*bb4ee6a4SAndroid Build Coastguard Worker         let mut device = Vmwdt::new(TEST_VMWDT_CPU_NO, vm_evt_wrtube, irq, vm_ctrl_rdtube).unwrap();
526*bb4ee6a4SAndroid Build Coastguard Worker 
527*bb4ee6a4SAndroid Build Coastguard Worker         // Configure the watchdog device, 2Hz internal clock
528*bb4ee6a4SAndroid Build Coastguard Worker         device.write(
529*bb4ee6a4SAndroid Build Coastguard Worker             vmwdt_bus_address(VMWDT_REG_CLOCK_FREQ_HZ as u64),
530*bb4ee6a4SAndroid Build Coastguard Worker             &[10, 0, 0, 0],
531*bb4ee6a4SAndroid Build Coastguard Worker         );
532*bb4ee6a4SAndroid Build Coastguard Worker         device.write(vmwdt_bus_address(VMWDT_REG_LOAD_CNT as u64), &[1, 0, 0, 0]);
533*bb4ee6a4SAndroid Build Coastguard Worker         device.write(vmwdt_bus_address(VMWDT_REG_STATUS as u64), &[1, 0, 0, 0]);
534*bb4ee6a4SAndroid Build Coastguard Worker         let next_expiration_ms = {
535*bb4ee6a4SAndroid Build Coastguard Worker             let mut vmwdt_locked = device.vm_wdts.lock();
536*bb4ee6a4SAndroid Build Coastguard Worker             // In the test scenario the guest does not interpret the /proc/stat::guest_time, thus
537*bb4ee6a4SAndroid Build Coastguard Worker             // the function get_guest_time() returns 0
538*bb4ee6a4SAndroid Build Coastguard Worker             vmwdt_locked[0].last_guest_time_ms = 10;
539*bb4ee6a4SAndroid Build Coastguard Worker             vmwdt_locked[0].next_expiration_interval_ms
540*bb4ee6a4SAndroid Build Coastguard Worker         };
541*bb4ee6a4SAndroid Build Coastguard Worker 
542*bb4ee6a4SAndroid Build Coastguard Worker         // Poll multiple times as we don't get a signal when the watchdog thread has run.
543*bb4ee6a4SAndroid Build Coastguard Worker         poll_assert!(10, || {
544*bb4ee6a4SAndroid Build Coastguard Worker             sleep(Duration::from_millis(50));
545*bb4ee6a4SAndroid Build Coastguard Worker             let vmwdt_locked = device.vm_wdts.lock();
546*bb4ee6a4SAndroid Build Coastguard Worker             // Verify that our timer expired and the next_expiration_interval_ms changed
547*bb4ee6a4SAndroid Build Coastguard Worker             vmwdt_locked[0].next_expiration_interval_ms != next_expiration_ms
548*bb4ee6a4SAndroid Build Coastguard Worker         });
549*bb4ee6a4SAndroid Build Coastguard Worker     }
550*bb4ee6a4SAndroid Build Coastguard Worker 
551*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
test_watchdog_expiration()552*bb4ee6a4SAndroid Build Coastguard Worker     fn test_watchdog_expiration() {
553*bb4ee6a4SAndroid Build Coastguard Worker         let (vm_evt_wrtube, vm_evt_rdtube) = Tube::directional_pair().unwrap();
554*bb4ee6a4SAndroid Build Coastguard Worker         let (vm_ctrl_wrtube, vm_ctrl_rdtube) = Tube::pair().unwrap();
555*bb4ee6a4SAndroid Build Coastguard Worker         let irq = IrqEdgeEvent::new().unwrap();
556*bb4ee6a4SAndroid Build Coastguard Worker         #[cfg(any(target_os = "linux", target_os = "android"))]
557*bb4ee6a4SAndroid Build Coastguard Worker         {
558*bb4ee6a4SAndroid Build Coastguard Worker             vm_ctrl_wrtube
559*bb4ee6a4SAndroid Build Coastguard Worker                 .send(&VmResponse::VcpuPidTidResponse {
560*bb4ee6a4SAndroid Build Coastguard Worker                     pid_tid_map: BTreeMap::from([(0, (process::id(), gettid() as u32))]),
561*bb4ee6a4SAndroid Build Coastguard Worker                 })
562*bb4ee6a4SAndroid Build Coastguard Worker                 .unwrap();
563*bb4ee6a4SAndroid Build Coastguard Worker         }
564*bb4ee6a4SAndroid Build Coastguard Worker         let mut device = Vmwdt::new(TEST_VMWDT_CPU_NO, vm_evt_wrtube, irq, vm_ctrl_rdtube).unwrap();
565*bb4ee6a4SAndroid Build Coastguard Worker 
566*bb4ee6a4SAndroid Build Coastguard Worker         // Configure the watchdog device, 2Hz internal clock
567*bb4ee6a4SAndroid Build Coastguard Worker         device.write(
568*bb4ee6a4SAndroid Build Coastguard Worker             vmwdt_bus_address(VMWDT_REG_CLOCK_FREQ_HZ as u64),
569*bb4ee6a4SAndroid Build Coastguard Worker             &[10, 0, 0, 0],
570*bb4ee6a4SAndroid Build Coastguard Worker         );
571*bb4ee6a4SAndroid Build Coastguard Worker         device.write(vmwdt_bus_address(VMWDT_REG_LOAD_CNT as u64), &[1, 0, 0, 0]);
572*bb4ee6a4SAndroid Build Coastguard Worker         device.write(vmwdt_bus_address(VMWDT_REG_STATUS as u64), &[1, 0, 0, 0]);
573*bb4ee6a4SAndroid Build Coastguard Worker         // In the test scenario the guest does not interpret the /proc/stat::guest_time, thus
574*bb4ee6a4SAndroid Build Coastguard Worker         // the function get_guest_time() returns 0
575*bb4ee6a4SAndroid Build Coastguard Worker         device.vm_wdts.lock()[0].last_guest_time_ms = -100;
576*bb4ee6a4SAndroid Build Coastguard Worker 
577*bb4ee6a4SAndroid Build Coastguard Worker         // Check that the interrupt has raised
578*bb4ee6a4SAndroid Build Coastguard Worker         poll_assert!(10, || {
579*bb4ee6a4SAndroid Build Coastguard Worker             sleep(Duration::from_millis(50));
580*bb4ee6a4SAndroid Build Coastguard Worker             let vmwdt_locked = device.vm_wdts.lock();
581*bb4ee6a4SAndroid Build Coastguard Worker             vmwdt_locked[0].stall_evt_ppi_triggered
582*bb4ee6a4SAndroid Build Coastguard Worker         });
583*bb4ee6a4SAndroid Build Coastguard Worker 
584*bb4ee6a4SAndroid Build Coastguard Worker         // Simulate that the time has passed since the last expiration
585*bb4ee6a4SAndroid Build Coastguard Worker         device.vm_wdts.lock()[0].last_guest_time_ms = -100;
586*bb4ee6a4SAndroid Build Coastguard Worker 
587*bb4ee6a4SAndroid Build Coastguard Worker         // Poll multiple times as we don't get a signal when the watchdog thread has run.
588*bb4ee6a4SAndroid Build Coastguard Worker         poll_assert!(10, || {
589*bb4ee6a4SAndroid Build Coastguard Worker             sleep(Duration::from_millis(50));
590*bb4ee6a4SAndroid Build Coastguard Worker             match vm_evt_rdtube.recv::<VmEventType>() {
591*bb4ee6a4SAndroid Build Coastguard Worker                 Ok(vm_event) => vm_event == VmEventType::WatchdogReset,
592*bb4ee6a4SAndroid Build Coastguard Worker                 Err(_e) => false,
593*bb4ee6a4SAndroid Build Coastguard Worker             }
594*bb4ee6a4SAndroid Build Coastguard Worker         });
595*bb4ee6a4SAndroid Build Coastguard Worker     }
596*bb4ee6a4SAndroid Build Coastguard Worker }
597