blob: 1e09c760ebfee5feb04c88245762d8f301c8289e [file] [log] [blame]
// 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
#if LK_DEBUGLEVEL > 1
#include <lib/console.h>
#include <lib/debuglog.h>
#include <platform.h>
#include <dev/hw_watchdog.h>
#include <platform/halt_helper.h>
static void usage(const char* cmd_name) {
printf("Usage:\n");
printf("%s status : show the recent status of the hardware watchdog subsystem.\n", cmd_name);
printf("%s pet : force an immediate pet of the watchdog.\n", cmd_name);
printf("%s enable : attempt to enable the watchdog.\n", cmd_name);
printf("%s disable : attempt to disable the watchdog.\n", cmd_name);
printf("%s force : force the watchdog to fire by locking up all cores.\n", cmd_name);
printf("%s suppress : Pet the WDT one last time, then suppress future pets.\n", cmd_name);
printf("%s help : show this message.\n", cmd_name);
}
enum class Cmd {
Status,
Pet,
Enable,
Disable,
Force,
Suppress,
};
static int cmd_watchdog(int argc, const cmd_args* argv, uint32_t flags) {
if (argc < 2) {
printf("Not enough arguments.\n");
usage(argv[0].str);
return -1;
}
Cmd command;
if (!strcmp(argv[1].str, "status")) {
command = Cmd::Status;
} else if (!strcmp(argv[1].str, "pet")) {
command = Cmd::Pet;
} else if (!strcmp(argv[1].str, "enable")) {
command = Cmd::Enable;
} else if (!strcmp(argv[1].str, "disable")) {
command = Cmd::Disable;
} else if (!strcmp(argv[1].str, "force")) {
command = Cmd::Force;
} else if (!strcmp(argv[1].str, "suppress")) {
command = Cmd::Suppress;
} else if (!strcmp(argv[1].str, "help")) {
usage(argv[0].str);
return 0;
} else {
printf("Unrecognized command.\n");
usage(argv[0].str);
return -1;
}
if (!hw_watchdog_present()) {
printf("There is no hardware watchdog present in this system.\n");
return 0;
}
switch (command) {
case Cmd::Status: {
zx_time_t last_pet = hw_watchdog_get_last_pet_time();
zx_time_t now = current_time();
printf("Enabled : %s\n", hw_watchdog_is_enabled() ? "yes" : "no");
printf("Timeout : %ld mSec\n", hw_watchdog_get_timeout_nsec() / ZX_MSEC(1));
printf("Last Pet : %ld.%03ld (%ld mSec ago)\n", last_pet / ZX_SEC(1),
(last_pet / ZX_MSEC(1)) % 1000, (now - last_pet) / ZX_MSEC(1));
printf("Petting : %s\n", hw_watchdog_is_petting_suppressed() ? "Suppressed" : "Enabled");
} break;
case Cmd::Pet: {
printf("Watchdog has been pet. They're a good dog! (yes they are!!)\n");
} break;
case Cmd::Enable: {
zx_status_t res;
res = hw_watchdog_set_enabled(true);
if (res == ZX_ERR_NOT_SUPPORTED) {
printf("Watchdog does not support enabling.\n");
return res;
} else if (res != ZX_OK) {
printf("Error enabling watchdog (%d)\n", res);
return res;
} else {
printf("Watchdog enabled.\n");
}
} break;
case Cmd::Disable: {
zx_status_t res;
res = hw_watchdog_set_enabled(false);
if (res == ZX_ERR_NOT_SUPPORTED) {
printf("Watchdog does not support disabling.\n");
return res;
} else if (res != ZX_OK) {
printf("Error disabling watchdog (%d)\n", res);
return res;
} else {
printf("Watchdog disabled.\n");
}
} break;
case Cmd::Force: {
if (!hw_watchdog_is_enabled()) {
printf("Watchdog is not enabled. Enable the watchdog first.\n");
return ZX_ERR_BAD_STATE;
}
// 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.
//
Thread::Current::MigrateToCpu(BOOT_CPU_ID);
[[maybe_unused]] zx_status_t status = platform_halt_secondary_cpus(ZX_TIME_INFINITE);
DEBUG_ASSERT(status == ZX_OK);
arch_disable_ints();
// Make sure that our printf goes directly to the UART, bypassing any
// buffering which is not going to get drained now that we have stopped
// the system.
dlog_force_panic();
zx_time_t deadline =
zx_time_add_duration(hw_watchdog_get_last_pet_time(), hw_watchdog_get_timeout_nsec());
printf("System wedged! Watchdog will fire in %ld mSec\n",
(deadline - current_time()) / ZX_MSEC(1));
// Spin forever. The watchdog should reboot us.
while (true)
;
} break;
case Cmd::Suppress: {
hw_watchdog_pet();
hw_watchdog_suppress_petting(true);
if (!hw_watchdog_is_enabled()) {
printf("WDT petting is now suppressed, but the WDT is not currently enabled.\n");
} else {
zx_time_t deadline =
zx_time_add_duration(hw_watchdog_get_last_pet_time(), hw_watchdog_get_timeout_nsec());
printf("WDT petting is now suppressed, WDT will fire in %ld mSec\n",
(deadline - current_time()) / ZX_MSEC(1));
}
} break;
};
return 0;
}
STATIC_COMMAND_START
STATIC_COMMAND("wdt", "hardware watchdog commands", &cmd_watchdog)
STATIC_COMMAND_END(gfx)
#endif