xref: /qemu/hw/watchdog/watchdog.c (revision 30e5210a706ca6b52cbefa8b71e40ae614ffd6e5)
19dd986ccSRichard W.M. Jones /*
29dd986ccSRichard W.M. Jones  * Virtual hardware watchdog.
39dd986ccSRichard W.M. Jones  *
49dd986ccSRichard W.M. Jones  * Copyright (C) 2009 Red Hat Inc.
59dd986ccSRichard W.M. Jones  *
69dd986ccSRichard W.M. Jones  * This program is free software; you can redistribute it and/or
79dd986ccSRichard W.M. Jones  * modify it under the terms of the GNU General Public License
89dd986ccSRichard W.M. Jones  * as published by the Free Software Foundation; either version 2
99dd986ccSRichard W.M. Jones  * of the License, or (at your option) any later version.
109dd986ccSRichard W.M. Jones  *
119dd986ccSRichard W.M. Jones  * This program is distributed in the hope that it will be useful,
129dd986ccSRichard W.M. Jones  * but WITHOUT ANY WARRANTY; without even the implied warranty of
139dd986ccSRichard W.M. Jones  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
149dd986ccSRichard W.M. Jones  * GNU General Public License for more details.
159dd986ccSRichard W.M. Jones  *
169dd986ccSRichard W.M. Jones  * You should have received a copy of the GNU General Public License
178167ee88SBlue Swirl  * along with this program; if not, see <http://www.gnu.org/licenses/>.
189dd986ccSRichard W.M. Jones  *
199dd986ccSRichard W.M. Jones  * By Richard W.M. Jones (rjones@redhat.com).
209dd986ccSRichard W.M. Jones  */
219dd986ccSRichard W.M. Jones 
229dd986ccSRichard W.M. Jones #include "qemu-common.h"
231de7afc9SPaolo Bonzini #include "qemu/option.h"
241de7afc9SPaolo Bonzini #include "qemu/config-file.h"
251de7afc9SPaolo Bonzini #include "qemu/queue.h"
267b1b5d19SPaolo Bonzini #include "qapi/qmp/types.h"
279c17d615SPaolo Bonzini #include "sysemu/sysemu.h"
280d09e41aSPaolo Bonzini #include "sysemu/watchdog.h"
2999eaf09cSWenchao Xia #include "qapi-event.h"
309dd986ccSRichard W.M. Jones 
3188b3be20SMarkus Armbruster /* Possible values for action parameter. */
3288b3be20SMarkus Armbruster #define WDT_RESET        1	/* Hard reset. */
3388b3be20SMarkus Armbruster #define WDT_SHUTDOWN     2	/* Shutdown. */
3488b3be20SMarkus Armbruster #define WDT_POWEROFF     3	/* Quit. */
3588b3be20SMarkus Armbruster #define WDT_PAUSE        4	/* Pause. */
3688b3be20SMarkus Armbruster #define WDT_DEBUG        5	/* Prints a message and continues running. */
3788b3be20SMarkus Armbruster #define WDT_NONE         6	/* Do nothing. */
3888b3be20SMarkus Armbruster 
3988b3be20SMarkus Armbruster static int watchdog_action = WDT_RESET;
4072cf2d4fSBlue Swirl static QLIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list;
419dd986ccSRichard W.M. Jones 
429dd986ccSRichard W.M. Jones void watchdog_add_model(WatchdogTimerModel *model)
439dd986ccSRichard W.M. Jones {
4472cf2d4fSBlue Swirl     QLIST_INSERT_HEAD(&watchdog_list, model, entry);
459dd986ccSRichard W.M. Jones }
469dd986ccSRichard W.M. Jones 
479dd986ccSRichard W.M. Jones /* Returns:
489dd986ccSRichard W.M. Jones  *   0 = continue
499dd986ccSRichard W.M. Jones  *   1 = exit program with error
509dd986ccSRichard W.M. Jones  *   2 = exit program without error
519dd986ccSRichard W.M. Jones  */
529dd986ccSRichard W.M. Jones int select_watchdog(const char *p)
539dd986ccSRichard W.M. Jones {
549dd986ccSRichard W.M. Jones     WatchdogTimerModel *model;
5509aaa160SMarkus Armbruster     QemuOpts *opts;
569dd986ccSRichard W.M. Jones 
579dd986ccSRichard W.M. Jones     /* -watchdog ? lists available devices and exits cleanly. */
58c8057f95SPeter Maydell     if (is_help_option(p)) {
5972cf2d4fSBlue Swirl         QLIST_FOREACH(model, &watchdog_list, entry) {
609dd986ccSRichard W.M. Jones             fprintf(stderr, "\t%s\t%s\n",
619dd986ccSRichard W.M. Jones                      model->wdt_name, model->wdt_description);
629dd986ccSRichard W.M. Jones         }
639dd986ccSRichard W.M. Jones         return 2;
649dd986ccSRichard W.M. Jones     }
659dd986ccSRichard W.M. Jones 
6672cf2d4fSBlue Swirl     QLIST_FOREACH(model, &watchdog_list, entry) {
679dd986ccSRichard W.M. Jones         if (strcasecmp(model->wdt_name, p) == 0) {
6809aaa160SMarkus Armbruster             /* add the device */
6987ea75d5SPeter Crosthwaite             opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
7087ea75d5SPeter Crosthwaite                                     &error_abort);
7109aaa160SMarkus Armbruster             qemu_opt_set(opts, "driver", p);
729dd986ccSRichard W.M. Jones             return 0;
739dd986ccSRichard W.M. Jones         }
749dd986ccSRichard W.M. Jones     }
759dd986ccSRichard W.M. Jones 
769dd986ccSRichard W.M. Jones     fprintf(stderr, "Unknown -watchdog device. Supported devices are:\n");
7772cf2d4fSBlue Swirl     QLIST_FOREACH(model, &watchdog_list, entry) {
789dd986ccSRichard W.M. Jones         fprintf(stderr, "\t%s\t%s\n",
799dd986ccSRichard W.M. Jones                  model->wdt_name, model->wdt_description);
809dd986ccSRichard W.M. Jones     }
819dd986ccSRichard W.M. Jones     return 1;
829dd986ccSRichard W.M. Jones }
839dd986ccSRichard W.M. Jones 
849dd986ccSRichard W.M. Jones int select_watchdog_action(const char *p)
859dd986ccSRichard W.M. Jones {
869dd986ccSRichard W.M. Jones     if (strcasecmp(p, "reset") == 0)
879dd986ccSRichard W.M. Jones         watchdog_action = WDT_RESET;
889dd986ccSRichard W.M. Jones     else if (strcasecmp(p, "shutdown") == 0)
899dd986ccSRichard W.M. Jones         watchdog_action = WDT_SHUTDOWN;
909dd986ccSRichard W.M. Jones     else if (strcasecmp(p, "poweroff") == 0)
919dd986ccSRichard W.M. Jones         watchdog_action = WDT_POWEROFF;
929dd986ccSRichard W.M. Jones     else if (strcasecmp(p, "pause") == 0)
939dd986ccSRichard W.M. Jones         watchdog_action = WDT_PAUSE;
949dd986ccSRichard W.M. Jones     else if (strcasecmp(p, "debug") == 0)
959dd986ccSRichard W.M. Jones         watchdog_action = WDT_DEBUG;
969dd986ccSRichard W.M. Jones     else if (strcasecmp(p, "none") == 0)
979dd986ccSRichard W.M. Jones         watchdog_action = WDT_NONE;
989dd986ccSRichard W.M. Jones     else
999dd986ccSRichard W.M. Jones         return -1;
1009dd986ccSRichard W.M. Jones 
1019dd986ccSRichard W.M. Jones     return 0;
1029dd986ccSRichard W.M. Jones }
1039dd986ccSRichard W.M. Jones 
1049dd986ccSRichard W.M. Jones /* This actually performs the "action" once a watchdog has expired,
1059dd986ccSRichard W.M. Jones  * ie. reboot, shutdown, exit, etc.
1069dd986ccSRichard W.M. Jones  */
1079dd986ccSRichard W.M. Jones void watchdog_perform_action(void)
1089dd986ccSRichard W.M. Jones {
1099dd986ccSRichard W.M. Jones     switch (watchdog_action) {
1109dd986ccSRichard W.M. Jones     case WDT_RESET:             /* same as 'system_reset' in monitor */
11199eaf09cSWenchao Xia         qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_RESET, &error_abort);
1129dd986ccSRichard W.M. Jones         qemu_system_reset_request();
1139dd986ccSRichard W.M. Jones         break;
1149dd986ccSRichard W.M. Jones 
1159dd986ccSRichard W.M. Jones     case WDT_SHUTDOWN:          /* same as 'system_powerdown' in monitor */
11699eaf09cSWenchao Xia         qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_SHUTDOWN, &error_abort);
1179dd986ccSRichard W.M. Jones         qemu_system_powerdown_request();
1189dd986ccSRichard W.M. Jones         break;
1199dd986ccSRichard W.M. Jones 
1209dd986ccSRichard W.M. Jones     case WDT_POWEROFF:          /* same as 'quit' command in monitor */
12199eaf09cSWenchao Xia         qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_POWEROFF, &error_abort);
1229dd986ccSRichard W.M. Jones         exit(0);
1239dd986ccSRichard W.M. Jones 
1249dd986ccSRichard W.M. Jones     case WDT_PAUSE:             /* same as 'stop' command in monitor */
125*30e5210aSPaolo Bonzini         /* In a timer callback, when vm_stop calls qemu_clock_enable
126*30e5210aSPaolo Bonzini          * you would get a deadlock.  Bypass the problem.
127*30e5210aSPaolo Bonzini          */
128*30e5210aSPaolo Bonzini         qemu_system_vmstop_request_prepare();
12999eaf09cSWenchao Xia         qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_PAUSE, &error_abort);
130*30e5210aSPaolo Bonzini         qemu_system_vmstop_request(RUN_STATE_WATCHDOG);
1319dd986ccSRichard W.M. Jones         break;
1329dd986ccSRichard W.M. Jones 
1339dd986ccSRichard W.M. Jones     case WDT_DEBUG:
13499eaf09cSWenchao Xia         qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_DEBUG, &error_abort);
1359dd986ccSRichard W.M. Jones         fprintf(stderr, "watchdog: timer fired\n");
1369dd986ccSRichard W.M. Jones         break;
1379dd986ccSRichard W.M. Jones 
1389dd986ccSRichard W.M. Jones     case WDT_NONE:
13999eaf09cSWenchao Xia         qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_NONE, &error_abort);
1409dd986ccSRichard W.M. Jones         break;
1419dd986ccSRichard W.M. Jones     }
1429dd986ccSRichard W.M. Jones }
143