blob: 5ba904403c0f1ef465701cd9133e72c9b4a9bfbc [file]
// Copyright 2020 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
use super::{
hw_watchdog_get_last_pet_time, hw_watchdog_get_timeout_nsec, hw_watchdog_is_enabled,
hw_watchdog_is_petting_suppressed, hw_watchdog_pet, hw_watchdog_present,
hw_watchdog_set_enabled, hw_watchdog_suppress_petting,
};
use crate::arch_rs;
use crate::console::{CMD_AVAIL_NORMAL, CmdArgs, static_command};
use crate::debuglog_rs::dlog_panic_start;
use crate::kernel::thread;
use crate::kernel::types::BOOT_CPU_ID;
use crate::platform_rs::halt_helper::platform_halt_secondary_cpus;
use crate::platform_rs::timer::{
InstantMono, current_boot_time, current_mono_time, zx_msec, zx_sec,
};
use core::ffi::{CStr, c_int};
use kprint::kprint;
use zx_status::Status;
fn usage(cmd_name: &CStr) {
kprint!("Usage:\n");
kprint!(
"{:s} status : show the recent status of the hardware watchdog subsystem.\n",
cmd_name
);
kprint!("{:s} pet : force an immediate pet of the watchdog.\n", cmd_name);
kprint!("{:s} enable : attempt to enable the watchdog.\n", cmd_name);
kprint!("{:s} disable : attempt to disable the watchdog.\n", cmd_name);
kprint!("{:s} force : force the watchdog to fire by locking up all cores.\n", cmd_name);
kprint!("{:s} suppress : Pet the WDT one last time, then suppress future pets.\n", cmd_name);
kprint!("{:s} help : show this message.\n", cmd_name);
}
enum Cmd {
Status,
Pet,
Enable,
Disable,
Force,
Suppress,
}
/// # Safety
/// `argv` must point to a valid array of `CmdArgs` of length `argc`, with `argc >= 1`.
unsafe extern "C" fn cmd_watchdog(argc: c_int, argv: *const CmdArgs, _flags: u32) -> c_int {
// SAFETY: The console framework passes `argc` valid arguments in `argv`.
let args = unsafe { core::slice::from_raw_parts(argv, argc as usize) };
// SAFETY: The console framework passes null-terminated argument strings.
let cmd_name = unsafe { CStr::from_ptr(args[0].arg_str) };
if argc < 2 {
kprint!("Not enough arguments.\n");
usage(cmd_name);
return -1;
}
// SAFETY: The console framework passes null-terminated argument strings.
let arg1 = unsafe { CStr::from_ptr(args[1].arg_str) };
let command = if arg1 == c"status" {
Cmd::Status
} else if arg1 == c"pet" {
Cmd::Pet
} else if arg1 == c"enable" {
Cmd::Enable
} else if arg1 == c"disable" {
Cmd::Disable
} else if arg1 == c"force" {
Cmd::Force
} else if arg1 == c"suppress" {
Cmd::Suppress
} else if arg1 == c"help" {
usage(cmd_name);
return 0;
} else {
kprint!("Unrecognized command.\n");
usage(cmd_name);
return -1;
};
// SAFETY: The layer always resolves a `'static` driver, set once in early boot.
if !unsafe { hw_watchdog_present() } {
kprint!("There is no hardware watchdog present in this system.\n");
return 0;
}
match command {
Cmd::Status => {
// SAFETY: The layer always resolves a `'static` driver, set once in early boot.
let last_pet = unsafe { hw_watchdog_get_last_pet_time() };
let now = current_boot_time().0;
// SAFETY: The layer always resolves a `'static` driver, set once in early boot.
kprint!(
"Enabled : {:s}\n",
if unsafe { hw_watchdog_is_enabled() } { "yes" } else { "no" }
);
// SAFETY: The layer always resolves a `'static` driver, set once in early boot.
kprint!("Timeout : {} mSec\n", unsafe { hw_watchdog_get_timeout_nsec() } / zx_msec(1));
kprint!(
"Last Pet : {}.{:03} ({} mSec ago)\n",
last_pet / zx_sec(1),
(last_pet / zx_msec(1)) % 1000,
(now - last_pet) / zx_msec(1)
);
// SAFETY: The layer always resolves a `'static` driver, set once in early boot.
kprint!(
"Petting : {:s}\n",
if unsafe { hw_watchdog_is_petting_suppressed() } {
"Suppressed"
} else {
"Enabled"
}
);
}
Cmd::Pet => {
kprint!("Watchdog has been pet. They're a good dog! (yes they are!!)\n");
}
Cmd::Enable => {
// SAFETY: The layer always resolves a `'static` driver, set once in early boot.
let res = unsafe { hw_watchdog_set_enabled(true) };
if res == Err(Status::NOT_SUPPORTED) {
kprint!("Watchdog does not support enabling.\n");
return Status::result_into_raw(res);
} else if res != Ok(()) {
kprint!("Error enabling watchdog ({})\n", Status::result_into_raw(res));
return Status::result_into_raw(res);
} else {
kprint!("Watchdog enabled.\n");
}
}
Cmd::Disable => {
// SAFETY: The layer always resolves a `'static` driver, set once in early boot.
let res = unsafe { hw_watchdog_set_enabled(false) };
if res == Err(Status::NOT_SUPPORTED) {
kprint!("Watchdog does not support disabling.\n");
return Status::result_into_raw(res);
} else if res != Ok(()) {
kprint!("Error disabling watchdog ({})\n", Status::result_into_raw(res));
return Status::result_into_raw(res);
} else {
kprint!("Watchdog disabled.\n");
}
}
Cmd::Force => {
// SAFETY: The layer always resolves a `'static` driver, set once in early boot.
if !unsafe { hw_watchdog_is_enabled() } {
kprint!("Watchdog is not enabled. Enable the watchdog first.\n");
return Status::BAD_STATE.into_raw();
}
// In order to _really_ wedge the system we...
// 1) Migrate our thread to the boot core. (this pins the thread there too)
// 2) Halt all of the secondary cores
// 3) Disable interrupts.
// 4) Spin forever.
//
// SAFETY: `BOOT_CPU_ID` is active, and console commands run in normal thread context
// with no spinlocks or `ChainLockTransaction` held.
unsafe { thread::migrate_to_cpu(BOOT_CPU_ID) };
let status = platform_halt_secondary_cpus(InstantMono::INFINITE);
debug_assert!(status == Ok(()));
arch_rs::disable_ints();
// Make sure that our kprint goes directly to the UART, bypassing any
// buffering which is not going to get drained now that we have stopped
// the system.
dlog_panic_start();
// SAFETY: The layer always resolves a `'static` driver, set once in early boot.
let deadline = unsafe { hw_watchdog_get_last_pet_time() }
.saturating_add(unsafe { hw_watchdog_get_timeout_nsec() });
kprint!(
"System wedged! Watchdog will fire in {} mSec\n",
(deadline - current_mono_time().0) / zx_msec(1)
);
// Spin forever. The watchdog should reboot us.
loop {
libarch::r#yield();
}
}
Cmd::Suppress => {
// SAFETY: The layer always resolves a `'static` driver, set once in early boot.
unsafe {
hw_watchdog_pet();
hw_watchdog_suppress_petting(true);
}
// SAFETY: The layer always resolves a `'static` driver, set once in early boot.
if !unsafe { hw_watchdog_is_enabled() } {
kprint!("WDT petting is now suppressed, but the WDT is not currently enabled.\n");
} else {
// SAFETY: The layer always resolves a `'static` driver, set once in early boot.
let deadline = unsafe { hw_watchdog_get_last_pet_time() }
.saturating_add(unsafe { hw_watchdog_get_timeout_nsec() });
kprint!(
"WDT petting is now suppressed, WDT will fire in {} mSec\n",
(deadline - current_mono_time().0) / zx_msec(1)
);
}
}
}
0
}
static_command!(
CMD_WDT,
c"wdt".as_ptr(),
c"hardware watchdog commands".as_ptr(),
cmd_watchdog,
CMD_AVAIL_NORMAL
);