Commit 9dd986ccf68f142aaafe543d80cf877716d91d4e

Authored by Richard W.M. Jones
Committed by Anthony Liguori
1 parent ffad4116

Hardware watchdog

Here is an updated hardware watchdog patch, which should fix
everything that was raised about the previous version ...

Signed-off-by: Richard W.M. Jones <rjones@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Makefile.target
... ... @@ -582,6 +582,10 @@ OBJS += pcnet.o
582 582 OBJS += rtl8139.o
583 583 OBJS += e1000.o
584 584  
  585 +# Generic watchdog support and some watchdog devices
  586 +OBJS += watchdog.o
  587 +OBJS += wdt_ib700.o wdt_i6300esb.o
  588 +
585 589 ifeq ($(TARGET_BASE_ARCH), i386)
586 590 # Hardware support
587 591 OBJS+= ide.o pckbd.o vga.o $(SOUND_HW) dma.o
... ...
... ... @@ -37,6 +37,7 @@
37 37 #include "virtio-balloon.h"
38 38 #include "virtio-console.h"
39 39 #include "hpet_emul.h"
  40 +#include "watchdog.h"
40 41 #include "smbios.h"
41 42  
42 43 /* output Bochs bios info messages */
... ... @@ -1023,6 +1024,8 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
1023 1024 }
1024 1025 }
1025 1026  
  1027 + watchdog_pc_init(pci_bus);
  1028 +
1026 1029 for(i = 0; i < nb_nics; i++) {
1027 1030 NICInfo *nd = &nd_table[i];
1028 1031  
... ...
hw/watchdog.c 0 → 100644
  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, write to the Free Software
  18 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
  19 + * USA.
  20 + *
  21 + * By Richard W.M. Jones (rjones@redhat.com).
  22 + */
  23 +
  24 +#include "qemu-common.h"
  25 +#include "sys-queue.h"
  26 +#include "sysemu.h"
  27 +#include "hw/watchdog.h"
  28 +
  29 +static LIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list;
  30 +
  31 +void watchdog_add_model(WatchdogTimerModel *model)
  32 +{
  33 + LIST_INSERT_HEAD(&watchdog_list, model, entry);
  34 +}
  35 +
  36 +/* Returns:
  37 + * 0 = continue
  38 + * 1 = exit program with error
  39 + * 2 = exit program without error
  40 + */
  41 +int select_watchdog(const char *p)
  42 +{
  43 + WatchdogTimerModel *model;
  44 +
  45 + if (watchdog) {
  46 + fprintf(stderr,
  47 + "qemu: only one watchdog option may be given\n");
  48 + return 1;
  49 + }
  50 +
  51 + /* -watchdog ? lists available devices and exits cleanly. */
  52 + if (strcmp(p, "?") == 0) {
  53 + LIST_FOREACH(model, &watchdog_list, entry) {
  54 + fprintf(stderr, "\t%s\t%s\n",
  55 + model->wdt_name, model->wdt_description);
  56 + }
  57 + return 2;
  58 + }
  59 +
  60 + LIST_FOREACH(model, &watchdog_list, entry) {
  61 + if (strcasecmp(model->wdt_name, p) == 0) {
  62 + watchdog = model;
  63 + return 0;
  64 + }
  65 + }
  66 +
  67 + fprintf(stderr, "Unknown -watchdog device. Supported devices are:\n");
  68 + LIST_FOREACH(model, &watchdog_list, entry) {
  69 + fprintf(stderr, "\t%s\t%s\n",
  70 + model->wdt_name, model->wdt_description);
  71 + }
  72 + return 1;
  73 +}
  74 +
  75 +int select_watchdog_action(const char *p)
  76 +{
  77 + if (strcasecmp(p, "reset") == 0)
  78 + watchdog_action = WDT_RESET;
  79 + else if (strcasecmp(p, "shutdown") == 0)
  80 + watchdog_action = WDT_SHUTDOWN;
  81 + else if (strcasecmp(p, "poweroff") == 0)
  82 + watchdog_action = WDT_POWEROFF;
  83 + else if (strcasecmp(p, "pause") == 0)
  84 + watchdog_action = WDT_PAUSE;
  85 + else if (strcasecmp(p, "debug") == 0)
  86 + watchdog_action = WDT_DEBUG;
  87 + else if (strcasecmp(p, "none") == 0)
  88 + watchdog_action = WDT_NONE;
  89 + else
  90 + return -1;
  91 +
  92 + return 0;
  93 +}
  94 +
  95 +/* This actually performs the "action" once a watchdog has expired,
  96 + * ie. reboot, shutdown, exit, etc.
  97 + */
  98 +void watchdog_perform_action(void)
  99 +{
  100 + switch(watchdog_action) {
  101 + case WDT_RESET: /* same as 'system_reset' in monitor */
  102 + qemu_system_reset_request();
  103 + break;
  104 +
  105 + case WDT_SHUTDOWN: /* same as 'system_powerdown' in monitor */
  106 + qemu_system_powerdown_request();
  107 + break;
  108 +
  109 + case WDT_POWEROFF: /* same as 'quit' command in monitor */
  110 + exit(0);
  111 + break;
  112 +
  113 + case WDT_PAUSE: /* same as 'stop' command in monitor */
  114 + vm_stop(0);
  115 + break;
  116 +
  117 + case WDT_DEBUG:
  118 + fprintf(stderr, "watchdog: timer fired\n");
  119 + break;
  120 +
  121 + case WDT_NONE:
  122 + break;
  123 + }
  124 +}
  125 +
  126 +void watchdog_pc_init(PCIBus *pci_bus)
  127 +{
  128 + if (watchdog)
  129 + watchdog->wdt_pc_init(pci_bus);
  130 +}
  131 +
  132 +void register_watchdogs(void)
  133 +{
  134 + wdt_ib700_init();
  135 + wdt_i6300esb_init();
  136 +}
... ...
hw/watchdog.h 0 → 100644
  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, write to the Free Software
  18 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
  19 + * USA.
  20 + *
  21 + * By Richard W.M. Jones (rjones@redhat.com).
  22 + */
  23 +
  24 +#ifndef QEMU_WATCHDOG_H
  25 +#define QEMU_WATCHDOG_H
  26 +
  27 +extern void wdt_i6300esb_init(void);
  28 +extern void wdt_ib700_init(void);
  29 +
  30 +/* Possible values for action parameter. */
  31 +#define WDT_RESET 1 /* Hard reset. */
  32 +#define WDT_SHUTDOWN 2 /* Shutdown. */
  33 +#define WDT_POWEROFF 3 /* Quit. */
  34 +#define WDT_PAUSE 4 /* Pause. */
  35 +#define WDT_DEBUG 5 /* Prints a message and continues running. */
  36 +#define WDT_NONE 6 /* Do nothing. */
  37 +
  38 +struct WatchdogTimerModel {
  39 + LIST_ENTRY(WatchdogTimerModel) entry;
  40 +
  41 + /* Short name of the device - used to select it on the command line. */
  42 + const char *wdt_name;
  43 + /* Longer description (eg. manufacturer and full model number). */
  44 + const char *wdt_description;
  45 +
  46 + /* This callback should create/register the device. It is called
  47 + * indirectly from hw/pc.c when the virtual PC is being set up.
  48 + */
  49 + void (*wdt_pc_init)(PCIBus *pci_bus);
  50 +};
  51 +typedef struct WatchdogTimerModel WatchdogTimerModel;
  52 +
  53 +/* in vl.c */
  54 +extern WatchdogTimerModel *watchdog;
  55 +extern int watchdog_action;
  56 +
  57 +/* in hw/watchdog.c */
  58 +extern int select_watchdog(const char *p);
  59 +extern int select_watchdog_action(const char *action);
  60 +extern void watchdog_add_model(WatchdogTimerModel *model);
  61 +extern void watchdog_perform_action(void);
  62 +extern void watchdog_pc_init(PCIBus *pci_bus);
  63 +extern void register_watchdogs(void);
  64 +
  65 +#endif /* QEMU_WATCHDOG_H */
... ...
hw/wdt_i6300esb.c 0 → 100644
  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, write to the Free Software
  18 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
  19 + * USA.
  20 + *
  21 + * By Richard W.M. Jones (rjones@redhat.com).
  22 + */
  23 +
  24 +#include <inttypes.h>
  25 +
  26 +#include "qemu-common.h"
  27 +#include "qemu-timer.h"
  28 +#include "watchdog.h"
  29 +#include "hw.h"
  30 +#include "isa.h"
  31 +#include "pc.h"
  32 +#include "pci.h"
  33 +
  34 +/*#define I6300ESB_DEBUG 1*/
  35 +
  36 +#ifdef I6300ESB_DEBUG
  37 +#define i6300esb_debug(fs,...) \
  38 + fprintf(stderr,"i6300esb: %s: "fs,__func__,##__VA_ARGS__)
  39 +#else
  40 +#define i6300esb_debug(fs,...)
  41 +#endif
  42 +
  43 +#ifndef PCI_DEVICE_ID_INTEL_ESB_9
  44 +#define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab
  45 +#endif
  46 +
  47 +/* PCI configuration registers */
  48 +#define ESB_CONFIG_REG 0x60 /* Config register */
  49 +#define ESB_LOCK_REG 0x68 /* WDT lock register */
  50 +
  51 +/* Memory mapped registers (offset from base address) */
  52 +#define ESB_TIMER1_REG 0x00 /* Timer1 value after each reset */
  53 +#define ESB_TIMER2_REG 0x04 /* Timer2 value after each reset */
  54 +#define ESB_GINTSR_REG 0x08 /* General Interrupt Status Register */
  55 +#define ESB_RELOAD_REG 0x0c /* Reload register */
  56 +
  57 +/* Lock register bits */
  58 +#define ESB_WDT_FUNC (0x01 << 2) /* Watchdog functionality */
  59 +#define ESB_WDT_ENABLE (0x01 << 1) /* Enable WDT */
  60 +#define ESB_WDT_LOCK (0x01 << 0) /* Lock (nowayout) */
  61 +
  62 +/* Config register bits */
  63 +#define ESB_WDT_REBOOT (0x01 << 5) /* Enable reboot on timeout */
  64 +#define ESB_WDT_FREQ (0x01 << 2) /* Decrement frequency */
  65 +#define ESB_WDT_INTTYPE (0x11 << 0) /* Interrupt type on timer1 timeout */
  66 +
  67 +/* Reload register bits */
  68 +#define ESB_WDT_RELOAD (0x01 << 8) /* prevent timeout */
  69 +
  70 +/* Magic constants */
  71 +#define ESB_UNLOCK1 0x80 /* Step 1 to unlock reset registers */
  72 +#define ESB_UNLOCK2 0x86 /* Step 2 to unlock reset registers */
  73 +
  74 +/* Device state. */
  75 +struct I6300State {
  76 + PCIDevice dev; /* PCI device state, must be first field. */
  77 +
  78 + int reboot_enabled; /* "Reboot" on timer expiry. The real action
  79 + * performed depends on the -watchdog-action
  80 + * param passed on QEMU command line.
  81 + */
  82 + int clock_scale; /* Clock scale. */
  83 +#define CLOCK_SCALE_1KHZ 0
  84 +#define CLOCK_SCALE_1MHZ 1
  85 +
  86 + int int_type; /* Interrupt type generated. */
  87 +#define INT_TYPE_IRQ 0 /* APIC 1, INT 10 */
  88 +#define INT_TYPE_SMI 2
  89 +#define INT_TYPE_DISABLED 3
  90 +
  91 + int free_run; /* If true, reload timer on expiry. */
  92 + int locked; /* If true, enabled field cannot be changed. */
  93 + int enabled; /* If true, watchdog is enabled. */
  94 +
  95 + QEMUTimer *timer; /* The actual watchdog timer. */
  96 +
  97 + uint32_t timer1_preload; /* Values preloaded into timer1, timer2. */
  98 + uint32_t timer2_preload;
  99 + int stage; /* Stage (1 or 2). */
  100 +
  101 + int unlock_state; /* Guest writes 0x80, 0x86 to unlock the
  102 + * registers, and we transition through
  103 + * states 0 -> 1 -> 2 when this happens.
  104 + */
  105 +
  106 + int previous_reboot_flag; /* If the watchdog caused the previous
  107 + * reboot, this flag will be set.
  108 + */
  109 +};
  110 +
  111 +typedef struct I6300State I6300State;
  112 +
  113 +/* This function is called when the watchdog has either been enabled
  114 + * (hence it starts counting down) or has been keep-alived.
  115 + */
  116 +static void i6300esb_restart_timer(I6300State *d, int stage)
  117 +{
  118 + int64_t timeout;
  119 +
  120 + if (!d->enabled)
  121 + return;
  122 +
  123 + d->stage = stage;
  124 +
  125 + if (d->stage <= 1)
  126 + timeout = d->timer1_preload;
  127 + else
  128 + timeout = d->timer2_preload;
  129 +
  130 + if (d->clock_scale == CLOCK_SCALE_1KHZ)
  131 + timeout <<= 15;
  132 + else
  133 + timeout <<= 5;
  134 +
  135 + /* Get the timeout in units of ticks_per_sec. */
  136 + timeout = ticks_per_sec * timeout / 33000000;
  137 +
  138 + i6300esb_debug("stage %d, timeout %" PRIi64 "\n", d->stage, timeout);
  139 +
  140 + qemu_mod_timer(d->timer, qemu_get_clock(vm_clock) + timeout);
  141 +}
  142 +
  143 +/* This is called when the guest disables the watchdog. */
  144 +static void i6300esb_disable_timer(I6300State *d)
  145 +{
  146 + i6300esb_debug("timer disabled\n");
  147 +
  148 + qemu_del_timer(d->timer);
  149 +}
  150 +
  151 +static void i6300esb_reset(I6300State *d)
  152 +{
  153 + /* XXX We should probably reset other parts of the state here,
  154 + * but we should also reset our state on general machine reset
  155 + * too. For now just disable the timer so it doesn't fire
  156 + * again after the reboot.
  157 + */
  158 + i6300esb_disable_timer(d);
  159 +}
  160 +
  161 +/* This function is called when the watchdog expires. Note that
  162 + * the hardware has two timers, and so expiry happens in two stages.
  163 + * If d->stage == 1 then we perform the first stage action (usually,
  164 + * sending an interrupt) and then restart the timer again for the
  165 + * second stage. If the second stage expires then the watchdog
  166 + * really has run out.
  167 + */
  168 +static void i6300esb_timer_expired(void *vp)
  169 +{
  170 + I6300State *d = (I6300State *) vp;
  171 +
  172 + i6300esb_debug("stage %d\n", d->stage);
  173 +
  174 + if (d->stage == 1) {
  175 + /* What to do at the end of stage 1? */
  176 + switch (d->int_type) {
  177 + case INT_TYPE_IRQ:
  178 + fprintf(stderr, "i6300esb_timer_expired: I would send APIC 1 INT 10 here if I knew how (XXX)\n");
  179 + break;
  180 + case INT_TYPE_SMI:
  181 + fprintf(stderr, "i6300esb_timer_expired: I would send SMI here if I knew how (XXX)\n");
  182 + break;
  183 + }
  184 +
  185 + /* Start the second stage. */
  186 + i6300esb_restart_timer(d, 2);
  187 + } else {
  188 + /* Second stage expired, reboot for real. */
  189 + if (d->reboot_enabled) {
  190 + d->previous_reboot_flag = 1;
  191 + watchdog_perform_action(); /* This reboots, exits, etc */
  192 + i6300esb_reset(d);
  193 + }
  194 +
  195 + /* In "free running mode" we start stage 1 again. */
  196 + if (d->free_run)
  197 + i6300esb_restart_timer(d, 1);
  198 + }
  199 +}
  200 +
  201 +static void i6300esb_config_write(PCIDevice *dev, uint32_t addr,
  202 + uint32_t data, int len)
  203 +{
  204 + I6300State *d = (I6300State *) dev;
  205 + int old;
  206 +
  207 + i6300esb_debug("addr = %x, data = %x, len = %d\n", addr, data, len);
  208 +
  209 + if (addr == ESB_CONFIG_REG && len == 2) {
  210 + d->reboot_enabled = (data & ESB_WDT_REBOOT) == 0;
  211 + d->clock_scale =
  212 + (data & ESB_WDT_FREQ) != 0 ? CLOCK_SCALE_1MHZ : CLOCK_SCALE_1KHZ;
  213 + d->int_type = (data & ESB_WDT_INTTYPE);
  214 + } else if (addr == ESB_LOCK_REG && len == 1) {
  215 + if (!d->locked) {
  216 + d->locked = (data & ESB_WDT_LOCK) != 0;
  217 + d->free_run = (data & ESB_WDT_FUNC) != 0;
  218 + old = d->enabled;
  219 + d->enabled = (data & ESB_WDT_ENABLE) != 0;
  220 + if (!old && d->enabled) /* Enabled transitioned from 0 -> 1 */
  221 + i6300esb_restart_timer(d, 1);
  222 + else if (!d->enabled)
  223 + i6300esb_disable_timer(d);
  224 + }
  225 + } else {
  226 + pci_default_write_config(dev, addr, data, len);
  227 + }
  228 +}
  229 +
  230 +static uint32_t i6300esb_config_read(PCIDevice *dev, uint32_t addr, int len)
  231 +{
  232 + I6300State *d = (I6300State *) dev;
  233 + uint32_t data;
  234 +
  235 + i6300esb_debug ("addr = %x, len = %d\n", addr, len);
  236 +
  237 + if (addr == ESB_CONFIG_REG && len == 2) {
  238 + data =
  239 + (d->reboot_enabled ? 0 : ESB_WDT_REBOOT) |
  240 + (d->clock_scale == CLOCK_SCALE_1MHZ ? ESB_WDT_FREQ : 0) |
  241 + d->int_type;
  242 + return data;
  243 + } else if (addr == ESB_LOCK_REG && len == 1) {
  244 + data =
  245 + (d->free_run ? ESB_WDT_FUNC : 0) |
  246 + (d->locked ? ESB_WDT_LOCK : 0) |
  247 + (d->enabled ? ESB_WDT_ENABLE : 0);
  248 + return data;
  249 + } else {
  250 + return pci_default_read_config(dev, addr, len);
  251 + }
  252 +}
  253 +
  254 +static uint32_t i6300esb_mem_readb(void *vp, target_phys_addr_t addr)
  255 +{
  256 + i6300esb_debug ("addr = %x\n", (int) addr);
  257 +
  258 + return 0;
  259 +}
  260 +
  261 +static uint32_t i6300esb_mem_readw(void *vp, target_phys_addr_t addr)
  262 +{
  263 + uint32_t data = 0;
  264 + I6300State *d = (I6300State *) vp;
  265 +
  266 + i6300esb_debug("addr = %x\n", (int) addr);
  267 +
  268 + if (addr == 0xc) {
  269 + /* The previous reboot flag is really bit 9, but there is
  270 + * a bug in the Linux driver where it thinks it's bit 12.
  271 + * Set both.
  272 + */
  273 + data = d->previous_reboot_flag ? 0x1200 : 0;
  274 + }
  275 +
  276 + return data;
  277 +}
  278 +
  279 +static uint32_t i6300esb_mem_readl(void *vp, target_phys_addr_t addr)
  280 +{
  281 + i6300esb_debug("addr = %x\n", (int) addr);
  282 +
  283 + return 0;
  284 +}
  285 +
  286 +static void i6300esb_mem_writeb(void *vp, target_phys_addr_t addr, uint32_t val)
  287 +{
  288 + I6300State *d = (I6300State *) vp;
  289 +
  290 + i6300esb_debug("addr = %x, val = %x\n", (int) addr, val);
  291 +
  292 + if (addr == 0xc && val == 0x80)
  293 + d->unlock_state = 1;
  294 + else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
  295 + d->unlock_state = 2;
  296 +}
  297 +
  298 +static void i6300esb_mem_writew(void *vp, target_phys_addr_t addr, uint32_t val)
  299 +{
  300 + I6300State *d = (I6300State *) vp;
  301 +
  302 + i6300esb_debug("addr = %x, val = %x\n", (int) addr, val);
  303 +
  304 + if (addr == 0xc && val == 0x80)
  305 + d->unlock_state = 1;
  306 + else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
  307 + d->unlock_state = 2;
  308 + else {
  309 + if (d->unlock_state == 2) {
  310 + if (addr == 0xc) {
  311 + if ((val & 0x100) != 0)
  312 + /* This is the "ping" from the userspace watchdog in
  313 + * the guest ...
  314 + */
  315 + i6300esb_restart_timer(d, 1);
  316 +
  317 + /* Setting bit 9 resets the previous reboot flag.
  318 + * There's a bug in the Linux driver where it sets
  319 + * bit 12 instead.
  320 + */
  321 + if ((val & 0x200) != 0 || (val & 0x1000) != 0) {
  322 + d->previous_reboot_flag = 0;
  323 + }
  324 + }
  325 +
  326 + d->unlock_state = 0;
  327 + }
  328 + }
  329 +}
  330 +
  331 +static void i6300esb_mem_writel(void *vp, target_phys_addr_t addr, uint32_t val)
  332 +{
  333 + I6300State *d = (I6300State *) vp;
  334 +
  335 + i6300esb_debug ("addr = %x, val = %x\n", (int) addr, val);
  336 +
  337 + if (addr == 0xc && val == 0x80)
  338 + d->unlock_state = 1;
  339 + else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
  340 + d->unlock_state = 2;
  341 + else {
  342 + if (d->unlock_state == 2) {
  343 + if (addr == 0)
  344 + d->timer1_preload = val & 0xfffff;
  345 + else if (addr == 4)
  346 + d->timer2_preload = val & 0xfffff;
  347 +
  348 + d->unlock_state = 0;
  349 + }
  350 + }
  351 +}
  352 +
  353 +static void i6300esb_map(PCIDevice *dev, int region_num,
  354 + uint32_t addr, uint32_t size, int type)
  355 +{
  356 + static CPUReadMemoryFunc *mem_read[3] = {
  357 + i6300esb_mem_readb,
  358 + i6300esb_mem_readw,
  359 + i6300esb_mem_readl,
  360 + };
  361 + static CPUWriteMemoryFunc *mem_write[3] = {
  362 + i6300esb_mem_writeb,
  363 + i6300esb_mem_writew,
  364 + i6300esb_mem_writel,
  365 + };
  366 + I6300State *d = (I6300State *) dev;
  367 + int io_mem;
  368 +
  369 + i6300esb_debug("addr = %x, size = %x, type = %d\n", addr, size, type);
  370 +
  371 + io_mem = cpu_register_io_memory (0, mem_read, mem_write, d);
  372 + cpu_register_physical_memory (addr, 0x10, io_mem);
  373 + /* qemu_register_coalesced_mmio (addr, 0x10); ? */
  374 +}
  375 +
  376 +static void i6300esb_save(QEMUFile *f, void *vp)
  377 +{
  378 + I6300State *d = (I6300State *) vp;
  379 +
  380 + pci_device_save(&d->dev, f);
  381 + qemu_put_be32(f, d->reboot_enabled);
  382 + qemu_put_be32(f, d->clock_scale);
  383 + qemu_put_be32(f, d->int_type);
  384 + qemu_put_be32(f, d->free_run);
  385 + qemu_put_be32(f, d->locked);
  386 + qemu_put_be32(f, d->enabled);
  387 + qemu_put_timer(f, d->timer);
  388 + qemu_put_be32(f, d->timer1_preload);
  389 + qemu_put_be32(f, d->timer2_preload);
  390 + qemu_put_be32(f, d->stage);
  391 + qemu_put_be32(f, d->unlock_state);
  392 + qemu_put_be32(f, d->previous_reboot_flag);
  393 +}
  394 +
  395 +static int i6300esb_load(QEMUFile *f, void *vp, int version)
  396 +{
  397 + I6300State *d = (I6300State *) vp;
  398 +
  399 + if (version != sizeof (I6300State))
  400 + return -EINVAL;
  401 +
  402 + pci_device_load(&d->dev, f);
  403 + d->reboot_enabled = qemu_get_be32(f);
  404 + d->clock_scale = qemu_get_be32(f);
  405 + d->int_type = qemu_get_be32(f);
  406 + d->free_run = qemu_get_be32(f);
  407 + d->locked = qemu_get_be32(f);
  408 + d->enabled = qemu_get_be32(f);
  409 + qemu_get_timer(f, d->timer);
  410 + d->timer1_preload = qemu_get_be32(f);
  411 + d->timer2_preload = qemu_get_be32(f);
  412 + d->stage = qemu_get_be32(f);
  413 + d->unlock_state = qemu_get_be32(f);
  414 + d->previous_reboot_flag = qemu_get_be32(f);
  415 +
  416 + return 0;
  417 +}
  418 +
  419 +/* Create and initialize a virtual Intel 6300ESB during PC creation. */
  420 +static void i6300esb_pc_init(PCIBus *pci_bus)
  421 +{
  422 + I6300State *d;
  423 + uint8_t *pci_conf;
  424 +
  425 + if (!pci_bus) {
  426 + fprintf(stderr, "wdt_i6300esb: no PCI bus in this machine\n");
  427 + return;
  428 + }
  429 +
  430 + d = (I6300State *)
  431 + pci_register_device (pci_bus, "i6300esb_wdt", sizeof (I6300State),
  432 + -1,
  433 + i6300esb_config_read, i6300esb_config_write);
  434 +
  435 + d->reboot_enabled = 1;
  436 + d->clock_scale = CLOCK_SCALE_1KHZ;
  437 + d->int_type = INT_TYPE_IRQ;
  438 + d->free_run = 0;
  439 + d->locked = 0;
  440 + d->enabled = 0;
  441 + d->timer = qemu_new_timer(vm_clock, i6300esb_timer_expired, d);
  442 + d->timer1_preload = 0xfffff;
  443 + d->timer2_preload = 0xfffff;
  444 + d->stage = 1;
  445 + d->unlock_state = 0;
  446 + d->previous_reboot_flag = 0;
  447 +
  448 + pci_conf = d->dev.config;
  449 + pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
  450 + pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_ESB_9);
  451 + pci_config_set_class(pci_conf, PCI_CLASS_SYSTEM_OTHER);
  452 + pci_conf[0x0e] = 0x00;
  453 +
  454 + pci_register_io_region(&d->dev, 0, 0x10,
  455 + PCI_ADDRESS_SPACE_MEM, i6300esb_map);
  456 +
  457 + register_savevm("i6300esb_wdt", -1, sizeof(I6300State),
  458 + i6300esb_save, i6300esb_load, d);
  459 +}
  460 +
  461 +static WatchdogTimerModel model = {
  462 + .wdt_name = "i6300esb",
  463 + .wdt_description = "Intel 6300ESB",
  464 + .wdt_pc_init = i6300esb_pc_init,
  465 +};
  466 +
  467 +void wdt_i6300esb_init(void)
  468 +{
  469 + watchdog_add_model(&model);
  470 +}
... ...
hw/wdt_ib700.c 0 → 100644
  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, write to the Free Software
  18 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
  19 + * USA.
  20 + *
  21 + * By Richard W.M. Jones (rjones@redhat.com).
  22 + */
  23 +
  24 +#include "qemu-common.h"
  25 +#include "qemu-timer.h"
  26 +#include "watchdog.h"
  27 +#include "hw.h"
  28 +#include "isa.h"
  29 +#include "pc.h"
  30 +
  31 +/*#define IB700_DEBUG 1*/
  32 +
  33 +#ifdef IB700_DEBUG
  34 +#define ib700_debug(fs,...) \
  35 + fprintf(stderr,"ib700: %s: "fs,__func__,##__VA_ARGS__)
  36 +#else
  37 +#define ib700_debug(fs,...)
  38 +#endif
  39 +
  40 +/* This is the timer. We use a global here because the watchdog
  41 + * code ensures there is only one watchdog (it is located at a fixed,
  42 + * unchangable IO port, so there could only ever be one anyway).
  43 + */
  44 +static QEMUTimer *timer = NULL;
  45 +
  46 +/* A write to this register enables the timer. */
  47 +static void ib700_write_enable_reg(void *vp, uint32_t addr, uint32_t data)
  48 +{
  49 + static int time_map[] = {
  50 + 30, 28, 26, 24, 22, 20, 18, 16,
  51 + 14, 12, 10, 8, 6, 4, 2, 0
  52 + };
  53 + int64 timeout;
  54 +
  55 + ib700_debug("addr = %x, data = %x\n", addr, data);
  56 +
  57 + timeout = (int64_t) time_map[data & 0xF] * ticks_per_sec;
  58 + qemu_mod_timer(timer, qemu_get_clock (vm_clock) + timeout);
  59 +}
  60 +
  61 +/* A write (of any value) to this register disables the timer. */
  62 +static void ib700_write_disable_reg(void *vp, uint32_t addr, uint32_t data)
  63 +{
  64 + ib700_debug("addr = %x, data = %x\n", addr, data);
  65 +
  66 + qemu_del_timer(timer);
  67 +}
  68 +
  69 +/* This is called when the watchdog expires. */
  70 +static void ib700_timer_expired(void *vp)
  71 +{
  72 + ib700_debug("watchdog expired\n");
  73 +
  74 + watchdog_perform_action();
  75 + qemu_del_timer(timer);
  76 +}
  77 +
  78 +static void ib700_save(QEMUFile *f, void *vp)
  79 +{
  80 + qemu_put_timer(f, timer);
  81 +}
  82 +
  83 +static int ib700_load(QEMUFile *f, void *vp, int version)
  84 +{
  85 + if (version != 0)
  86 + return -EINVAL;
  87 +
  88 + qemu_get_timer(f, timer);
  89 +
  90 + return 0;
  91 +}
  92 +
  93 +/* Create and initialize a virtual IB700 during PC creation. */
  94 +static void ib700_pc_init(PCIBus *unused)
  95 +{
  96 + register_savevm("ib700_wdt", -1, 0, ib700_save, ib700_load, NULL);
  97 +
  98 + register_ioport_write(0x441, 2, 1, ib700_write_disable_reg, NULL);
  99 + register_ioport_write(0x443, 2, 1, ib700_write_enable_reg, NULL);
  100 +}
  101 +
  102 +static WatchdogTimerModel model = {
  103 + .wdt_name = "ib700",
  104 + .wdt_description = "iBASE 700",
  105 + .wdt_pc_init = ib700_pc_init,
  106 +};
  107 +
  108 +void wdt_ib700_init(void)
  109 +{
  110 + watchdog_add_model(&model);
  111 + timer = qemu_new_timer(vm_clock, ib700_timer_expired, NULL);
  112 +}
... ...
monitor.c
... ... @@ -27,6 +27,7 @@
27 27 #include "hw/pcmcia.h"
28 28 #include "hw/pc.h"
29 29 #include "hw/pci.h"
  30 +#include "hw/watchdog.h"
30 31 #include "gdbstub.h"
31 32 #include "net.h"
32 33 #include "qemu-char.h"
... ... @@ -597,6 +598,13 @@ static void do_gdbserver(Monitor *mon, const char *device)
597 598 }
598 599 #endif
599 600  
  601 +static void do_watchdog_action(Monitor *mon, const char *action)
  602 +{
  603 + if (select_watchdog_action(action) == -1) {
  604 + monitor_printf(mon, "Unknown watchdog action '%s'\n", action);
  605 + }
  606 +}
  607 +
600 608 static void monitor_printc(Monitor *mon, int c)
601 609 {
602 610 monitor_printf(mon, "'");
... ... @@ -1762,6 +1770,8 @@ static const mon_cmd_t mon_cmds[] = {
1762 1770 "target", "request VM to change it's memory allocation (in MB)" },
1763 1771 { "set_link", "ss", do_set_link,
1764 1772 "name up|down", "change the link status of a network adapter" },
  1773 + { "watchdog_action", "s", do_watchdog_action,
  1774 + "[reset|shutdown|poweroff|pause|debug|none]", "change watchdog action" },
1765 1775 { "acl", "sss?i?", do_acl, "<command> <aclname> [<match> [<index>]]\n",
1766 1776 "acl show vnc.username\n"
1767 1777 "acl policy vnc.username deny\n"
... ...
qemu-options.hx
... ... @@ -1448,6 +1448,55 @@ order cores with complex cache hierarchies. The number of instructions
1448 1448 executed often has little or no correlation with actual performance.
1449 1449 ETEXI
1450 1450  
  1451 +DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \
  1452 + "-watchdog i6300esb|ib700\n" \
  1453 + " enable virtual hardware watchdog [default=none]\n")
  1454 +STEXI
  1455 +@item -watchdog @var{model}
  1456 +Create a virtual hardware watchdog device. Once enabled (by a guest
  1457 +action), the watchdog must be periodically polled by an agent inside
  1458 +the guest or else the guest will be restarted.
  1459 +
  1460 +The @var{model} is the model of hardware watchdog to emulate. Choices
  1461 +for model are: @code{ib700} (iBASE 700) which is a very simple ISA
  1462 +watchdog with a single timer, or @code{i6300esb} (Intel 6300ESB I/O
  1463 +controller hub) which is a much more featureful PCI-based dual-timer
  1464 +watchdog. Choose a model for which your guest has drivers.
  1465 +
  1466 +Use @code{-watchdog ?} to list available hardware models. Only one
  1467 +watchdog can be enabled for a guest.
  1468 +ETEXI
  1469 +
  1470 +DEF("watchdog-action", HAS_ARG, QEMU_OPTION_watchdog_action, \
  1471 + "-watchdog-action reset|shutdown|poweroff|pause|debug|none\n" \
  1472 + " action when watchdog fires [default=reset]\n")
  1473 +STEXI
  1474 +@item -watchdog-action @var{action}
  1475 +
  1476 +The @var{action} controls what QEMU will do when the watchdog timer
  1477 +expires.
  1478 +The default is
  1479 +@code{reset} (forcefully reset the guest).
  1480 +Other possible actions are:
  1481 +@code{shutdown} (attempt to gracefully shutdown the guest),
  1482 +@code{poweroff} (forcefully poweroff the guest),
  1483 +@code{pause} (pause the guest),
  1484 +@code{debug} (print a debug message and continue), or
  1485 +@code{none} (do nothing).
  1486 +
  1487 +Note that the @code{shutdown} action requires that the guest responds
  1488 +to ACPI signals, which it may not be able to do in the sort of
  1489 +situations where the watchdog would have expired, and thus
  1490 +@code{-watchdog-action shutdown} is not recommended for production use.
  1491 +
  1492 +Examples:
  1493 +
  1494 +@table @code
  1495 +@item -watchdog i6300esb -watchdog-action pause
  1496 +@item -watchdog ib700
  1497 +@end table
  1498 +ETEXI
  1499 +
1451 1500 DEF("echr", HAS_ARG, QEMU_OPTION_echr, \
1452 1501 "-echr chr set terminal escape character instead of ctrl-a\n")
1453 1502 STEXI
... ...
... ... @@ -138,6 +138,7 @@ int main(int argc, char **argv)
138 138 #include "hw/isa.h"
139 139 #include "hw/baum.h"
140 140 #include "hw/bt.h"
  141 +#include "hw/watchdog.h"
141 142 #include "hw/smbios.h"
142 143 #include "hw/xen.h"
143 144 #include "bt-host.h"
... ... @@ -252,6 +253,8 @@ int graphic_rotate = 0;
252 253 #ifndef _WIN32
253 254 int daemonize = 0;
254 255 #endif
  256 +WatchdogTimerModel *watchdog = NULL;
  257 +int watchdog_action = WDT_RESET;
255 258 const char *option_rom[MAX_OPTION_ROMS];
256 259 int nb_option_roms;
257 260 int semihosting_enabled = 0;
... ... @@ -4917,6 +4920,8 @@ int main(int argc, char **argv, char **envp)
4917 4920 tb_size = 0;
4918 4921 autostart= 1;
4919 4922  
  4923 + register_watchdogs();
  4924 +
4920 4925 optind = 1;
4921 4926 for(;;) {
4922 4927 if (optind >= argc)
... ... @@ -5308,6 +5313,17 @@ int main(int argc, char **argv, char **envp)
5308 5313 serial_devices[serial_device_index] = optarg;
5309 5314 serial_device_index++;
5310 5315 break;
  5316 + case QEMU_OPTION_watchdog:
  5317 + i = select_watchdog(optarg);
  5318 + if (i > 0)
  5319 + exit (i == 1 ? 1 : 0);
  5320 + break;
  5321 + case QEMU_OPTION_watchdog_action:
  5322 + if (select_watchdog_action(optarg) == -1) {
  5323 + fprintf(stderr, "Unknown -watchdog-action parameter\n");
  5324 + exit(1);
  5325 + }
  5326 + break;
5311 5327 case QEMU_OPTION_virtiocon:
5312 5328 if (virtio_console_index >= MAX_VIRTIO_CONSOLES) {
5313 5329 fprintf(stderr, "qemu: too many virtio consoles\n");
... ...