xref: /openbmc/qemu/hw/watchdog/watchdog.c (revision 0b8f74488e50f98b04e63157f85fde8a13f8d6aa)
1  /*
2   * Virtual hardware watchdog.
3   *
4   * Copyright (C) 2009 Red Hat Inc.
5   *
6   * This program is free software; you can redistribute it and/or
7   * modify it under the terms of the GNU General Public License
8   * as published by the Free Software Foundation; either version 2
9   * of the License, or (at your option) any later version.
10   *
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU General Public License for more details.
15   *
16   * You should have received a copy of the GNU General Public License
17   * along with this program; if not, see <http://www.gnu.org/licenses/>.
18   *
19   * By Richard W.M. Jones (rjones@redhat.com).
20   */
21  
22  #include "qemu/osdep.h"
23  #include "qemu/option.h"
24  #include "qemu/config-file.h"
25  #include "qemu/queue.h"
26  #include "qapi/error.h"
27  #include "qapi/qapi-commands-run-state.h"
28  #include "qapi/qapi-events-run-state.h"
29  #include "sysemu/sysemu.h"
30  #include "sysemu/watchdog.h"
31  #include "hw/nmi.h"
32  #include "qemu/help_option.h"
33  
34  static WatchdogAction watchdog_action = WATCHDOG_ACTION_RESET;
35  static QLIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list;
36  
37  void watchdog_add_model(WatchdogTimerModel *model)
38  {
39      QLIST_INSERT_HEAD(&watchdog_list, model, entry);
40  }
41  
42  /* Returns:
43   *   0 = continue
44   *   1 = exit program with error
45   *   2 = exit program without error
46   */
47  int select_watchdog(const char *p)
48  {
49      WatchdogTimerModel *model;
50      QemuOpts *opts;
51  
52      /* -watchdog ? lists available devices and exits cleanly. */
53      if (is_help_option(p)) {
54          QLIST_FOREACH(model, &watchdog_list, entry) {
55              fprintf(stderr, "\t%s\t%s\n",
56                       model->wdt_name, model->wdt_description);
57          }
58          return 2;
59      }
60  
61      QLIST_FOREACH(model, &watchdog_list, entry) {
62          if (strcasecmp(model->wdt_name, p) == 0) {
63              /* add the device */
64              opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
65                                      &error_abort);
66              qemu_opt_set(opts, "driver", p, &error_abort);
67              return 0;
68          }
69      }
70  
71      fprintf(stderr, "Unknown -watchdog device. Supported devices are:\n");
72      QLIST_FOREACH(model, &watchdog_list, entry) {
73          fprintf(stderr, "\t%s\t%s\n",
74                   model->wdt_name, model->wdt_description);
75      }
76      return 1;
77  }
78  
79  int select_watchdog_action(const char *p)
80  {
81      int action;
82      char *qapi_value;
83  
84      qapi_value = g_ascii_strdown(p, -1);
85      action = qapi_enum_parse(&WatchdogAction_lookup, qapi_value, -1, NULL);
86      g_free(qapi_value);
87      if (action < 0)
88          return -1;
89      qmp_watchdog_set_action(action, &error_abort);
90      return 0;
91  }
92  
93  WatchdogAction get_watchdog_action(void)
94  {
95      return watchdog_action;
96  }
97  
98  /* This actually performs the "action" once a watchdog has expired,
99   * ie. reboot, shutdown, exit, etc.
100   */
101  void watchdog_perform_action(void)
102  {
103      switch (watchdog_action) {
104      case WATCHDOG_ACTION_RESET:     /* same as 'system_reset' in monitor */
105          qapi_event_send_watchdog(WATCHDOG_ACTION_RESET);
106          qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
107          break;
108  
109      case WATCHDOG_ACTION_SHUTDOWN:  /* same as 'system_powerdown' in monitor */
110          qapi_event_send_watchdog(WATCHDOG_ACTION_SHUTDOWN);
111          qemu_system_powerdown_request();
112          break;
113  
114      case WATCHDOG_ACTION_POWEROFF:  /* same as 'quit' command in monitor */
115          qapi_event_send_watchdog(WATCHDOG_ACTION_POWEROFF);
116          exit(0);
117  
118      case WATCHDOG_ACTION_PAUSE:     /* same as 'stop' command in monitor */
119          /* In a timer callback, when vm_stop calls qemu_clock_enable
120           * you would get a deadlock.  Bypass the problem.
121           */
122          qemu_system_vmstop_request_prepare();
123          qapi_event_send_watchdog(WATCHDOG_ACTION_PAUSE);
124          qemu_system_vmstop_request(RUN_STATE_WATCHDOG);
125          break;
126  
127      case WATCHDOG_ACTION_DEBUG:
128          qapi_event_send_watchdog(WATCHDOG_ACTION_DEBUG);
129          fprintf(stderr, "watchdog: timer fired\n");
130          break;
131  
132      case WATCHDOG_ACTION_NONE:
133          qapi_event_send_watchdog(WATCHDOG_ACTION_NONE);
134          break;
135  
136      case WATCHDOG_ACTION_INJECT_NMI:
137          qapi_event_send_watchdog(WATCHDOG_ACTION_INJECT_NMI);
138          nmi_monitor_handle(0, NULL);
139          break;
140  
141      default:
142          assert(0);
143      }
144  }
145  
146  void qmp_watchdog_set_action(WatchdogAction action, Error **errp)
147  {
148      watchdog_action = action;
149  }
150