xref: /qemu/hw/watchdog/watchdog.c (revision 0430891ce162b986c6e02a7729a942ecd2a32ca4)
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 
22*0430891cSPeter Maydell #include "qemu/osdep.h"
239dd986ccSRichard W.M. Jones #include "qemu-common.h"
241de7afc9SPaolo Bonzini #include "qemu/option.h"
251de7afc9SPaolo Bonzini #include "qemu/config-file.h"
261de7afc9SPaolo Bonzini #include "qemu/queue.h"
277b1b5d19SPaolo Bonzini #include "qapi/qmp/types.h"
289c17d615SPaolo Bonzini #include "sysemu/sysemu.h"
290d09e41aSPaolo Bonzini #include "sysemu/watchdog.h"
3099eaf09cSWenchao Xia #include "qapi-event.h"
31795dc6e4SMao Chuan Li #include "hw/nmi.h"
329dd986ccSRichard W.M. Jones 
3388b3be20SMarkus Armbruster static int watchdog_action = WDT_RESET;
3472cf2d4fSBlue Swirl static QLIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list;
359dd986ccSRichard W.M. Jones 
369dd986ccSRichard W.M. Jones void watchdog_add_model(WatchdogTimerModel *model)
379dd986ccSRichard W.M. Jones {
3872cf2d4fSBlue Swirl     QLIST_INSERT_HEAD(&watchdog_list, model, entry);
399dd986ccSRichard W.M. Jones }
409dd986ccSRichard W.M. Jones 
419dd986ccSRichard W.M. Jones /* Returns:
429dd986ccSRichard W.M. Jones  *   0 = continue
439dd986ccSRichard W.M. Jones  *   1 = exit program with error
449dd986ccSRichard W.M. Jones  *   2 = exit program without error
459dd986ccSRichard W.M. Jones  */
469dd986ccSRichard W.M. Jones int select_watchdog(const char *p)
479dd986ccSRichard W.M. Jones {
489dd986ccSRichard W.M. Jones     WatchdogTimerModel *model;
4909aaa160SMarkus Armbruster     QemuOpts *opts;
509dd986ccSRichard W.M. Jones 
519dd986ccSRichard W.M. Jones     /* -watchdog ? lists available devices and exits cleanly. */
52c8057f95SPeter Maydell     if (is_help_option(p)) {
5372cf2d4fSBlue Swirl         QLIST_FOREACH(model, &watchdog_list, entry) {
549dd986ccSRichard W.M. Jones             fprintf(stderr, "\t%s\t%s\n",
559dd986ccSRichard W.M. Jones                      model->wdt_name, model->wdt_description);
569dd986ccSRichard W.M. Jones         }
579dd986ccSRichard W.M. Jones         return 2;
589dd986ccSRichard W.M. Jones     }
599dd986ccSRichard W.M. Jones 
6072cf2d4fSBlue Swirl     QLIST_FOREACH(model, &watchdog_list, entry) {
619dd986ccSRichard W.M. Jones         if (strcasecmp(model->wdt_name, p) == 0) {
6209aaa160SMarkus Armbruster             /* add the device */
6387ea75d5SPeter Crosthwaite             opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
6487ea75d5SPeter Crosthwaite                                     &error_abort);
65f43e47dbSMarkus Armbruster             qemu_opt_set(opts, "driver", p, &error_abort);
669dd986ccSRichard W.M. Jones             return 0;
679dd986ccSRichard W.M. Jones         }
689dd986ccSRichard W.M. Jones     }
699dd986ccSRichard W.M. Jones 
709dd986ccSRichard W.M. Jones     fprintf(stderr, "Unknown -watchdog device. Supported devices are:\n");
7172cf2d4fSBlue Swirl     QLIST_FOREACH(model, &watchdog_list, entry) {
729dd986ccSRichard W.M. Jones         fprintf(stderr, "\t%s\t%s\n",
739dd986ccSRichard W.M. Jones                  model->wdt_name, model->wdt_description);
749dd986ccSRichard W.M. Jones     }
759dd986ccSRichard W.M. Jones     return 1;
769dd986ccSRichard W.M. Jones }
779dd986ccSRichard W.M. Jones 
789dd986ccSRichard W.M. Jones int select_watchdog_action(const char *p)
799dd986ccSRichard W.M. Jones {
809dd986ccSRichard W.M. Jones     if (strcasecmp(p, "reset") == 0)
819dd986ccSRichard W.M. Jones         watchdog_action = WDT_RESET;
829dd986ccSRichard W.M. Jones     else if (strcasecmp(p, "shutdown") == 0)
839dd986ccSRichard W.M. Jones         watchdog_action = WDT_SHUTDOWN;
849dd986ccSRichard W.M. Jones     else if (strcasecmp(p, "poweroff") == 0)
859dd986ccSRichard W.M. Jones         watchdog_action = WDT_POWEROFF;
869dd986ccSRichard W.M. Jones     else if (strcasecmp(p, "pause") == 0)
879dd986ccSRichard W.M. Jones         watchdog_action = WDT_PAUSE;
889dd986ccSRichard W.M. Jones     else if (strcasecmp(p, "debug") == 0)
899dd986ccSRichard W.M. Jones         watchdog_action = WDT_DEBUG;
909dd986ccSRichard W.M. Jones     else if (strcasecmp(p, "none") == 0)
919dd986ccSRichard W.M. Jones         watchdog_action = WDT_NONE;
92795dc6e4SMao Chuan Li     else if (strcasecmp(p, "inject-nmi") == 0)
93795dc6e4SMao Chuan Li         watchdog_action = WDT_NMI;
949dd986ccSRichard W.M. Jones     else
959dd986ccSRichard W.M. Jones         return -1;
969dd986ccSRichard W.M. Jones 
979dd986ccSRichard W.M. Jones     return 0;
989dd986ccSRichard W.M. Jones }
999dd986ccSRichard W.M. Jones 
1000d035b6cSBo Tu int get_watchdog_action(void)
1010d035b6cSBo Tu {
1020d035b6cSBo Tu     return watchdog_action;
1030d035b6cSBo Tu }
1040d035b6cSBo Tu 
1059dd986ccSRichard W.M. Jones /* This actually performs the "action" once a watchdog has expired,
1069dd986ccSRichard W.M. Jones  * ie. reboot, shutdown, exit, etc.
1079dd986ccSRichard W.M. Jones  */
1089dd986ccSRichard W.M. Jones void watchdog_perform_action(void)
1099dd986ccSRichard W.M. Jones {
1109dd986ccSRichard W.M. Jones     switch (watchdog_action) {
1119dd986ccSRichard W.M. Jones     case WDT_RESET:             /* same as 'system_reset' in monitor */
11299eaf09cSWenchao Xia         qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_RESET, &error_abort);
1139dd986ccSRichard W.M. Jones         qemu_system_reset_request();
1149dd986ccSRichard W.M. Jones         break;
1159dd986ccSRichard W.M. Jones 
1169dd986ccSRichard W.M. Jones     case WDT_SHUTDOWN:          /* same as 'system_powerdown' in monitor */
11799eaf09cSWenchao Xia         qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_SHUTDOWN, &error_abort);
1189dd986ccSRichard W.M. Jones         qemu_system_powerdown_request();
1199dd986ccSRichard W.M. Jones         break;
1209dd986ccSRichard W.M. Jones 
1219dd986ccSRichard W.M. Jones     case WDT_POWEROFF:          /* same as 'quit' command in monitor */
12299eaf09cSWenchao Xia         qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_POWEROFF, &error_abort);
1239dd986ccSRichard W.M. Jones         exit(0);
1249dd986ccSRichard W.M. Jones 
1259dd986ccSRichard W.M. Jones     case WDT_PAUSE:             /* same as 'stop' command in monitor */
12630e5210aSPaolo Bonzini         /* In a timer callback, when vm_stop calls qemu_clock_enable
12730e5210aSPaolo Bonzini          * you would get a deadlock.  Bypass the problem.
12830e5210aSPaolo Bonzini          */
12930e5210aSPaolo Bonzini         qemu_system_vmstop_request_prepare();
13099eaf09cSWenchao Xia         qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_PAUSE, &error_abort);
13130e5210aSPaolo Bonzini         qemu_system_vmstop_request(RUN_STATE_WATCHDOG);
1329dd986ccSRichard W.M. Jones         break;
1339dd986ccSRichard W.M. Jones 
1349dd986ccSRichard W.M. Jones     case WDT_DEBUG:
13599eaf09cSWenchao Xia         qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_DEBUG, &error_abort);
1369dd986ccSRichard W.M. Jones         fprintf(stderr, "watchdog: timer fired\n");
1379dd986ccSRichard W.M. Jones         break;
1389dd986ccSRichard W.M. Jones 
1399dd986ccSRichard W.M. Jones     case WDT_NONE:
14099eaf09cSWenchao Xia         qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_NONE, &error_abort);
1419dd986ccSRichard W.M. Jones         break;
142795dc6e4SMao Chuan Li 
143795dc6e4SMao Chuan Li     case WDT_NMI:
144795dc6e4SMao Chuan Li         qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_INJECT_NMI,
145795dc6e4SMao Chuan Li                                  &error_abort);
146795dc6e4SMao Chuan Li         inject_nmi();
147795dc6e4SMao Chuan Li         break;
1489dd986ccSRichard W.M. Jones     }
1499dd986ccSRichard W.M. Jones }
150