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