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,6 +582,10 @@ OBJS += pcnet.o
582 OBJS += rtl8139.o 582 OBJS += rtl8139.o
583 OBJS += e1000.o 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 ifeq ($(TARGET_BASE_ARCH), i386) 589 ifeq ($(TARGET_BASE_ARCH), i386)
586 # Hardware support 590 # Hardware support
587 OBJS+= ide.o pckbd.o vga.o $(SOUND_HW) dma.o 591 OBJS+= ide.o pckbd.o vga.o $(SOUND_HW) dma.o
@@ -37,6 +37,7 @@ @@ -37,6 +37,7 @@
37 #include "virtio-balloon.h" 37 #include "virtio-balloon.h"
38 #include "virtio-console.h" 38 #include "virtio-console.h"
39 #include "hpet_emul.h" 39 #include "hpet_emul.h"
  40 +#include "watchdog.h"
40 #include "smbios.h" 41 #include "smbios.h"
41 42
42 /* output Bochs bios info messages */ 43 /* output Bochs bios info messages */
@@ -1023,6 +1024,8 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size, @@ -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 for(i = 0; i < nb_nics; i++) { 1029 for(i = 0; i < nb_nics; i++) {
1027 NICInfo *nd = &nd_table[i]; 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,6 +27,7 @@
27 #include "hw/pcmcia.h" 27 #include "hw/pcmcia.h"
28 #include "hw/pc.h" 28 #include "hw/pc.h"
29 #include "hw/pci.h" 29 #include "hw/pci.h"
  30 +#include "hw/watchdog.h"
30 #include "gdbstub.h" 31 #include "gdbstub.h"
31 #include "net.h" 32 #include "net.h"
32 #include "qemu-char.h" 33 #include "qemu-char.h"
@@ -597,6 +598,13 @@ static void do_gdbserver(Monitor *mon, const char *device) @@ -597,6 +598,13 @@ static void do_gdbserver(Monitor *mon, const char *device)
597 } 598 }
598 #endif 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 static void monitor_printc(Monitor *mon, int c) 608 static void monitor_printc(Monitor *mon, int c)
601 { 609 {
602 monitor_printf(mon, "'"); 610 monitor_printf(mon, "'");
@@ -1762,6 +1770,8 @@ static const mon_cmd_t mon_cmds[] = { @@ -1762,6 +1770,8 @@ static const mon_cmd_t mon_cmds[] = {
1762 "target", "request VM to change it's memory allocation (in MB)" }, 1770 "target", "request VM to change it's memory allocation (in MB)" },
1763 { "set_link", "ss", do_set_link, 1771 { "set_link", "ss", do_set_link,
1764 "name up|down", "change the link status of a network adapter" }, 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 { "acl", "sss?i?", do_acl, "<command> <aclname> [<match> [<index>]]\n", 1775 { "acl", "sss?i?", do_acl, "<command> <aclname> [<match> [<index>]]\n",
1766 "acl show vnc.username\n" 1776 "acl show vnc.username\n"
1767 "acl policy vnc.username deny\n" 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,6 +1448,55 @@ order cores with complex cache hierarchies. The number of instructions
1448 executed often has little or no correlation with actual performance. 1448 executed often has little or no correlation with actual performance.
1449 ETEXI 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 DEF("echr", HAS_ARG, QEMU_OPTION_echr, \ 1500 DEF("echr", HAS_ARG, QEMU_OPTION_echr, \
1452 "-echr chr set terminal escape character instead of ctrl-a\n") 1501 "-echr chr set terminal escape character instead of ctrl-a\n")
1453 STEXI 1502 STEXI
@@ -138,6 +138,7 @@ int main(int argc, char **argv) @@ -138,6 +138,7 @@ int main(int argc, char **argv)
138 #include "hw/isa.h" 138 #include "hw/isa.h"
139 #include "hw/baum.h" 139 #include "hw/baum.h"
140 #include "hw/bt.h" 140 #include "hw/bt.h"
  141 +#include "hw/watchdog.h"
141 #include "hw/smbios.h" 142 #include "hw/smbios.h"
142 #include "hw/xen.h" 143 #include "hw/xen.h"
143 #include "bt-host.h" 144 #include "bt-host.h"
@@ -252,6 +253,8 @@ int graphic_rotate = 0; @@ -252,6 +253,8 @@ int graphic_rotate = 0;
252 #ifndef _WIN32 253 #ifndef _WIN32
253 int daemonize = 0; 254 int daemonize = 0;
254 #endif 255 #endif
  256 +WatchdogTimerModel *watchdog = NULL;
  257 +int watchdog_action = WDT_RESET;
255 const char *option_rom[MAX_OPTION_ROMS]; 258 const char *option_rom[MAX_OPTION_ROMS];
256 int nb_option_roms; 259 int nb_option_roms;
257 int semihosting_enabled = 0; 260 int semihosting_enabled = 0;
@@ -4917,6 +4920,8 @@ int main(int argc, char **argv, char **envp) @@ -4917,6 +4920,8 @@ int main(int argc, char **argv, char **envp)
4917 tb_size = 0; 4920 tb_size = 0;
4918 autostart= 1; 4921 autostart= 1;
4919 4922
  4923 + register_watchdogs();
  4924 +
4920 optind = 1; 4925 optind = 1;
4921 for(;;) { 4926 for(;;) {
4922 if (optind >= argc) 4927 if (optind >= argc)
@@ -5308,6 +5313,17 @@ int main(int argc, char **argv, char **envp) @@ -5308,6 +5313,17 @@ int main(int argc, char **argv, char **envp)
5308 serial_devices[serial_device_index] = optarg; 5313 serial_devices[serial_device_index] = optarg;
5309 serial_device_index++; 5314 serial_device_index++;
5310 break; 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 case QEMU_OPTION_virtiocon: 5327 case QEMU_OPTION_virtiocon:
5312 if (virtio_console_index >= MAX_VIRTIO_CONSOLES) { 5328 if (virtio_console_index >= MAX_VIRTIO_CONSOLES) {
5313 fprintf(stderr, "qemu: too many virtio consoles\n"); 5329 fprintf(stderr, "qemu: too many virtio consoles\n");