qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH] Hardware watchdog patch, version 5
@ 2009-03-16 18:49 Richard W.M. Jones
  2009-03-25  8:43 ` Richard W.M. Jones
  2009-03-26 13:03 ` Anthony Liguori
  0 siblings, 2 replies; 14+ messages in thread
From: Richard W.M. Jones @ 2009-03-16 18:49 UTC (permalink / raw)
  To: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 1364 bytes --]

This is version 5 of the hardware watchdogs patch.  Thanks to Anthony
Liguori and Blueswirl for looking at the previous version.

What's fixed in this version:

* Separate options:
  -watchdog <model>          selects the model
  -watchdog-action <action>  selects the action that QEMU takes

* The action is changable via the watchdog_action monitor command

* wdt_model struct renamed to WatchdogTimerModel

* Uses "sys-queue.h" macros for the linked list of watchdogs

* All command line parsing has been moved to vl.c

* Intel 6300 state struct renamed to I6300State

* Debug macros instead of #ifdef *_DEBUG

* The host state (action) is no longer stored in the save/restore data

* FSF address updated in license

* Extra actions:
  debug                     prints a debug message and continues
  none                      nothing is done when watchdog timer expires

What's not done in this release:

* PCI hotplug support for the i6300esb.

I think that it's unlikely anyone would want to hotplug a virtual
watchdog device, so that would just be adding complicated code that no
one would ever use.

Rich.

-- 
Richard Jones, Emerging Technologies, Red Hat  http://et.redhat.com/~rjones
virt-top is 'top' for virtual machines.  Tiny program with many
powerful monitoring features, net stats, disk stats, logging, etc.
http://et.redhat.com/~rjones/virt-top

[-- Attachment #2: qemu-watchdog-5.patch --]
[-- Type: text/plain, Size: 33812 bytes --]

Index: Makefile.target
===================================================================
--- Makefile.target	(revision 6855)
+++ Makefile.target	(working copy)
@@ -582,6 +582,9 @@
 # Serial mouse
 OBJS += msmouse.o
 
+# Generic watchdog support
+OBJS += watchdog.o
+
 ifeq ($(TARGET_BASE_ARCH), i386)
 # Hardware support
 OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o
@@ -589,7 +592,8 @@
 OBJS+= cirrus_vga.o apic.o ioapic.o parallel.o acpi.o piix_pci.o
 OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o
 OBJS += device-hotplug.o pci-hotplug.o
-CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
+OBJS+= wdt_ib700.o wdt_i6300esb.o
+CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE -DHAS_WATCHDOGS
 endif
 ifeq ($(TARGET_BASE_ARCH), ppc)
 CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
Index: vl.c
===================================================================
--- vl.c	(revision 6855)
+++ vl.c	(working copy)
@@ -138,6 +138,7 @@
 #include "hw/isa.h"
 #include "hw/baum.h"
 #include "hw/bt.h"
+#include "hw/watchdog.h"
 #include "bt-host.h"
 #include "net.h"
 #include "monitor.h"
@@ -245,6 +246,8 @@
 int cursor_hide = 1;
 int graphic_rotate = 0;
 int daemonize = 0;
+WatchdogTimerModel *watchdog = NULL;
+int watchdog_action = WDT_RESET;
 const char *option_rom[MAX_OPTION_ROMS];
 int nb_option_roms;
 int semihosting_enabled = 0;
@@ -4119,6 +4122,10 @@
            "-old-param      old param mode\n"
 #endif
            "-tb-size n      set TB size\n"
+           "-watchdog i6300esb|ib700\n"
+           "                enable virtual hardware watchdog [default=none]\n"
+           "-watchdog-action reset|shutdown|poweroff|pause|debug|none\n"
+           "                action when watchdog fires [default=reset]\n"
            "-incoming p     prepare for incoming migration, listen on port p\n"
 #ifndef _WIN32
            "-chroot dir     Chroot to dir just before starting the VM.\n"
@@ -4235,6 +4242,8 @@
     QEMU_OPTION_startdate,
     QEMU_OPTION_icount,
     QEMU_OPTION_echr,
+    QEMU_OPTION_watchdog,
+    QEMU_OPTION_watchdog_action,
     QEMU_OPTION_virtiocon,
     QEMU_OPTION_show_cursor,
     QEMU_OPTION_semihosting,
@@ -4363,6 +4372,8 @@
     { "startdate", HAS_ARG, QEMU_OPTION_startdate },
     { "icount", HAS_ARG, QEMU_OPTION_icount },
     { "echr", HAS_ARG, QEMU_OPTION_echr },
+    { "watchdog", HAS_ARG, QEMU_OPTION_watchdog },
+    { "watchdog-action", HAS_ARG, QEMU_OPTION_watchdog_action },
     { "virtioconsole", HAS_ARG, QEMU_OPTION_virtiocon },
     { "show-cursor", 0, QEMU_OPTION_show_cursor },
 #if defined(TARGET_ARM) || defined(TARGET_M68K)
@@ -4559,6 +4570,102 @@
     }
 }
 
+static LIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list;
+
+void
+watchdog_add_model (WatchdogTimerModel *model)
+{
+    LIST_INSERT_HEAD (&watchdog_list, model, entry);
+}
+
+static void
+select_watchdog (const char *p)
+{
+    WatchdogTimerModel *model;
+
+    if (watchdog) {
+        fprintf (stderr,
+                 "qemu: only one watchdog option may be given\n");
+        exit (1);
+    }
+
+    /* -watchdog ? lists available devices and exits cleanly. */
+    if (strcmp (p, "?") == 0) {
+        LIST_FOREACH (model, &watchdog_list, entry) {
+            fprintf (stderr, "\t%s\t%s\n",
+                     model->wdt_name, model->wdt_description);
+        }
+        exit (0);
+    }
+
+    LIST_FOREACH (model, &watchdog_list, entry) {
+        if (strcasecmp (model->wdt_name, p) == 0) {
+            watchdog = model;
+            return;
+        }
+    }
+
+    fprintf (stderr, "Unknown -watchdog device. Supported devices are:\n");
+    LIST_FOREACH (model, &watchdog_list, entry) {
+        fprintf (stderr, "\t%s\t%s\n",
+                 model->wdt_name, model->wdt_description);
+    }
+    exit (1);
+}
+
+int
+select_watchdog_action (const char *p)
+{
+    if (strcasecmp (p, "reset") == 0)
+        watchdog_action = WDT_RESET;
+    else if (strcasecmp (p, "shutdown") == 0)
+        watchdog_action = WDT_SHUTDOWN;
+    else if (strcasecmp (p, "poweroff") == 0)
+        watchdog_action = WDT_POWEROFF;
+    else if (strcasecmp (p, "pause") == 0)
+        watchdog_action = WDT_PAUSE;
+    else if (strcasecmp (p, "debug") == 0)
+        watchdog_action = WDT_DEBUG;
+    else if (strcasecmp (p, "none") == 0)
+        watchdog_action = WDT_NONE;
+    else
+        return -1;
+
+    return 0;
+}
+
+/* This actually performs the "action" once a watchdog has expired,
+ * ie. reboot, shutdown, exit, etc.
+ */
+void
+watchdog_perform_action (void)
+{
+    switch (watchdog_action) {
+    case WDT_RESET:             /* same as 'system_reset' in monitor */
+        qemu_system_reset_request ();
+        break;
+
+    case WDT_SHUTDOWN:          /* same as 'system_powerdown' in monitor */
+        qemu_system_powerdown_request ();
+        break;
+
+    case WDT_POWEROFF:          /* same as 'quit' command in monitor */
+        exit (0);
+        break;
+
+    case WDT_PAUSE:             /* same as 'stop' command in monitor */
+        vm_stop (0);
+        break;
+
+    case WDT_DEBUG:
+        fprintf (stderr, "watchdog: timer fired\n");
+        break;
+
+    case WDT_NONE:
+        break;
+    }
+}
+
 #ifdef _WIN32
 static BOOL WINAPI qemu_ctrl_handler(DWORD type)
 {
@@ -4729,6 +4836,8 @@
     tb_size = 0;
     autostart= 1;
 
+    register_watchdogs ();
+
     optind = 1;
     for(;;) {
         if (optind >= argc)
@@ -5102,6 +5211,15 @@
                 serial_devices[serial_device_index] = optarg;
                 serial_device_index++;
                 break;
+            case QEMU_OPTION_watchdog:
+                select_watchdog (optarg);
+                break;
+            case QEMU_OPTION_watchdog_action:
+                if (select_watchdog_action (optarg) == -1) {
+                    fprintf (stderr, "Unknown -watchdog-action parameter\n");
+                    exit (1);
+                }
+                break;
             case QEMU_OPTION_virtiocon:
                 if (virtio_console_index >= MAX_VIRTIO_CONSOLES) {
                     fprintf(stderr, "qemu: too many virtio consoles\n");
Index: qemu-doc.texi
===================================================================
--- qemu-doc.texi	(revision 6855)
+++ qemu-doc.texi	(working copy)
@@ -1189,6 +1189,43 @@
 @item -echr 20
 @end table
 
+@item -watchdog @var{model}
+Create a virtual hardware watchdog device.  Once enabled (by a guest
+action), the watchdog must be periodically polled by an agent inside
+the guest or else the guest will be restarted.
+
+The @var{model} is the model of hardware watchdog to emulate.  Choices
+for model are: @code{ib700} (iBASE 700) which is a very simple ISA
+watchdog with a single timer, or @code{i6300esb} (Intel 6300ESB I/O
+controller hub) which is a much more featureful PCI-based dual-timer
+watchdog.  Choose a model for which your guest has drivers.
+
+@item -watchdog-action @var{action}
+
+The @var{action} controls what QEMU will do when the watchdog timer
+expires.
+The default is
+@code{reset} (forcefully reset the guest).
+Other possible actions are:
+@code{shutdown} (attempt to gracefully shutdown the guest),
+@code{poweroff} (forcefully poweroff the guest),
+@code{pause} (pause the guest),
+@code{debug} (print a debug message and continue), or
+@code{none} (do nothing).
+
+Note that the @code{shutdown} action requires that the guest responds
+to ACPI signals, which it may not be able to do in the sort of
+situations where the watchdog would have expired, and thus
+@code{-watchdog-action shutdown} is not recommended for production use.
+
+Use @code{-watchdog ?} to list available hardware models.  Only one
+watchdog can be enabled for a guest.
+
+@table @code
+@item -watchdog i6300esb -watchdog-action pause
+@item -watchdog ib700
+@end table
+
 @item -chroot dir
 Immediately before starting guest execution, chroot to the specified
 directory.  Especially useful in combination with -runas.
Index: monitor.c
===================================================================
--- monitor.c	(revision 6855)
+++ monitor.c	(working copy)
@@ -27,6 +27,7 @@
 #include "hw/pcmcia.h"
 #include "hw/pc.h"
 #include "hw/pci.h"
+#include "hw/watchdog.h"
 #include "gdbstub.h"
 #include "net.h"
 #include "qemu-char.h"
@@ -583,6 +584,13 @@
 }
 #endif
 
+static void do_watchdog_action (Monitor *mon, const char *action)
+{
+    if (select_watchdog_action (action) == -1) {
+        monitor_printf (mon, "Unknown watchdog action '%s'\n", action);
+    }
+}
+
 static void monitor_printc(Monitor *mon, int c)
 {
     monitor_printf(mon, "'");
@@ -1719,6 +1727,8 @@
       "target", "request VM to change it's memory allocation (in MB)" },
     { "set_link", "ss", do_set_link,
       "name [up|down]", "change the link status of a network adapter" },
+    { "watchdog_action", "s", do_watchdog_action,
+      "[reset|shutdown|poweroff|pause|debug|none]", "change watchdog action" },
     { "acl", "sss?i?", do_acl, "<command> <aclname> [<match>] [<index>]\n",
                                "acl show vnc.username\n"
                                "acl policy vnc.username deny\n"
Index: hw/watchdog.c
===================================================================
--- hw/watchdog.c	(revision 0)
+++ hw/watchdog.c	(revision 0)
@@ -0,0 +1,42 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#include "qemu-common.h"
+#include "sys-queue.h"
+#include "hw/watchdog.h"
+
+void
+watchdog_pc_init (PCIBus *pci_bus)
+{
+    if (watchdog)
+        watchdog->wdt_pc_init (pci_bus);
+}
+
+void
+register_watchdogs (void)
+{
+#ifdef HAS_WATCHDOGS
+    wdt_ib700_init ();
+    wdt_i6300esb_init ();
+#endif
+}
Index: hw/watchdog.h
===================================================================
--- hw/watchdog.h	(revision 0)
+++ hw/watchdog.h	(revision 0)
@@ -0,0 +1,65 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#ifndef QEMU_WATCHDOG_H
+#define QEMU_WATCHDOG_H
+
+extern void wdt_i6300esb_init (void);
+extern void wdt_ib700_init (void);
+
+/* Possible values for action parameter. */
+#define WDT_RESET        1	/* Hard reset. */
+#define WDT_SHUTDOWN     2	/* Shutdown. */
+#define WDT_POWEROFF     3	/* Quit. */
+#define WDT_PAUSE        4	/* Pause. */
+#define WDT_DEBUG        5	/* Prints a message and continues running. */
+#define WDT_NONE         6	/* Do nothing. */
+
+struct WatchdogTimerModel {
+    LIST_ENTRY(WatchdogTimerModel) entry;
+
+    /* Short name of the device - used to select it on the command line. */
+    const char *wdt_name;
+    /* Longer description (eg. manufacturer and full model number). */
+    const char *wdt_description;
+
+    /* This callback should create/register the device.  It is called
+     * indirectly from hw/pc.c when the virtual PC is being set up.
+     */
+    void (*wdt_pc_init) (PCIBus *pci_bus);
+};
+typedef struct WatchdogTimerModel WatchdogTimerModel;
+
+/* in vl.c */
+extern int select_watchdog_action (const char *action);
+extern void watchdog_add_model (WatchdogTimerModel *model);
+extern void watchdog_perform_action (void);
+
+/* in hw/watchdog.c */
+extern void watchdog_pc_init (PCIBus *pci_bus);
+extern void register_watchdogs (void);
+
+/* The currently selected watchdog model. */
+extern WatchdogTimerModel *watchdog;
+
+#endif /* QEMU_WATCHDOG_H */
Index: hw/wdt_i6300esb.c
===================================================================
--- hw/wdt_i6300esb.c	(revision 0)
+++ hw/wdt_i6300esb.c	(revision 0)
@@ -0,0 +1,501 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#include <inttypes.h>
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "watchdog.h"
+#include "hw.h"
+#include "isa.h"
+#include "pc.h"
+#include "pci.h"
+
+/*#define I6300ESB_DEBUG 1*/
+
+#ifdef I6300ESB_DEBUG
+#define i6300esb_debug(fs,...) \
+    fprintf(stderr,"i6300esb: %s: "fs,__func__,##__VA_ARGS__)
+#else
+#define i6300esb_debug(fs,...)
+#endif
+
+#ifndef PCI_DEVICE_ID_INTEL_ESB_9
+#define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab
+#endif
+
+/* PCI configuration registers */
+#define ESB_CONFIG_REG  0x60            /* Config register                   */
+#define ESB_LOCK_REG    0x68            /* WDT lock register                 */
+
+/* Memory mapped registers (offset from base address) */
+#define ESB_TIMER1_REG  0x00            /* Timer1 value after each reset     */
+#define ESB_TIMER2_REG  0x04            /* Timer2 value after each reset     */
+#define ESB_GINTSR_REG  0x08            /* General Interrupt Status Register */
+#define ESB_RELOAD_REG  0x0c            /* Reload register                   */
+
+/* Lock register bits */
+#define ESB_WDT_FUNC    (0x01 << 2)   /* Watchdog functionality            */
+#define ESB_WDT_ENABLE  (0x01 << 1)   /* Enable WDT                        */
+#define ESB_WDT_LOCK    (0x01 << 0)   /* Lock (nowayout)                   */
+
+/* Config register bits */
+#define ESB_WDT_REBOOT  (0x01 << 5)   /* Enable reboot on timeout          */
+#define ESB_WDT_FREQ    (0x01 << 2)   /* Decrement frequency               */
+#define ESB_WDT_INTTYPE (0x11 << 0)   /* Interrupt type on timer1 timeout  */
+
+/* Reload register bits */
+#define ESB_WDT_RELOAD  (0x01 << 8)    /* prevent timeout                   */
+
+/* Magic constants */
+#define ESB_UNLOCK1     0x80            /* Step 1 to unlock reset registers  */
+#define ESB_UNLOCK2     0x86            /* Step 2 to unlock reset registers  */
+
+/* Device state. */
+struct I6300State {
+    PCIDevice dev;              /* PCI device state, must be first field. */
+
+    int reboot_enabled;         /* "Reboot" on timer expiry.  The real action
+                                 * performed depends on the -watchdog-action
+                                 * param passed on QEMU command line.
+                                 */
+    int clock_scale;            /* Clock scale. */
+#define CLOCK_SCALE_1KHZ 0
+#define CLOCK_SCALE_1MHZ 1
+
+    int int_type;               /* Interrupt type generated. */
+#define INT_TYPE_IRQ 0          /* APIC 1, INT 10 */
+#define INT_TYPE_SMI 2
+#define INT_TYPE_DISABLED 3
+
+    int free_run;               /* If true, reload timer on expiry. */
+    int locked;                 /* If true, enabled field cannot be changed. */
+    int enabled;                /* If true, watchdog is enabled. */
+
+    QEMUTimer *timer;           /* The actual watchdog timer. */
+
+    uint32_t timer1_preload;    /* Values preloaded into timer1, timer2. */
+    uint32_t timer2_preload;
+    int stage;                  /* Stage (1 or 2). */
+
+    int unlock_state;           /* Guest writes 0x80, 0x86 to unlock the
+                                 * registers, and we transition through
+                                 * states 0 -> 1 -> 2 when this happens.
+                                 */
+
+    int previous_reboot_flag;   /* If the watchdog caused the previous
+                                 * reboot, this flag will be set.
+                                 */
+};
+
+typedef struct I6300State I6300State;
+
+static void i6300esb_pc_init (PCIBus *pci_bus);
+static void i6300esb_map (PCIDevice *dev, int region_num, uint32_t addr, uint32_t size, int type);
+static void i6300esb_config_write (PCIDevice *dev, uint32_t addr, uint32_t data, int len);
+static uint32_t i6300esb_config_read (PCIDevice *dev, uint32_t addr, int len);
+static uint32_t i6300esb_mem_readb (void *vp, target_phys_addr_t addr);
+static uint32_t i6300esb_mem_readw (void *vp, target_phys_addr_t addr);
+static uint32_t i6300esb_mem_readl (void *vp, target_phys_addr_t addr);
+static void i6300esb_mem_writeb (void *vp, target_phys_addr_t addr, uint32_t val);
+static void i6300esb_mem_writew (void *vp, target_phys_addr_t addr, uint32_t val);
+static void i6300esb_mem_writel (void *vp, target_phys_addr_t addr, uint32_t val);
+static void i6300esb_restart_timer (I6300State *, int stage);
+static void i6300esb_disable_timer (I6300State *);
+static void i6300esb_timer_expired (void *vp);
+static void i6300esb_reset (I6300State *d);
+static void i6300esb_save (QEMUFile *f, void *vp);
+static int i6300esb_load (QEMUFile *f, void *vp, int version);
+
+static WatchdogTimerModel model = {
+    .wdt_name = "i6300esb",
+    .wdt_description = "Intel 6300ESB",
+    .wdt_pc_init = i6300esb_pc_init,
+};
+
+void
+wdt_i6300esb_init (void)
+{
+    watchdog_add_model (&model);
+}
+
+/* Create and initialize a virtual Intel 6300ESB during PC creation. */
+static void
+i6300esb_pc_init (PCIBus *pci_bus)
+{
+    I6300State *d;
+    uint8_t *pci_conf;
+
+    if (!pci_bus) {
+        fprintf (stderr, "wdt_i6300esb: no PCI bus in this machine\n");
+        return;
+    }
+
+    d = (I6300State *)
+        pci_register_device (pci_bus, "i6300esb_wdt", sizeof (I6300State),
+                             -1,
+                             i6300esb_config_read, i6300esb_config_write);
+
+    d->reboot_enabled = 1;
+    d->clock_scale = CLOCK_SCALE_1KHZ;
+    d->int_type = INT_TYPE_IRQ;
+    d->free_run = 0;
+    d->locked = 0;
+    d->enabled = 0;
+    d->timer = qemu_new_timer (vm_clock, i6300esb_timer_expired, d);
+    d->timer1_preload = 0xfffff;
+    d->timer2_preload = 0xfffff;
+    d->stage = 1;
+    d->unlock_state = 0;
+    d->previous_reboot_flag = 0;
+
+    pci_conf = d->dev.config;
+    pci_config_set_vendor_id (pci_conf, PCI_VENDOR_ID_INTEL);
+    pci_config_set_device_id (pci_conf, PCI_DEVICE_ID_INTEL_ESB_9);
+    pci_config_set_class (pci_conf, PCI_CLASS_SYSTEM_OTHER);
+    pci_conf[0x0e] = 0x00;
+
+    pci_register_io_region (&d->dev, 0, 0x10,
+                            PCI_ADDRESS_SPACE_MEM, i6300esb_map);
+
+    register_savevm ("i6300esb_wdt", -1, sizeof (I6300State),
+                     i6300esb_save, i6300esb_load, d);
+}
+
+static void
+i6300esb_map (PCIDevice *dev, int region_num,
+              uint32_t addr, uint32_t size, int type)
+{
+    static CPUReadMemoryFunc *mem_read[3] = {
+        i6300esb_mem_readb,
+        i6300esb_mem_readw,
+        i6300esb_mem_readl,
+    };
+    static CPUWriteMemoryFunc *mem_write[3] = {
+        i6300esb_mem_writeb,
+        i6300esb_mem_writew,
+        i6300esb_mem_writel,
+    };
+    I6300State *d = (I6300State *) dev;
+    int io_mem;
+
+    i6300esb_debug ("addr = %x, size = %x, type = %d\n", addr, size, type);
+
+    io_mem = cpu_register_io_memory (0, mem_read, mem_write, d);
+    cpu_register_physical_memory (addr, 0x10, io_mem);
+    /* qemu_register_coalesced_mmio (addr, 0x10); ? */
+}
+
+static void
+i6300esb_config_write (PCIDevice *dev, uint32_t addr, uint32_t data, int len)
+{
+    I6300State *d = (I6300State *) dev;
+    int old;
+
+    i6300esb_debug ("addr = %x, data = %x, len = %d\n", addr, data, len);
+
+    if (addr == ESB_CONFIG_REG && len == 2) {
+        d->reboot_enabled = (data & ESB_WDT_REBOOT) == 0;
+        d->clock_scale =
+            (data & ESB_WDT_FREQ) != 0 ? CLOCK_SCALE_1MHZ : CLOCK_SCALE_1KHZ;
+        d->int_type = (data & ESB_WDT_INTTYPE);
+    } else if (addr == ESB_LOCK_REG && len == 1) {
+        if (!d->locked) {
+            d->locked = (data & ESB_WDT_LOCK) != 0;
+            d->free_run = (data & ESB_WDT_FUNC) != 0;
+            old = d->enabled;
+            d->enabled = (data & ESB_WDT_ENABLE) != 0;
+            if (!old && d->enabled) /* Enabled transitioned from 0 -> 1 */
+                i6300esb_restart_timer (d, 1);
+            else if (!d->enabled)
+                i6300esb_disable_timer (d);
+        }
+    } else {
+        pci_default_write_config (dev, addr, data, len);
+    }
+}
+
+static uint32_t
+i6300esb_config_read (PCIDevice *dev, uint32_t addr, int len)
+{
+    I6300State *d = (I6300State *) dev;
+    uint32_t data;
+
+    i6300esb_debug ("addr = %x, len = %d\n", addr, len);
+
+    if (addr == ESB_CONFIG_REG && len == 2) {
+        data =
+            (d->reboot_enabled ? 0 : ESB_WDT_REBOOT) |
+            (d->clock_scale == CLOCK_SCALE_1MHZ ? ESB_WDT_FREQ : 0) |
+            d->int_type;
+        return data;
+    } else if (addr == ESB_LOCK_REG && len == 1) {
+        data =
+            (d->free_run ? ESB_WDT_FUNC : 0) |
+            (d->locked ? ESB_WDT_LOCK : 0) |
+            (d->enabled ? ESB_WDT_ENABLE : 0);
+        return data;
+    } else {
+        return pci_default_read_config (dev, addr, len);
+    }
+}
+
+static uint32_t
+i6300esb_mem_readb (void *vp, target_phys_addr_t addr)
+{
+    i6300esb_debug ("addr = %x\n", (int) addr);
+
+    return 0;
+}
+
+static uint32_t
+i6300esb_mem_readw (void *vp, target_phys_addr_t addr)
+{
+    uint32_t data = 0;
+    I6300State *d = (I6300State *) vp;
+
+    i6300esb_debug ("addr = %x\n", (int) addr);
+
+    if (addr == 0xc) {
+        /* The previous reboot flag is really bit 9, but there is
+         * a bug in the Linux driver where it thinks it's bit 12.
+         * Set both.
+         */
+        data = d->previous_reboot_flag ? 0x1200 : 0;
+    }
+
+    return data;
+}
+
+static uint32_t
+i6300esb_mem_readl (void *vp, target_phys_addr_t addr)
+{
+    i6300esb_debug ("addr = %x\n", (int) addr);
+
+    return 0;
+}
+
+static void
+i6300esb_mem_writeb (void *vp, target_phys_addr_t addr, uint32_t val)
+{
+    I6300State *d = (I6300State *) vp;
+
+    i6300esb_debug ("addr = %x, val = %x\n", (int) addr, val);
+
+    if (addr == 0xc && val == 0x80)
+        d->unlock_state = 1;
+    else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
+        d->unlock_state = 2;
+}
+
+static void
+i6300esb_mem_writew (void *vp, target_phys_addr_t addr, uint32_t val)
+{
+    I6300State *d = (I6300State *) vp;
+
+    i6300esb_debug ("addr = %x, val = %x\n", (int) addr, val);
+
+    if (addr == 0xc && val == 0x80)
+        d->unlock_state = 1;
+    else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
+        d->unlock_state = 2;
+    else {
+        if (d->unlock_state == 2) {
+            if (addr == 0xc) {
+                if ((val & 0x100) != 0)
+                    /* This is the "ping" from the userspace watchdog in
+                     * the guest ...
+                     */
+                    i6300esb_restart_timer (d, 1);
+
+                /* Setting bit 9 resets the previous reboot flag.
+                 * There's a bug in the Linux driver where it sets
+                 * bit 12 instead.
+                 */
+                if ((val & 0x200) != 0 || (val & 0x1000) != 0) {
+                    d->previous_reboot_flag = 0;
+                }
+            }
+
+            d->unlock_state = 0;
+        }
+    }
+}
+
+static void
+i6300esb_mem_writel (void *vp, target_phys_addr_t addr, uint32_t val)
+{
+    I6300State *d = (I6300State *) vp;
+
+    i6300esb_debug ("addr = %x, val = %x\n", (int) addr, val);
+
+    if (addr == 0xc && val == 0x80)
+        d->unlock_state = 1;
+    else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
+        d->unlock_state = 2;
+    else {
+        if (d->unlock_state == 2) {
+            if (addr == 0)
+                d->timer1_preload = val & 0xfffff;
+            else if (addr == 4)
+                d->timer2_preload = val & 0xfffff;
+
+            d->unlock_state = 0;
+        }
+    }
+}
+
+/* This function is called when the watchdog has either been enabled
+ * (hence it starts counting down) or has been keep-alived.
+ */
+static void
+i6300esb_restart_timer (I6300State *d, int stage)
+{
+    int64_t timeout;
+
+    if (!d->enabled) return;
+
+    d->stage = stage;
+
+    if (d->stage <= 1)
+        timeout = d->timer1_preload;
+    else
+        timeout = d->timer2_preload;
+
+    if (d->clock_scale == CLOCK_SCALE_1KHZ)
+        timeout <<= 15;
+    else
+        timeout <<= 5;
+
+    /* Get the timeout in units of ticks_per_sec. */
+    timeout = ticks_per_sec * timeout / 33000000;
+
+    i6300esb_debug ("stage %d, timeout %" PRIi64 "\n", d->stage, timeout);
+
+    qemu_mod_timer (d->timer, qemu_get_clock (vm_clock) + timeout);
+}
+
+/* This is called when the guest disables the watchdog. */
+static void
+i6300esb_disable_timer (I6300State *d)
+{
+    i6300esb_debug ("timer disabled\n");
+
+    qemu_del_timer (d->timer);
+}
+
+/* This function is called when the watchdog expires.  Note that
+ * the hardware has two timers, and so expiry happens in two stages.
+ * If d->stage == 1 then we perform the first stage action (usually,
+ * sending an interrupt) and then restart the timer again for the
+ * second stage.  If the second stage expires then the watchdog
+ * really has run out.
+ */
+static void
+i6300esb_timer_expired (void *vp)
+{
+    I6300State *d = (I6300State *) vp;
+
+    i6300esb_debug ("stage %d\n", d->stage);
+
+    if (d->stage == 1) {
+        /* What to do at the end of stage 1? */
+        switch (d->int_type) {
+        case INT_TYPE_IRQ:
+            fprintf (stderr, "i6300esb_timer_expired: I would send APIC 1 INT 10 here if I knew how (XXX)\n");
+            break;
+        case INT_TYPE_SMI:
+            fprintf (stderr, "i6300esb_timer_expired: I would send SMI here if I knew how (XXX)\n");
+            break;
+        }
+
+        /* Start the second stage. */
+        i6300esb_restart_timer (d, 2);
+    } else {
+        /* Second stage expired, reboot for real. */
+        if (d->reboot_enabled) {
+            d->previous_reboot_flag = 1;
+            watchdog_perform_action (); /* This reboots, exits, etc */
+            i6300esb_reset (d);
+        }
+
+        /* In "free running mode" we start stage 1 again. */
+        if (d->free_run)
+            i6300esb_restart_timer (d, 1);
+    }
+}
+
+static void
+i6300esb_reset (I6300State *d)
+{
+    /* XXX We should probably reset other parts of the state here,
+     * but we should also reset our state on general machine reset
+     * too.  For now just disable the timer so it doesn't fire
+     * again after the reboot.
+     */
+    i6300esb_disable_timer (d);
+}
+
+static void
+i6300esb_save (QEMUFile *f, void *vp)
+{
+    I6300State *d = (I6300State *) vp;
+
+    pci_device_save (&d->dev, f);
+    qemu_put_be32 (f, d->reboot_enabled);
+    qemu_put_be32 (f, d->clock_scale);
+    qemu_put_be32 (f, d->int_type);
+    qemu_put_be32 (f, d->free_run);
+    qemu_put_be32 (f, d->locked);
+    qemu_put_be32 (f, d->enabled);
+    qemu_put_timer (f, d->timer);
+    qemu_put_be32 (f, d->timer1_preload);
+    qemu_put_be32 (f, d->timer2_preload);
+    qemu_put_be32 (f, d->stage);
+    qemu_put_be32 (f, d->unlock_state);
+    qemu_put_be32 (f, d->previous_reboot_flag);
+}
+
+static int
+i6300esb_load (QEMUFile *f, void *vp, int version)
+{
+    I6300State *d = (I6300State *) vp;
+
+    if (version != sizeof (I6300State)) return -EINVAL;
+
+    pci_device_load (&d->dev, f);
+    d->reboot_enabled = qemu_get_be32 (f);
+    d->clock_scale = qemu_get_be32 (f);
+    d->int_type = qemu_get_be32 (f);
+    d->free_run = qemu_get_be32 (f);
+    d->locked = qemu_get_be32 (f);
+    d->enabled = qemu_get_be32 (f);
+    qemu_get_timer (f, d->timer);
+    d->timer1_preload = qemu_get_be32 (f);
+    d->timer2_preload = qemu_get_be32 (f);
+    d->stage = qemu_get_be32 (f);
+    d->unlock_state = qemu_get_be32 (f);
+    d->previous_reboot_flag = qemu_get_be32 (f);
+
+    return 0;
+}
Index: hw/wdt_ib700.c
===================================================================
--- hw/wdt_ib700.c	(revision 0)
+++ hw/wdt_ib700.c	(revision 0)
@@ -0,0 +1,125 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "watchdog.h"
+#include "hw.h"
+#include "isa.h"
+#include "pc.h"
+
+/*#define IB700_DEBUG 1*/
+
+#ifdef IB700_DEBUG
+#define ib700_debug(fs,...)					\
+    fprintf(stderr,"ib700: %s: "fs,__func__,##__VA_ARGS__)
+#else
+#define ib700_debug(fs,...)
+#endif
+
+static void ib700_pc_init (PCIBus *unused);
+static void ib700_write_enable_reg (void *vp, uint32_t addr, uint32_t data);
+static void ib700_write_disable_reg (void *vp, uint32_t addr, uint32_t data);
+static void ib700_timer_expired (void *vp);
+static void ib700_save (QEMUFile *f, void *vp);
+static int ib700_load (QEMUFile *f, void *vp, int version);
+
+/* This is the timer.  We use a global here because the watchdog
+ * code ensures there is only one watchdog (it is located at a fixed,
+ * unchangable IO port, so there could only ever be one anyway).
+ */
+static QEMUTimer *timer = NULL;
+
+static WatchdogTimerModel model = {
+    .wdt_name = "ib700",
+    .wdt_description = "iBASE 700",
+    .wdt_pc_init = ib700_pc_init,
+};
+
+void
+wdt_ib700_init (void)
+{
+    watchdog_add_model (&model);
+    timer = qemu_new_timer (vm_clock, ib700_timer_expired, NULL);
+}
+
+/* Create and initialize a virtual IB700 during PC creation. */
+static void
+ib700_pc_init (PCIBus *unused)
+{
+    register_savevm ("ib700_wdt", -1, 0, ib700_save, ib700_load, NULL);
+
+    register_ioport_write (0x441, 2, 1, ib700_write_disable_reg, NULL);
+    register_ioport_write (0x443, 2, 1, ib700_write_enable_reg, NULL);
+}
+
+/* A write to this register enables the timer. */
+static void
+ib700_write_enable_reg (void *vp, uint32_t addr, uint32_t data)
+{
+    static int time_map[] = {
+        30, 28, 26, 24, 22, 20, 18, 16,
+        14, 12, 10,  8,  6,  4,  2,  0
+    };
+    int64 timeout;
+
+    ib700_debug ("addr = %x, data = %x\n", addr, data);
+
+    timeout = (int64_t) time_map[data & 0xF] * ticks_per_sec;
+    qemu_mod_timer (timer, qemu_get_clock (vm_clock) + timeout);
+}
+
+/* A write (of any value) to this register disables the timer. */
+static void
+ib700_write_disable_reg (void *vp, uint32_t addr, uint32_t data)
+{
+    ib700_debug ("addr = %x, data = %x\n", addr, data);
+
+    qemu_del_timer (timer);
+}
+
+/* This is called when the watchdog expires. */
+static void
+ib700_timer_expired (void *vp)
+{
+    ib700_debug ("watchdog expired\n");
+
+    watchdog_perform_action ();
+    qemu_del_timer (timer);
+}
+
+static void
+ib700_save (QEMUFile *f, void *vp)
+{
+    qemu_put_timer (f, timer);
+}
+
+static int
+ib700_load (QEMUFile *f, void *vp, int version)
+{
+    if (version != 0) return -EINVAL;
+
+    qemu_get_timer (f, timer);
+
+    return 0;
+}
Index: hw/pc.c
===================================================================
--- hw/pc.c	(revision 6855)
+++ hw/pc.c	(working copy)
@@ -37,6 +37,7 @@
 #include "virtio-balloon.h"
 #include "virtio-console.h"
 #include "hpet_emul.h"
+#include "watchdog.h"
 
 /* output Bochs bios info messages */
 //#define DEBUG_BIOS
@@ -1005,6 +1006,8 @@
         }
     }
 
+    watchdog_pc_init (pci_bus);
+
     for(i = 0; i < nb_nics; i++) {
         NICInfo *nd = &nd_table[i];
 

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH] Hardware watchdog patch, version 5
  2009-03-16 18:49 [Qemu-devel] [PATCH] Hardware watchdog patch, version 5 Richard W.M. Jones
@ 2009-03-25  8:43 ` Richard W.M. Jones
  2009-03-26 13:03 ` Anthony Liguori
  1 sibling, 0 replies; 14+ messages in thread
From: Richard W.M. Jones @ 2009-03-25  8:43 UTC (permalink / raw)
  To: qemu-devel

On Mon, Mar 16, 2009 at 06:49:29PM +0000, Richard W.M. Jones wrote:
> This is version 5 of the hardware watchdogs patch.  Thanks to Anthony
> Liguori and Blueswirl for looking at the previous version.

Anyone have any comments on this one?  It should fix what people
said about the previous versions ..

Rich.

-- 
Richard Jones, Emerging Technologies, Red Hat  http://et.redhat.com/~rjones
New in Fedora 11: Fedora Windows cross-compiler. Compile Windows
programs, test, and build Windows installers. Over 70 libraries supprt'd
http://fedoraproject.org/wiki/MinGW http://www.annexia.org/fedora_mingw

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH] Hardware watchdog patch, version 5
  2009-03-16 18:49 [Qemu-devel] [PATCH] Hardware watchdog patch, version 5 Richard W.M. Jones
  2009-03-25  8:43 ` Richard W.M. Jones
@ 2009-03-26 13:03 ` Anthony Liguori
  2009-03-31 11:23   ` [PATCH] Hardware watchdog patch, version 6 (was: Re: [Qemu-devel] [PATCH] Hardware watchdog patch, version 5) Richard W.M. Jones
  1 sibling, 1 reply; 14+ messages in thread
From: Anthony Liguori @ 2009-03-26 13:03 UTC (permalink / raw)
  To: qemu-devel

Richard W.M. Jones wrote:
> I think that it's unlikely anyone would want to hotplug a virtual
> watchdog device, so that would just be adding complicated code that no
> one would ever use.
>
> Rich.
>   

I think we're getting pretty close.  I did a thorough review and I don't 
see any major issues except for the lack of a Signed-off-by :-)  So if 
you make the stylistic suggestions below, I think it'll be ready for 
inclusion.

BTW, if anyone wants to write a QEMU CodingStyle, it would be 
tremendously useful.  I'll try to get to it at some point but it may 
take me a while.

> Index: Makefile.target
> ===================================================================
> --- Makefile.target	(revision 6855)
> +++ Makefile.target	(working copy)
> @@ -582,6 +582,9 @@
>  # Serial mouse
>  OBJS += msmouse.o
>  
> +# Generic watchdog support
> +OBJS += watchdog.o
> +
>  ifeq ($(TARGET_BASE_ARCH), i386)
>  # Hardware support
>  OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o
> @@ -589,7 +592,8 @@
>  OBJS+= cirrus_vga.o apic.o ioapic.o parallel.o acpi.o piix_pci.o
>  OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o
>  OBJS += device-hotplug.o pci-hotplug.o
> -CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
> +OBJS+= wdt_ib700.o wdt_i6300esb.o
> +CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE -DHAS_WATCHDOGS
>   

I don't think this is the right way to address this problem as it isn't 
likely to scale as we add additional devices.  I'd suggest either 
registering the watch dogs in the pc init function or better yet, as 
part of PCI/ISA initialization.  Hopefully this will all get a lot 
cleaner as Markus' device config patches get included but for now, let's 
try to at least avoid using CFLAGS to solve this.

>  endif
>  ifeq ($(TARGET_BASE_ARCH), ppc)
>  CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
> Index: vl.c
> ===================================================================
> --- vl.c	(revision 6855)
> +++ vl.c	(working copy)
> +static LIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list;
> +
> +void
> +watchdog_add_model (WatchdogTimerModel *model)
> +{
> +    LIST_INSERT_HEAD (&watchdog_list, model, entry);
> +}
>   

void watchdog_add_model(WatchdogTimerModel *mode)
{
    LIST_INSERT_HEAD(&watchdog_list, model, entry);
}

Return type on the same line as function name, no space after identifier 
and first '('.  This is how the vast majority of QEMU code does it.

> +
> +static void
> +select_watchdog (const char *p)
> +{
> +    WatchdogTimerModel *model;
> +
> +    if (watchdog) {
> +        fprintf (stderr,
> +                 "qemu: only one watchdog option may be given\n");
> +        exit (1);
> +    }
> +
> +    /* -watchdog ? lists available devices and exits cleanly. */
> +    if (strcmp (p, "?") == 0) {
> +        LIST_FOREACH (model, &watchdog_list, entry) {
> +            fprintf (stderr, "\t%s\t%s\n",
> +                     model->wdt_name, model->wdt_description);
> +        }
> +        exit (0);
> +    }
>   

How about returning a value in this function and then having the caller 
exit?  At some point RSN we're going to have to go through QEMU and get 
rid of all the open-coded calls to exit().

> Index: qemu-doc.texi
> ===================================================================
> --- qemu-doc.texi	(revision 6855)
> +++ qemu-doc.texi	(working copy)
> @@ -1189,6 +1189,43 @@
>  @item -echr 20
>  @end table
>  
> +@item -watchdog @var{model}
> +Create a virtual hardware watchdog device.  Once enabled (by a guest
> +action), the watchdog must be periodically polled by an agent inside
> +the guest or else the guest will be restarted.
> +
> +The @var{model} is the model of hardware watchdog to emulate.  Choices
> +for model are: @code{ib700} (iBASE 700) which is a very simple ISA
> +watchdog with a single timer, or @code{i6300esb} (Intel 6300ESB I/O
> +controller hub) which is a much more featureful PCI-based dual-timer
> +watchdog.  Choose a model for which your guest has drivers.
> +
> +@item -watchdog-action @var{action}
> +
> +The @var{action} controls what QEMU will do when the watchdog timer
> +expires.
> +The default is
> +@code{reset} (forcefully reset the guest).
> +Other possible actions are:
> +@code{shutdown} (attempt to gracefully shutdown the guest),
> +@code{poweroff} (forcefully poweroff the guest),
> +@code{pause} (pause the guest),
> +@code{debug} (print a debug message and continue), or
> +@code{none} (do nothing).
> +
> +Note that the @code{shutdown} action requires that the guest responds
> +to ACPI signals, which it may not be able to do in the sort of
> +situations where the watchdog would have expired, and thus
> +@code{-watchdog-action shutdown} is not recommended for production use.
> +
> +Use @code{-watchdog ?} to list available hardware models.  Only one
> +watchdog can be enabled for a guest.
> +
> +@table @code
> +@item -watchdog i6300esb -watchdog-action pause
> +@item -watchdog ib700
> +@end table
> +
>   

Nice documentation.  Particularly the ACPI gotcha bits.  We need more of 
that.

> Index: hw/watchdog.h
> ===================================================================
> --- hw/watchdog.h	(revision 0)
> +++ hw/watchdog.h	(revision 0)
> @@ -0,0 +1,65 @@
> +/*
> + * Virtual hardware watchdog.
> + *
> + * Copyright (C) 2009 Red Hat Inc.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
> + * USA.
> + *
> + * By Richard W.M. Jones (rjones@redhat.com).
> + */
> +
> +#ifndef QEMU_WATCHDOG_H
> +#define QEMU_WATCHDOG_H
> +
> +extern void wdt_i6300esb_init (void);
> +extern void wdt_ib700_init (void);
> +
> +/* Possible values for action parameter. */
> +#define WDT_RESET        1	/* Hard reset. */
> +#define WDT_SHUTDOWN     2	/* Shutdown. */
> +#define WDT_POWEROFF     3	/* Quit. */
> +#define WDT_PAUSE        4	/* Pause. */
> +#define WDT_DEBUG        5	/* Prints a message and continues running. */
> +#define WDT_NONE         6	/* Do nothing. */
> +
> +struct WatchdogTimerModel {
> +    LIST_ENTRY(WatchdogTimerModel) entry;
> +
> +    /* Short name of the device - used to select it on the command line. */
> +    const char *wdt_name;
> +    /* Longer description (eg. manufacturer and full model number). */
> +    const char *wdt_description;
> +
> +    /* This callback should create/register the device.  It is called
> +     * indirectly from hw/pc.c when the virtual PC is being set up.
> +     */
> +    void (*wdt_pc_init) (PCIBus *pci_bus);
> +};
> +typedef struct WatchdogTimerModel WatchdogTimerModel;
> +
> +/* in vl.c */
> +extern int select_watchdog_action (const char *action);
> +extern void watchdog_add_model (WatchdogTimerModel *model);
> +extern void watchdog_perform_action (void);
>   
What not move these things to watchdog.c?  We should try to avoid adding 
stuff to vl.c whenever possible...

> +#ifdef I6300ESB_DEBUG
> +#define i6300esb_debug(fs,...) \
> +    fprintf(stderr,"i6300esb: %s: "fs,__func__,##__VA_ARGS__)
> +#else
> +#define i6300esb_debug(fs,...)
>   
#define i6300esb_debug(fs,...) do {} while (0)
> +
> +static void i6300esb_pc_init (PCIBus *pci_bus);
> +static void i6300esb_map (PCIDevice *dev, int region_num, uint32_t addr, uint32_t size, int type);
> +static void i6300esb_config_write (PCIDevice *dev, uint32_t addr, uint32_t data, int len);
> +static uint32_t i6300esb_config_read (PCIDevice *dev, uint32_t addr, int len);
> +static uint32_t i6300esb_mem_readb (void *vp, target_phys_addr_t addr);
> +static uint32_t i6300esb_mem_readw (void *vp, target_phys_addr_t addr);
> +static uint32_t i6300esb_mem_readl (void *vp, target_phys_addr_t addr);
> +static void i6300esb_mem_writeb (void *vp, target_phys_addr_t addr, uint32_t val);
> +static void i6300esb_mem_writew (void *vp, target_phys_addr_t addr, uint32_t val);
> +static void i6300esb_mem_writel (void *vp, target_phys_addr_t addr, uint32_t val);
> +static void i6300esb_restart_timer (I6300State *, int stage);
> +static void i6300esb_disable_timer (I6300State *);
> +static void i6300esb_timer_expired (void *vp);
> +static void i6300esb_reset (I6300State *d);
> +static void i6300esb_save (QEMUFile *f, void *vp);
> +static int i6300esb_load (QEMUFile *f, void *vp, int version);
>   

If you have to predefine a bunch of statics, it suggests you should 
probably rearrange your code.

Regards,

Anthony Liguori

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH] Hardware watchdog patch, version 6 (was: Re: [Qemu-devel] [PATCH] Hardware watchdog patch, version 5)
  2009-03-26 13:03 ` Anthony Liguori
@ 2009-03-31 11:23   ` Richard W.M. Jones
  2009-03-31 11:24     ` Richard W.M. Jones
  2009-03-31 16:07     ` Blue Swirl
  0 siblings, 2 replies; 14+ messages in thread
From: Richard W.M. Jones @ 2009-03-31 11:23 UTC (permalink / raw)
  To: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 1578 bytes --]

On Thu, Mar 26, 2009 at 08:03:47AM -0500, Anthony Liguori wrote:
> Richard W.M. Jones wrote:
>> -CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
>> +OBJS+= wdt_ib700.o wdt_i6300esb.o
>> +CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE -DHAS_WATCHDOGS
>
> I don't think this is the right way to address this problem as it isn't  
> likely to scale as we add additional devices.  I'd suggest either  
> registering the watch dogs in the pc init function or better yet, as  
> part of PCI/ISA initialization.  Hopefully this will all get a lot  
> cleaner as Markus' device config patches get included but for now, let's  
> try to at least avoid using CFLAGS to solve this.

I wasn't sure how to do this 'correctly'.  If I initialize the
watchdog in the pc_init1 code, then the initialization happens too
late, after command line processing.  So no watchdogs are registered
in time for parsing the -watchdog flag.

The patch below contains a different solution, just using '#ifdef
TARGET_I386' as a test instead of the custom -DHAS_WATCHDOGS flag.
This matches the conditional that adds the watchdog devices in
Makefile.target, so everything is good.

Everything else that you mentioned should be fixed in this patch.

It's also been rebased to the latest SVN, in particular the big
changes to command-line flags and documentation.

Rich.

-- 
Richard Jones, Emerging Technologies, Red Hat  http://et.redhat.com/~rjones
virt-df lists disk usage of guests without needing to install any
software inside the virtual machine.  Supports Linux and Windows.
http://et.redhat.com/~rjones/virt-df/

[-- Attachment #2: qemu-watchdog-6.patch --]
[-- Type: text/plain, Size: 31443 bytes --]

Index: Makefile.target
===================================================================
--- Makefile.target	(revision 6960)
+++ Makefile.target	(working copy)
@@ -582,6 +582,9 @@
 # Serial mouse
 OBJS += msmouse.o
 
+# Generic watchdog support
+OBJS += watchdog.o
+
 ifeq ($(TARGET_BASE_ARCH), i386)
 # Hardware support
 OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o
@@ -589,6 +592,7 @@
 OBJS+= cirrus_vga.o apic.o ioapic.o parallel.o acpi.o piix_pci.o
 OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o
 OBJS += device-hotplug.o pci-hotplug.o
+OBJS+= wdt_ib700.o wdt_i6300esb.o
 CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
 endif
 ifeq ($(TARGET_BASE_ARCH), ppc)
Index: vl.c
===================================================================
--- vl.c	(revision 6960)
+++ vl.c	(working copy)
@@ -138,6 +138,7 @@
 #include "hw/isa.h"
 #include "hw/baum.h"
 #include "hw/bt.h"
+#include "hw/watchdog.h"
 #include "bt-host.h"
 #include "net.h"
 #include "monitor.h"
@@ -246,6 +247,8 @@
 int cursor_hide = 1;
 int graphic_rotate = 0;
 int daemonize = 0;
+WatchdogTimerModel *watchdog = NULL;
+int watchdog_action = WDT_RESET;
 const char *option_rom[MAX_OPTION_ROMS];
 int nb_option_roms;
 int semihosting_enabled = 0;
@@ -4350,6 +4353,8 @@
     tb_size = 0;
     autostart= 1;
 
+    register_watchdogs ();
+
     optind = 1;
     for(;;) {
         if (optind >= argc)
@@ -4727,6 +4732,16 @@
                 serial_devices[serial_device_index] = optarg;
                 serial_device_index++;
                 break;
+            case QEMU_OPTION_watchdog:
+                i = select_watchdog (optarg);
+                if (i > 0) exit (i == 1 ? 1 : 0);
+                break;
+            case QEMU_OPTION_watchdog_action:
+                if (select_watchdog_action (optarg) == -1) {
+                    fprintf (stderr, "Unknown -watchdog-action parameter\n");
+                    exit (1);
+                }
+                break;
             case QEMU_OPTION_virtiocon:
                 if (virtio_console_index >= MAX_VIRTIO_CONSOLES) {
                     fprintf(stderr, "qemu: too many virtio consoles\n");
Index: qemu-options.hx
===================================================================
--- qemu-options.hx	(revision 6960)
+++ qemu-options.hx	(working copy)
@@ -1382,6 +1382,55 @@
 executed often has little or no correlation with actual performance.
 ETEXI
 
+DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \
+    "-watchdog i6300esb|ib700\n" \
+    "                enable virtual hardware watchdog [default=none]\n")
+STEXI
+@item -watchdog @var{model}
+Create a virtual hardware watchdog device.  Once enabled (by a guest
+action), the watchdog must be periodically polled by an agent inside
+the guest or else the guest will be restarted.
+
+The @var{model} is the model of hardware watchdog to emulate.  Choices
+for model are: @code{ib700} (iBASE 700) which is a very simple ISA
+watchdog with a single timer, or @code{i6300esb} (Intel 6300ESB I/O
+controller hub) which is a much more featureful PCI-based dual-timer
+watchdog.  Choose a model for which your guest has drivers.
+
+Use @code{-watchdog ?} to list available hardware models.  Only one
+watchdog can be enabled for a guest.
+ETEXI
+
+DEF("watchdog-action", HAS_ARG, QEMU_OPTION_watchdog_action, \
+    "-watchdog-action reset|shutdown|poweroff|pause|debug|none\n" \
+    "                action when watchdog fires [default=reset]\n")
+STEXI
+@item -watchdog-action @var{action}
+
+The @var{action} controls what QEMU will do when the watchdog timer
+expires.
+The default is
+@code{reset} (forcefully reset the guest).
+Other possible actions are:
+@code{shutdown} (attempt to gracefully shutdown the guest),
+@code{poweroff} (forcefully poweroff the guest),
+@code{pause} (pause the guest),
+@code{debug} (print a debug message and continue), or
+@code{none} (do nothing).
+
+Note that the @code{shutdown} action requires that the guest responds
+to ACPI signals, which it may not be able to do in the sort of
+situations where the watchdog would have expired, and thus
+@code{-watchdog-action shutdown} is not recommended for production use.
+
+Examples:
+
+@table @code
+@item -watchdog i6300esb -watchdog-action pause
+@item -watchdog ib700
+@end table
+ETEXI
+
 DEF("echr", HAS_ARG, QEMU_OPTION_echr, \
     "-echr chr       set terminal escape character instead of ctrl-a\n")
 STEXI
Index: monitor.c
===================================================================
--- monitor.c	(revision 6960)
+++ monitor.c	(working copy)
@@ -27,6 +27,7 @@
 #include "hw/pcmcia.h"
 #include "hw/pc.h"
 #include "hw/pci.h"
+#include "hw/watchdog.h"
 #include "gdbstub.h"
 #include "net.h"
 #include "qemu-char.h"
@@ -585,6 +586,13 @@
 }
 #endif
 
+static void do_watchdog_action (Monitor *mon, const char *action)
+{
+    if (select_watchdog_action (action) == -1) {
+        monitor_printf (mon, "Unknown watchdog action '%s'\n", action);
+    }
+}
+
 static void monitor_printc(Monitor *mon, int c)
 {
     monitor_printf(mon, "'");
@@ -1721,6 +1729,8 @@
       "target", "request VM to change it's memory allocation (in MB)" },
     { "set_link", "ss", do_set_link,
       "name [up|down]", "change the link status of a network adapter" },
+    { "watchdog_action", "s", do_watchdog_action,
+      "[reset|shutdown|poweroff|pause|debug|none]", "change watchdog action" },
     { "acl", "sss?i?", do_acl, "<command> <aclname> [<match>] [<index>]\n",
                                "acl show vnc.username\n"
                                "acl policy vnc.username deny\n"
Index: hw/watchdog.c
===================================================================
--- hw/watchdog.c	(revision 0)
+++ hw/watchdog.c	(revision 0)
@@ -0,0 +1,138 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#include "qemu-common.h"
+#include "sys-queue.h"
+#include "sysemu.h"
+#include "hw/watchdog.h"
+
+static LIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list;
+
+void watchdog_add_model(WatchdogTimerModel *model)
+{
+    LIST_INSERT_HEAD (&watchdog_list, model, entry);
+}
+
+/* Returns:
+ *   0 = continue
+ *   1 = exit program with error
+ *   2 = exit program without error
+ */
+int select_watchdog(const char *p)
+{
+    WatchdogTimerModel *model;
+
+    if (watchdog) {
+        fprintf (stderr,
+                 "qemu: only one watchdog option may be given\n");
+        return 1;
+    }
+
+    /* -watchdog ? lists available devices and exits cleanly. */
+    if (strcmp (p, "?") == 0) {
+        LIST_FOREACH (model, &watchdog_list, entry) {
+            fprintf (stderr, "\t%s\t%s\n",
+                     model->wdt_name, model->wdt_description);
+        }
+        return 2;
+    }
+
+    LIST_FOREACH (model, &watchdog_list, entry) {
+        if (strcasecmp (model->wdt_name, p) == 0) {
+            watchdog = model;
+            return 0;
+        }
+    }
+
+    fprintf (stderr, "Unknown -watchdog device. Supported devices are:\n");
+    LIST_FOREACH (model, &watchdog_list, entry) {
+        fprintf (stderr, "\t%s\t%s\n",
+                 model->wdt_name, model->wdt_description);
+    }
+    return 1;
+}
+
+int select_watchdog_action(const char *p)
+{
+    if (strcasecmp (p, "reset") == 0)
+        watchdog_action = WDT_RESET;
+    else if (strcasecmp (p, "shutdown") == 0)
+        watchdog_action = WDT_SHUTDOWN;
+    else if (strcasecmp (p, "poweroff") == 0)
+        watchdog_action = WDT_POWEROFF;
+    else if (strcasecmp (p, "pause") == 0)
+        watchdog_action = WDT_PAUSE;
+    else if (strcasecmp (p, "debug") == 0)
+        watchdog_action = WDT_DEBUG;
+    else if (strcasecmp (p, "none") == 0)
+        watchdog_action = WDT_NONE;
+    else
+        return -1;
+
+    return 0;
+}
+
+/* This actually performs the "action" once a watchdog has expired,
+ * ie. reboot, shutdown, exit, etc.
+ */
+void watchdog_perform_action(void)
+{
+    switch (watchdog_action) {
+    case WDT_RESET:             /* same as 'system_reset' in monitor */
+        qemu_system_reset_request ();
+        break;
+
+    case WDT_SHUTDOWN:          /* same as 'system_powerdown' in monitor */
+        qemu_system_powerdown_request ();
+        break;
+
+    case WDT_POWEROFF:          /* same as 'quit' command in monitor */
+        exit (0);
+        break;
+
+    case WDT_PAUSE:             /* same as 'stop' command in monitor */
+        vm_stop (0);
+        break;
+
+    case WDT_DEBUG:
+        fprintf (stderr, "watchdog: timer fired\n");
+        break;
+
+    case WDT_NONE:
+        break;
+    }
+}
+
+void watchdog_pc_init(PCIBus *pci_bus)
+{
+    if (watchdog)
+        watchdog->wdt_pc_init (pci_bus);
+}
+
+void register_watchdogs(void)
+{
+#ifdef TARGET_I386
+    wdt_ib700_init ();
+    wdt_i6300esb_init ();
+#endif
+}
Index: hw/watchdog.h
===================================================================
--- hw/watchdog.h	(revision 0)
+++ hw/watchdog.h	(revision 0)
@@ -0,0 +1,65 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#ifndef QEMU_WATCHDOG_H
+#define QEMU_WATCHDOG_H
+
+extern void wdt_i6300esb_init (void);
+extern void wdt_ib700_init (void);
+
+/* Possible values for action parameter. */
+#define WDT_RESET        1	/* Hard reset. */
+#define WDT_SHUTDOWN     2	/* Shutdown. */
+#define WDT_POWEROFF     3	/* Quit. */
+#define WDT_PAUSE        4	/* Pause. */
+#define WDT_DEBUG        5	/* Prints a message and continues running. */
+#define WDT_NONE         6	/* Do nothing. */
+
+struct WatchdogTimerModel {
+    LIST_ENTRY(WatchdogTimerModel) entry;
+
+    /* Short name of the device - used to select it on the command line. */
+    const char *wdt_name;
+    /* Longer description (eg. manufacturer and full model number). */
+    const char *wdt_description;
+
+    /* This callback should create/register the device.  It is called
+     * indirectly from hw/pc.c when the virtual PC is being set up.
+     */
+    void (*wdt_pc_init) (PCIBus *pci_bus);
+};
+typedef struct WatchdogTimerModel WatchdogTimerModel;
+
+/* in vl.c */
+extern WatchdogTimerModel *watchdog;
+extern int watchdog_action;
+
+/* in hw/watchdog.c */
+extern int select_watchdog(const char *p);
+extern int select_watchdog_action (const char *action);
+extern void watchdog_add_model (WatchdogTimerModel *model);
+extern void watchdog_perform_action (void);
+extern void watchdog_pc_init (PCIBus *pci_bus);
+extern void register_watchdogs (void);
+
+#endif /* QEMU_WATCHDOG_H */
Index: hw/wdt_i6300esb.c
===================================================================
--- hw/wdt_i6300esb.c	(revision 0)
+++ hw/wdt_i6300esb.c	(revision 0)
@@ -0,0 +1,468 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#include <inttypes.h>
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "watchdog.h"
+#include "hw.h"
+#include "isa.h"
+#include "pc.h"
+#include "pci.h"
+
+/*#define I6300ESB_DEBUG 1*/
+
+#ifdef I6300ESB_DEBUG
+#define i6300esb_debug(fs,...) \
+    fprintf(stderr,"i6300esb: %s: "fs,__func__,##__VA_ARGS__)
+#else
+#define i6300esb_debug(fs,...)
+#endif
+
+#ifndef PCI_DEVICE_ID_INTEL_ESB_9
+#define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab
+#endif
+
+/* PCI configuration registers */
+#define ESB_CONFIG_REG  0x60            /* Config register                   */
+#define ESB_LOCK_REG    0x68            /* WDT lock register                 */
+
+/* Memory mapped registers (offset from base address) */
+#define ESB_TIMER1_REG  0x00            /* Timer1 value after each reset     */
+#define ESB_TIMER2_REG  0x04            /* Timer2 value after each reset     */
+#define ESB_GINTSR_REG  0x08            /* General Interrupt Status Register */
+#define ESB_RELOAD_REG  0x0c            /* Reload register                   */
+
+/* Lock register bits */
+#define ESB_WDT_FUNC    (0x01 << 2)   /* Watchdog functionality            */
+#define ESB_WDT_ENABLE  (0x01 << 1)   /* Enable WDT                        */
+#define ESB_WDT_LOCK    (0x01 << 0)   /* Lock (nowayout)                   */
+
+/* Config register bits */
+#define ESB_WDT_REBOOT  (0x01 << 5)   /* Enable reboot on timeout          */
+#define ESB_WDT_FREQ    (0x01 << 2)   /* Decrement frequency               */
+#define ESB_WDT_INTTYPE (0x11 << 0)   /* Interrupt type on timer1 timeout  */
+
+/* Reload register bits */
+#define ESB_WDT_RELOAD  (0x01 << 8)    /* prevent timeout                   */
+
+/* Magic constants */
+#define ESB_UNLOCK1     0x80            /* Step 1 to unlock reset registers  */
+#define ESB_UNLOCK2     0x86            /* Step 2 to unlock reset registers  */
+
+/* Device state. */
+struct I6300State {
+    PCIDevice dev;              /* PCI device state, must be first field. */
+
+    int reboot_enabled;         /* "Reboot" on timer expiry.  The real action
+                                 * performed depends on the -watchdog-action
+                                 * param passed on QEMU command line.
+                                 */
+    int clock_scale;            /* Clock scale. */
+#define CLOCK_SCALE_1KHZ 0
+#define CLOCK_SCALE_1MHZ 1
+
+    int int_type;               /* Interrupt type generated. */
+#define INT_TYPE_IRQ 0          /* APIC 1, INT 10 */
+#define INT_TYPE_SMI 2
+#define INT_TYPE_DISABLED 3
+
+    int free_run;               /* If true, reload timer on expiry. */
+    int locked;                 /* If true, enabled field cannot be changed. */
+    int enabled;                /* If true, watchdog is enabled. */
+
+    QEMUTimer *timer;           /* The actual watchdog timer. */
+
+    uint32_t timer1_preload;    /* Values preloaded into timer1, timer2. */
+    uint32_t timer2_preload;
+    int stage;                  /* Stage (1 or 2). */
+
+    int unlock_state;           /* Guest writes 0x80, 0x86 to unlock the
+                                 * registers, and we transition through
+                                 * states 0 -> 1 -> 2 when this happens.
+                                 */
+
+    int previous_reboot_flag;   /* If the watchdog caused the previous
+                                 * reboot, this flag will be set.
+                                 */
+};
+
+typedef struct I6300State I6300State;
+
+/* This function is called when the watchdog has either been enabled
+ * (hence it starts counting down) or has been keep-alived.
+ */
+static void i6300esb_restart_timer(I6300State *d, int stage)
+{
+    int64_t timeout;
+
+    if (!d->enabled) return;
+
+    d->stage = stage;
+
+    if (d->stage <= 1)
+        timeout = d->timer1_preload;
+    else
+        timeout = d->timer2_preload;
+
+    if (d->clock_scale == CLOCK_SCALE_1KHZ)
+        timeout <<= 15;
+    else
+        timeout <<= 5;
+
+    /* Get the timeout in units of ticks_per_sec. */
+    timeout = ticks_per_sec * timeout / 33000000;
+
+    i6300esb_debug ("stage %d, timeout %" PRIi64 "\n", d->stage, timeout);
+
+    qemu_mod_timer (d->timer, qemu_get_clock (vm_clock) + timeout);
+}
+
+/* This is called when the guest disables the watchdog. */
+static void i6300esb_disable_timer(I6300State *d)
+{
+    i6300esb_debug ("timer disabled\n");
+
+    qemu_del_timer (d->timer);
+}
+
+static void i6300esb_reset(I6300State *d)
+{
+    /* XXX We should probably reset other parts of the state here,
+     * but we should also reset our state on general machine reset
+     * too.  For now just disable the timer so it doesn't fire
+     * again after the reboot.
+     */
+    i6300esb_disable_timer (d);
+}
+
+/* This function is called when the watchdog expires.  Note that
+ * the hardware has two timers, and so expiry happens in two stages.
+ * If d->stage == 1 then we perform the first stage action (usually,
+ * sending an interrupt) and then restart the timer again for the
+ * second stage.  If the second stage expires then the watchdog
+ * really has run out.
+ */
+static void i6300esb_timer_expired(void *vp)
+{
+    I6300State *d = (I6300State *) vp;
+
+    i6300esb_debug ("stage %d\n", d->stage);
+
+    if (d->stage == 1) {
+        /* What to do at the end of stage 1? */
+        switch (d->int_type) {
+        case INT_TYPE_IRQ:
+            fprintf (stderr, "i6300esb_timer_expired: I would send APIC 1 INT 10 here if I knew how (XXX)\n");
+            break;
+        case INT_TYPE_SMI:
+            fprintf (stderr, "i6300esb_timer_expired: I would send SMI here if I knew how (XXX)\n");
+            break;
+        }
+
+        /* Start the second stage. */
+        i6300esb_restart_timer (d, 2);
+    } else {
+        /* Second stage expired, reboot for real. */
+        if (d->reboot_enabled) {
+            d->previous_reboot_flag = 1;
+            watchdog_perform_action (); /* This reboots, exits, etc */
+            i6300esb_reset (d);
+        }
+
+        /* In "free running mode" we start stage 1 again. */
+        if (d->free_run)
+            i6300esb_restart_timer (d, 1);
+    }
+}
+
+static void i6300esb_config_write(PCIDevice *dev, uint32_t addr,
+				  uint32_t data, int len)
+{
+    I6300State *d = (I6300State *) dev;
+    int old;
+
+    i6300esb_debug ("addr = %x, data = %x, len = %d\n", addr, data, len);
+
+    if (addr == ESB_CONFIG_REG && len == 2) {
+        d->reboot_enabled = (data & ESB_WDT_REBOOT) == 0;
+        d->clock_scale =
+            (data & ESB_WDT_FREQ) != 0 ? CLOCK_SCALE_1MHZ : CLOCK_SCALE_1KHZ;
+        d->int_type = (data & ESB_WDT_INTTYPE);
+    } else if (addr == ESB_LOCK_REG && len == 1) {
+        if (!d->locked) {
+            d->locked = (data & ESB_WDT_LOCK) != 0;
+            d->free_run = (data & ESB_WDT_FUNC) != 0;
+            old = d->enabled;
+            d->enabled = (data & ESB_WDT_ENABLE) != 0;
+            if (!old && d->enabled) /* Enabled transitioned from 0 -> 1 */
+                i6300esb_restart_timer (d, 1);
+            else if (!d->enabled)
+                i6300esb_disable_timer (d);
+        }
+    } else {
+        pci_default_write_config (dev, addr, data, len);
+    }
+}
+
+static uint32_t i6300esb_config_read(PCIDevice *dev, uint32_t addr, int len)
+{
+    I6300State *d = (I6300State *) dev;
+    uint32_t data;
+
+    i6300esb_debug ("addr = %x, len = %d\n", addr, len);
+
+    if (addr == ESB_CONFIG_REG && len == 2) {
+        data =
+            (d->reboot_enabled ? 0 : ESB_WDT_REBOOT) |
+            (d->clock_scale == CLOCK_SCALE_1MHZ ? ESB_WDT_FREQ : 0) |
+            d->int_type;
+        return data;
+    } else if (addr == ESB_LOCK_REG && len == 1) {
+        data =
+            (d->free_run ? ESB_WDT_FUNC : 0) |
+            (d->locked ? ESB_WDT_LOCK : 0) |
+            (d->enabled ? ESB_WDT_ENABLE : 0);
+        return data;
+    } else {
+        return pci_default_read_config (dev, addr, len);
+    }
+}
+
+static uint32_t i6300esb_mem_readb(void *vp, target_phys_addr_t addr)
+{
+    i6300esb_debug ("addr = %x\n", (int) addr);
+
+    return 0;
+}
+
+static uint32_t i6300esb_mem_readw(void *vp, target_phys_addr_t addr)
+{
+    uint32_t data = 0;
+    I6300State *d = (I6300State *) vp;
+
+    i6300esb_debug ("addr = %x\n", (int) addr);
+
+    if (addr == 0xc) {
+        /* The previous reboot flag is really bit 9, but there is
+         * a bug in the Linux driver where it thinks it's bit 12.
+         * Set both.
+         */
+        data = d->previous_reboot_flag ? 0x1200 : 0;
+    }
+
+    return data;
+}
+
+static uint32_t i6300esb_mem_readl(void *vp, target_phys_addr_t addr)
+{
+    i6300esb_debug ("addr = %x\n", (int) addr);
+
+    return 0;
+}
+
+static void i6300esb_mem_writeb(void *vp, target_phys_addr_t addr, uint32_t val)
+{
+    I6300State *d = (I6300State *) vp;
+
+    i6300esb_debug ("addr = %x, val = %x\n", (int) addr, val);
+
+    if (addr == 0xc && val == 0x80)
+        d->unlock_state = 1;
+    else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
+        d->unlock_state = 2;
+}
+
+static void i6300esb_mem_writew(void *vp, target_phys_addr_t addr, uint32_t val)
+{
+    I6300State *d = (I6300State *) vp;
+
+    i6300esb_debug ("addr = %x, val = %x\n", (int) addr, val);
+
+    if (addr == 0xc && val == 0x80)
+        d->unlock_state = 1;
+    else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
+        d->unlock_state = 2;
+    else {
+        if (d->unlock_state == 2) {
+            if (addr == 0xc) {
+                if ((val & 0x100) != 0)
+                    /* This is the "ping" from the userspace watchdog in
+                     * the guest ...
+                     */
+                    i6300esb_restart_timer (d, 1);
+
+                /* Setting bit 9 resets the previous reboot flag.
+                 * There's a bug in the Linux driver where it sets
+                 * bit 12 instead.
+                 */
+                if ((val & 0x200) != 0 || (val & 0x1000) != 0) {
+                    d->previous_reboot_flag = 0;
+                }
+            }
+
+            d->unlock_state = 0;
+        }
+    }
+}
+
+static void i6300esb_mem_writel(void *vp, target_phys_addr_t addr, uint32_t val)
+{
+    I6300State *d = (I6300State *) vp;
+
+    i6300esb_debug ("addr = %x, val = %x\n", (int) addr, val);
+
+    if (addr == 0xc && val == 0x80)
+        d->unlock_state = 1;
+    else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
+        d->unlock_state = 2;
+    else {
+        if (d->unlock_state == 2) {
+            if (addr == 0)
+                d->timer1_preload = val & 0xfffff;
+            else if (addr == 4)
+                d->timer2_preload = val & 0xfffff;
+
+            d->unlock_state = 0;
+        }
+    }
+}
+
+static void i6300esb_map(PCIDevice *dev, int region_num,
+			 uint32_t addr, uint32_t size, int type)
+{
+    static CPUReadMemoryFunc *mem_read[3] = {
+        i6300esb_mem_readb,
+        i6300esb_mem_readw,
+        i6300esb_mem_readl,
+    };
+    static CPUWriteMemoryFunc *mem_write[3] = {
+        i6300esb_mem_writeb,
+        i6300esb_mem_writew,
+        i6300esb_mem_writel,
+    };
+    I6300State *d = (I6300State *) dev;
+    int io_mem;
+
+    i6300esb_debug ("addr = %x, size = %x, type = %d\n", addr, size, type);
+
+    io_mem = cpu_register_io_memory (0, mem_read, mem_write, d);
+    cpu_register_physical_memory (addr, 0x10, io_mem);
+    /* qemu_register_coalesced_mmio (addr, 0x10); ? */
+}
+
+static void i6300esb_save(QEMUFile *f, void *vp)
+{
+    I6300State *d = (I6300State *) vp;
+
+    pci_device_save (&d->dev, f);
+    qemu_put_be32 (f, d->reboot_enabled);
+    qemu_put_be32 (f, d->clock_scale);
+    qemu_put_be32 (f, d->int_type);
+    qemu_put_be32 (f, d->free_run);
+    qemu_put_be32 (f, d->locked);
+    qemu_put_be32 (f, d->enabled);
+    qemu_put_timer (f, d->timer);
+    qemu_put_be32 (f, d->timer1_preload);
+    qemu_put_be32 (f, d->timer2_preload);
+    qemu_put_be32 (f, d->stage);
+    qemu_put_be32 (f, d->unlock_state);
+    qemu_put_be32 (f, d->previous_reboot_flag);
+}
+
+static int i6300esb_load(QEMUFile *f, void *vp, int version)
+{
+    I6300State *d = (I6300State *) vp;
+
+    if (version != sizeof (I6300State)) return -EINVAL;
+
+    pci_device_load (&d->dev, f);
+    d->reboot_enabled = qemu_get_be32 (f);
+    d->clock_scale = qemu_get_be32 (f);
+    d->int_type = qemu_get_be32 (f);
+    d->free_run = qemu_get_be32 (f);
+    d->locked = qemu_get_be32 (f);
+    d->enabled = qemu_get_be32 (f);
+    qemu_get_timer (f, d->timer);
+    d->timer1_preload = qemu_get_be32 (f);
+    d->timer2_preload = qemu_get_be32 (f);
+    d->stage = qemu_get_be32 (f);
+    d->unlock_state = qemu_get_be32 (f);
+    d->previous_reboot_flag = qemu_get_be32 (f);
+
+    return 0;
+}
+
+/* Create and initialize a virtual Intel 6300ESB during PC creation. */
+static void i6300esb_pc_init(PCIBus *pci_bus)
+{
+    I6300State *d;
+    uint8_t *pci_conf;
+
+    if (!pci_bus) {
+        fprintf (stderr, "wdt_i6300esb: no PCI bus in this machine\n");
+        return;
+    }
+
+    d = (I6300State *)
+        pci_register_device (pci_bus, "i6300esb_wdt", sizeof (I6300State),
+                             -1,
+                             i6300esb_config_read, i6300esb_config_write);
+
+    d->reboot_enabled = 1;
+    d->clock_scale = CLOCK_SCALE_1KHZ;
+    d->int_type = INT_TYPE_IRQ;
+    d->free_run = 0;
+    d->locked = 0;
+    d->enabled = 0;
+    d->timer = qemu_new_timer (vm_clock, i6300esb_timer_expired, d);
+    d->timer1_preload = 0xfffff;
+    d->timer2_preload = 0xfffff;
+    d->stage = 1;
+    d->unlock_state = 0;
+    d->previous_reboot_flag = 0;
+
+    pci_conf = d->dev.config;
+    pci_config_set_vendor_id (pci_conf, PCI_VENDOR_ID_INTEL);
+    pci_config_set_device_id (pci_conf, PCI_DEVICE_ID_INTEL_ESB_9);
+    pci_config_set_class (pci_conf, PCI_CLASS_SYSTEM_OTHER);
+    pci_conf[0x0e] = 0x00;
+
+    pci_register_io_region (&d->dev, 0, 0x10,
+                            PCI_ADDRESS_SPACE_MEM, i6300esb_map);
+
+    register_savevm ("i6300esb_wdt", -1, sizeof (I6300State),
+                     i6300esb_save, i6300esb_load, d);
+}
+
+static WatchdogTimerModel model = {
+    .wdt_name = "i6300esb",
+    .wdt_description = "Intel 6300ESB",
+    .wdt_pc_init = i6300esb_pc_init,
+};
+
+void wdt_i6300esb_init(void)
+{
+    watchdog_add_model (&model);
+}
Index: hw/wdt_ib700.c
===================================================================
--- hw/wdt_ib700.c	(revision 0)
+++ hw/wdt_ib700.c	(revision 0)
@@ -0,0 +1,111 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "watchdog.h"
+#include "hw.h"
+#include "isa.h"
+#include "pc.h"
+
+/*#define IB700_DEBUG 1*/
+
+#ifdef IB700_DEBUG
+#define ib700_debug(fs,...)					\
+    fprintf(stderr,"ib700: %s: "fs,__func__,##__VA_ARGS__)
+#else
+#define ib700_debug(fs,...)
+#endif
+
+/* This is the timer.  We use a global here because the watchdog
+ * code ensures there is only one watchdog (it is located at a fixed,
+ * unchangable IO port, so there could only ever be one anyway).
+ */
+static QEMUTimer *timer = NULL;
+
+/* A write to this register enables the timer. */
+static void ib700_write_enable_reg(void *vp, uint32_t addr, uint32_t data)
+{
+    static int time_map[] = {
+        30, 28, 26, 24, 22, 20, 18, 16,
+        14, 12, 10,  8,  6,  4,  2,  0
+    };
+    int64 timeout;
+
+    ib700_debug ("addr = %x, data = %x\n", addr, data);
+
+    timeout = (int64_t) time_map[data & 0xF] * ticks_per_sec;
+    qemu_mod_timer (timer, qemu_get_clock (vm_clock) + timeout);
+}
+
+/* A write (of any value) to this register disables the timer. */
+static void ib700_write_disable_reg(void *vp, uint32_t addr, uint32_t data)
+{
+    ib700_debug ("addr = %x, data = %x\n", addr, data);
+
+    qemu_del_timer (timer);
+}
+
+/* This is called when the watchdog expires. */
+static void ib700_timer_expired(void *vp)
+{
+    ib700_debug ("watchdog expired\n");
+
+    watchdog_perform_action ();
+    qemu_del_timer (timer);
+}
+
+static void ib700_save(QEMUFile *f, void *vp)
+{
+    qemu_put_timer (f, timer);
+}
+
+static int ib700_load(QEMUFile *f, void *vp, int version)
+{
+    if (version != 0) return -EINVAL;
+
+    qemu_get_timer (f, timer);
+
+    return 0;
+}
+
+/* Create and initialize a virtual IB700 during PC creation. */
+static void ib700_pc_init(PCIBus *unused)
+{
+    register_savevm ("ib700_wdt", -1, 0, ib700_save, ib700_load, NULL);
+
+    register_ioport_write (0x441, 2, 1, ib700_write_disable_reg, NULL);
+    register_ioport_write (0x443, 2, 1, ib700_write_enable_reg, NULL);
+}
+
+static WatchdogTimerModel model = {
+    .wdt_name = "ib700",
+    .wdt_description = "iBASE 700",
+    .wdt_pc_init = ib700_pc_init,
+};
+
+void wdt_ib700_init(void)
+{
+    watchdog_add_model (&model);
+    timer = qemu_new_timer (vm_clock, ib700_timer_expired, NULL);
+}
Index: hw/pc.c
===================================================================
--- hw/pc.c	(revision 6960)
+++ hw/pc.c	(working copy)
@@ -37,6 +37,7 @@
 #include "virtio-balloon.h"
 #include "virtio-console.h"
 #include "hpet_emul.h"
+#include "watchdog.h"
 
 /* output Bochs bios info messages */
 //#define DEBUG_BIOS
@@ -1014,6 +1015,8 @@
         }
     }
 
+    watchdog_pc_init (pci_bus);
+
     for(i = 0; i < nb_nics; i++) {
         NICInfo *nd = &nd_table[i];
 

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] Hardware watchdog patch, version 6 (was: Re: [Qemu-devel] [PATCH] Hardware watchdog patch, version 5)
  2009-03-31 11:23   ` [PATCH] Hardware watchdog patch, version 6 (was: Re: [Qemu-devel] [PATCH] Hardware watchdog patch, version 5) Richard W.M. Jones
@ 2009-03-31 11:24     ` Richard W.M. Jones
  2009-03-31 16:07     ` Blue Swirl
  1 sibling, 0 replies; 14+ messages in thread
From: Richard W.M. Jones @ 2009-03-31 11:24 UTC (permalink / raw)
  To: qemu-devel

Ooops, managed to forget to add the Signed-off-by line that time.

  Signed-off-by: Richard W.M. Jones <rjones@redhat.com>

Rich.

-- 
Richard Jones, Emerging Technologies, Red Hat  http://et.redhat.com/~rjones
virt-p2v converts physical machines to virtual machines.  Boot with a
live CD or over the network (PXE) and turn machines into Xen guests.
http://et.redhat.com/~rjones/virt-p2v

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] Hardware watchdog patch, version 6 (was: Re: [Qemu-devel] [PATCH] Hardware watchdog patch, version 5)
  2009-03-31 11:23   ` [PATCH] Hardware watchdog patch, version 6 (was: Re: [Qemu-devel] [PATCH] Hardware watchdog patch, version 5) Richard W.M. Jones
  2009-03-31 11:24     ` Richard W.M. Jones
@ 2009-03-31 16:07     ` Blue Swirl
  2009-04-01  8:46       ` Richard W.M. Jones
  1 sibling, 1 reply; 14+ messages in thread
From: Blue Swirl @ 2009-03-31 16:07 UTC (permalink / raw)
  To: qemu-devel

On 3/31/09, Richard W.M. Jones <rjones@redhat.com> wrote:
> On Thu, Mar 26, 2009 at 08:03:47AM -0500, Anthony Liguori wrote:
>  > Richard W.M. Jones wrote:
>  >> -CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
>  >> +OBJS+= wdt_ib700.o wdt_i6300esb.o
>  >> +CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE -DHAS_WATCHDOGS
>  >
>  > I don't think this is the right way to address this problem as it isn't
>  > likely to scale as we add additional devices.  I'd suggest either
>  > registering the watch dogs in the pc init function or better yet, as
>  > part of PCI/ISA initialization.  Hopefully this will all get a lot
>  > cleaner as Markus' device config patches get included but for now, let's
>  > try to at least avoid using CFLAGS to solve this.
>
>  I wasn't sure how to do this 'correctly'.  If I initialize the
>  watchdog in the pc_init1 code, then the initialization happens too
>  late, after command line processing.  So no watchdogs are registered
>  in time for parsing the -watchdog flag.
>
>  The patch below contains a different solution, just using '#ifdef
>  TARGET_I386' as a test instead of the custom -DHAS_WATCHDOGS flag.
>  This matches the conditional that adds the watchdog devices in
>  Makefile.target, so everything is good.
>
>  Everything else that you mentioned should be fixed in this patch.
>
>  It's also been rebased to the latest SVN, in particular the big
>  changes to command-line flags and documentation.

+                if (i > 0) exit (i == 1 ? 1 : 0);
+    if (!d->enabled) return;

Please split these into two lines.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] Hardware watchdog patch, version 6 (was: Re: [Qemu-devel] [PATCH] Hardware watchdog patch, version 5)
  2009-03-31 16:07     ` Blue Swirl
@ 2009-04-01  8:46       ` Richard W.M. Jones
  2009-04-01 13:18         ` [Qemu-devel] Re: [PATCH] Hardware watchdog patch, version 6 Anthony Liguori
  2009-04-07 21:19         ` [PATCH] Hardware watchdog patch, version 6 (was: Re: [Qemu-devel] [PATCH] Hardware watchdog patch, version 5) Hollis Blanchard
  0 siblings, 2 replies; 14+ messages in thread
From: Richard W.M. Jones @ 2009-04-01  8:46 UTC (permalink / raw)
  To: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 579 bytes --]

On Tue, Mar 31, 2009 at 07:07:39PM +0300, Blue Swirl wrote:
> +                if (i > 0) exit (i == 1 ? 1 : 0);
> +    if (!d->enabled) return;
> 
> Please split these into two lines.

Version 7 of the patch is attached:

 - Fixed the 'if' statements above, plus a couple more I found.

 - Rebased to latest SVN.

Rich.

-- 
Richard Jones, Emerging Technologies, Red Hat  http://et.redhat.com/~rjones
virt-p2v converts physical machines to virtual machines.  Boot with a
live CD or over the network (PXE) and turn machines into Xen guests.
http://et.redhat.com/~rjones/virt-p2v

[-- Attachment #2: qemu-watchdog-7.patch --]
[-- Type: text/plain, Size: 31540 bytes --]

Index: Makefile.target
===================================================================
--- Makefile.target	(revision 6963)
+++ Makefile.target	(working copy)
@@ -582,6 +582,9 @@
 # Serial mouse
 OBJS += msmouse.o
 
+# Generic watchdog support
+OBJS += watchdog.o
+
 ifeq ($(TARGET_BASE_ARCH), i386)
 # Hardware support
 OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o
@@ -589,6 +592,7 @@
 OBJS+= cirrus_vga.o apic.o ioapic.o parallel.o acpi.o piix_pci.o
 OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o
 OBJS += device-hotplug.o pci-hotplug.o
+OBJS+= wdt_ib700.o wdt_i6300esb.o
 CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
 endif
 ifeq ($(TARGET_BASE_ARCH), ppc)
Index: vl.c
===================================================================
--- vl.c	(revision 6963)
+++ vl.c	(working copy)
@@ -138,6 +138,7 @@
 #include "hw/isa.h"
 #include "hw/baum.h"
 #include "hw/bt.h"
+#include "hw/watchdog.h"
 #include "bt-host.h"
 #include "net.h"
 #include "monitor.h"
@@ -246,6 +247,8 @@
 int cursor_hide = 1;
 int graphic_rotate = 0;
 int daemonize = 0;
+WatchdogTimerModel *watchdog = NULL;
+int watchdog_action = WDT_RESET;
 const char *option_rom[MAX_OPTION_ROMS];
 int nb_option_roms;
 int semihosting_enabled = 0;
@@ -4350,6 +4353,8 @@
     tb_size = 0;
     autostart= 1;
 
+    register_watchdogs ();
+
     optind = 1;
     for(;;) {
         if (optind >= argc)
@@ -4727,6 +4732,17 @@
                 serial_devices[serial_device_index] = optarg;
                 serial_device_index++;
                 break;
+            case QEMU_OPTION_watchdog:
+                i = select_watchdog (optarg);
+                if (i > 0)
+                    exit (i == 1 ? 1 : 0);
+                break;
+            case QEMU_OPTION_watchdog_action:
+                if (select_watchdog_action (optarg) == -1) {
+                    fprintf (stderr, "Unknown -watchdog-action parameter\n");
+                    exit (1);
+                }
+                break;
             case QEMU_OPTION_virtiocon:
                 if (virtio_console_index >= MAX_VIRTIO_CONSOLES) {
                     fprintf(stderr, "qemu: too many virtio consoles\n");
Index: qemu-options.hx
===================================================================
--- qemu-options.hx	(revision 6963)
+++ qemu-options.hx	(working copy)
@@ -1382,6 +1382,55 @@
 executed often has little or no correlation with actual performance.
 ETEXI
 
+DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \
+    "-watchdog i6300esb|ib700\n" \
+    "                enable virtual hardware watchdog [default=none]\n")
+STEXI
+@item -watchdog @var{model}
+Create a virtual hardware watchdog device.  Once enabled (by a guest
+action), the watchdog must be periodically polled by an agent inside
+the guest or else the guest will be restarted.
+
+The @var{model} is the model of hardware watchdog to emulate.  Choices
+for model are: @code{ib700} (iBASE 700) which is a very simple ISA
+watchdog with a single timer, or @code{i6300esb} (Intel 6300ESB I/O
+controller hub) which is a much more featureful PCI-based dual-timer
+watchdog.  Choose a model for which your guest has drivers.
+
+Use @code{-watchdog ?} to list available hardware models.  Only one
+watchdog can be enabled for a guest.
+ETEXI
+
+DEF("watchdog-action", HAS_ARG, QEMU_OPTION_watchdog_action, \
+    "-watchdog-action reset|shutdown|poweroff|pause|debug|none\n" \
+    "                action when watchdog fires [default=reset]\n")
+STEXI
+@item -watchdog-action @var{action}
+
+The @var{action} controls what QEMU will do when the watchdog timer
+expires.
+The default is
+@code{reset} (forcefully reset the guest).
+Other possible actions are:
+@code{shutdown} (attempt to gracefully shutdown the guest),
+@code{poweroff} (forcefully poweroff the guest),
+@code{pause} (pause the guest),
+@code{debug} (print a debug message and continue), or
+@code{none} (do nothing).
+
+Note that the @code{shutdown} action requires that the guest responds
+to ACPI signals, which it may not be able to do in the sort of
+situations where the watchdog would have expired, and thus
+@code{-watchdog-action shutdown} is not recommended for production use.
+
+Examples:
+
+@table @code
+@item -watchdog i6300esb -watchdog-action pause
+@item -watchdog ib700
+@end table
+ETEXI
+
 DEF("echr", HAS_ARG, QEMU_OPTION_echr, \
     "-echr chr       set terminal escape character instead of ctrl-a\n")
 STEXI
Index: monitor.c
===================================================================
--- monitor.c	(revision 6963)
+++ monitor.c	(working copy)
@@ -27,6 +27,7 @@
 #include "hw/pcmcia.h"
 #include "hw/pc.h"
 #include "hw/pci.h"
+#include "hw/watchdog.h"
 #include "gdbstub.h"
 #include "net.h"
 #include "qemu-char.h"
@@ -585,6 +586,13 @@
 }
 #endif
 
+static void do_watchdog_action (Monitor *mon, const char *action)
+{
+    if (select_watchdog_action (action) == -1) {
+        monitor_printf (mon, "Unknown watchdog action '%s'\n", action);
+    }
+}
+
 static void monitor_printc(Monitor *mon, int c)
 {
     monitor_printf(mon, "'");
@@ -1721,6 +1729,8 @@
       "target", "request VM to change it's memory allocation (in MB)" },
     { "set_link", "ss", do_set_link,
       "name [up|down]", "change the link status of a network adapter" },
+    { "watchdog_action", "s", do_watchdog_action,
+      "[reset|shutdown|poweroff|pause|debug|none]", "change watchdog action" },
     { "acl", "sss?i?", do_acl, "<command> <aclname> [<match>] [<index>]\n",
                                "acl show vnc.username\n"
                                "acl policy vnc.username deny\n"
Index: hw/watchdog.c
===================================================================
--- hw/watchdog.c	(revision 0)
+++ hw/watchdog.c	(revision 0)
@@ -0,0 +1,138 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#include "qemu-common.h"
+#include "sys-queue.h"
+#include "sysemu.h"
+#include "hw/watchdog.h"
+
+static LIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list;
+
+void watchdog_add_model(WatchdogTimerModel *model)
+{
+    LIST_INSERT_HEAD (&watchdog_list, model, entry);
+}
+
+/* Returns:
+ *   0 = continue
+ *   1 = exit program with error
+ *   2 = exit program without error
+ */
+int select_watchdog(const char *p)
+{
+    WatchdogTimerModel *model;
+
+    if (watchdog) {
+        fprintf (stderr,
+                 "qemu: only one watchdog option may be given\n");
+        return 1;
+    }
+
+    /* -watchdog ? lists available devices and exits cleanly. */
+    if (strcmp (p, "?") == 0) {
+        LIST_FOREACH (model, &watchdog_list, entry) {
+            fprintf (stderr, "\t%s\t%s\n",
+                     model->wdt_name, model->wdt_description);
+        }
+        return 2;
+    }
+
+    LIST_FOREACH (model, &watchdog_list, entry) {
+        if (strcasecmp (model->wdt_name, p) == 0) {
+            watchdog = model;
+            return 0;
+        }
+    }
+
+    fprintf (stderr, "Unknown -watchdog device. Supported devices are:\n");
+    LIST_FOREACH (model, &watchdog_list, entry) {
+        fprintf (stderr, "\t%s\t%s\n",
+                 model->wdt_name, model->wdt_description);
+    }
+    return 1;
+}
+
+int select_watchdog_action(const char *p)
+{
+    if (strcasecmp (p, "reset") == 0)
+        watchdog_action = WDT_RESET;
+    else if (strcasecmp (p, "shutdown") == 0)
+        watchdog_action = WDT_SHUTDOWN;
+    else if (strcasecmp (p, "poweroff") == 0)
+        watchdog_action = WDT_POWEROFF;
+    else if (strcasecmp (p, "pause") == 0)
+        watchdog_action = WDT_PAUSE;
+    else if (strcasecmp (p, "debug") == 0)
+        watchdog_action = WDT_DEBUG;
+    else if (strcasecmp (p, "none") == 0)
+        watchdog_action = WDT_NONE;
+    else
+        return -1;
+
+    return 0;
+}
+
+/* This actually performs the "action" once a watchdog has expired,
+ * ie. reboot, shutdown, exit, etc.
+ */
+void watchdog_perform_action(void)
+{
+    switch (watchdog_action) {
+    case WDT_RESET:             /* same as 'system_reset' in monitor */
+        qemu_system_reset_request ();
+        break;
+
+    case WDT_SHUTDOWN:          /* same as 'system_powerdown' in monitor */
+        qemu_system_powerdown_request ();
+        break;
+
+    case WDT_POWEROFF:          /* same as 'quit' command in monitor */
+        exit (0);
+        break;
+
+    case WDT_PAUSE:             /* same as 'stop' command in monitor */
+        vm_stop (0);
+        break;
+
+    case WDT_DEBUG:
+        fprintf (stderr, "watchdog: timer fired\n");
+        break;
+
+    case WDT_NONE:
+        break;
+    }
+}
+
+void watchdog_pc_init(PCIBus *pci_bus)
+{
+    if (watchdog)
+        watchdog->wdt_pc_init (pci_bus);
+}
+
+void register_watchdogs(void)
+{
+#ifdef TARGET_I386
+    wdt_ib700_init ();
+    wdt_i6300esb_init ();
+#endif
+}
Index: hw/watchdog.h
===================================================================
--- hw/watchdog.h	(revision 0)
+++ hw/watchdog.h	(revision 0)
@@ -0,0 +1,65 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#ifndef QEMU_WATCHDOG_H
+#define QEMU_WATCHDOG_H
+
+extern void wdt_i6300esb_init (void);
+extern void wdt_ib700_init (void);
+
+/* Possible values for action parameter. */
+#define WDT_RESET        1	/* Hard reset. */
+#define WDT_SHUTDOWN     2	/* Shutdown. */
+#define WDT_POWEROFF     3	/* Quit. */
+#define WDT_PAUSE        4	/* Pause. */
+#define WDT_DEBUG        5	/* Prints a message and continues running. */
+#define WDT_NONE         6	/* Do nothing. */
+
+struct WatchdogTimerModel {
+    LIST_ENTRY(WatchdogTimerModel) entry;
+
+    /* Short name of the device - used to select it on the command line. */
+    const char *wdt_name;
+    /* Longer description (eg. manufacturer and full model number). */
+    const char *wdt_description;
+
+    /* This callback should create/register the device.  It is called
+     * indirectly from hw/pc.c when the virtual PC is being set up.
+     */
+    void (*wdt_pc_init) (PCIBus *pci_bus);
+};
+typedef struct WatchdogTimerModel WatchdogTimerModel;
+
+/* in vl.c */
+extern WatchdogTimerModel *watchdog;
+extern int watchdog_action;
+
+/* in hw/watchdog.c */
+extern int select_watchdog(const char *p);
+extern int select_watchdog_action (const char *action);
+extern void watchdog_add_model (WatchdogTimerModel *model);
+extern void watchdog_perform_action (void);
+extern void watchdog_pc_init (PCIBus *pci_bus);
+extern void register_watchdogs (void);
+
+#endif /* QEMU_WATCHDOG_H */
Index: hw/wdt_i6300esb.c
===================================================================
--- hw/wdt_i6300esb.c	(revision 0)
+++ hw/wdt_i6300esb.c	(revision 0)
@@ -0,0 +1,470 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#include <inttypes.h>
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "watchdog.h"
+#include "hw.h"
+#include "isa.h"
+#include "pc.h"
+#include "pci.h"
+
+/*#define I6300ESB_DEBUG 1*/
+
+#ifdef I6300ESB_DEBUG
+#define i6300esb_debug(fs,...) \
+    fprintf(stderr,"i6300esb: %s: "fs,__func__,##__VA_ARGS__)
+#else
+#define i6300esb_debug(fs,...)
+#endif
+
+#ifndef PCI_DEVICE_ID_INTEL_ESB_9
+#define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab
+#endif
+
+/* PCI configuration registers */
+#define ESB_CONFIG_REG  0x60            /* Config register                   */
+#define ESB_LOCK_REG    0x68            /* WDT lock register                 */
+
+/* Memory mapped registers (offset from base address) */
+#define ESB_TIMER1_REG  0x00            /* Timer1 value after each reset     */
+#define ESB_TIMER2_REG  0x04            /* Timer2 value after each reset     */
+#define ESB_GINTSR_REG  0x08            /* General Interrupt Status Register */
+#define ESB_RELOAD_REG  0x0c            /* Reload register                   */
+
+/* Lock register bits */
+#define ESB_WDT_FUNC    (0x01 << 2)   /* Watchdog functionality            */
+#define ESB_WDT_ENABLE  (0x01 << 1)   /* Enable WDT                        */
+#define ESB_WDT_LOCK    (0x01 << 0)   /* Lock (nowayout)                   */
+
+/* Config register bits */
+#define ESB_WDT_REBOOT  (0x01 << 5)   /* Enable reboot on timeout          */
+#define ESB_WDT_FREQ    (0x01 << 2)   /* Decrement frequency               */
+#define ESB_WDT_INTTYPE (0x11 << 0)   /* Interrupt type on timer1 timeout  */
+
+/* Reload register bits */
+#define ESB_WDT_RELOAD  (0x01 << 8)    /* prevent timeout                   */
+
+/* Magic constants */
+#define ESB_UNLOCK1     0x80            /* Step 1 to unlock reset registers  */
+#define ESB_UNLOCK2     0x86            /* Step 2 to unlock reset registers  */
+
+/* Device state. */
+struct I6300State {
+    PCIDevice dev;              /* PCI device state, must be first field. */
+
+    int reboot_enabled;         /* "Reboot" on timer expiry.  The real action
+                                 * performed depends on the -watchdog-action
+                                 * param passed on QEMU command line.
+                                 */
+    int clock_scale;            /* Clock scale. */
+#define CLOCK_SCALE_1KHZ 0
+#define CLOCK_SCALE_1MHZ 1
+
+    int int_type;               /* Interrupt type generated. */
+#define INT_TYPE_IRQ 0          /* APIC 1, INT 10 */
+#define INT_TYPE_SMI 2
+#define INT_TYPE_DISABLED 3
+
+    int free_run;               /* If true, reload timer on expiry. */
+    int locked;                 /* If true, enabled field cannot be changed. */
+    int enabled;                /* If true, watchdog is enabled. */
+
+    QEMUTimer *timer;           /* The actual watchdog timer. */
+
+    uint32_t timer1_preload;    /* Values preloaded into timer1, timer2. */
+    uint32_t timer2_preload;
+    int stage;                  /* Stage (1 or 2). */
+
+    int unlock_state;           /* Guest writes 0x80, 0x86 to unlock the
+                                 * registers, and we transition through
+                                 * states 0 -> 1 -> 2 when this happens.
+                                 */
+
+    int previous_reboot_flag;   /* If the watchdog caused the previous
+                                 * reboot, this flag will be set.
+                                 */
+};
+
+typedef struct I6300State I6300State;
+
+/* This function is called when the watchdog has either been enabled
+ * (hence it starts counting down) or has been keep-alived.
+ */
+static void i6300esb_restart_timer(I6300State *d, int stage)
+{
+    int64_t timeout;
+
+    if (!d->enabled)
+        return;
+
+    d->stage = stage;
+
+    if (d->stage <= 1)
+        timeout = d->timer1_preload;
+    else
+        timeout = d->timer2_preload;
+
+    if (d->clock_scale == CLOCK_SCALE_1KHZ)
+        timeout <<= 15;
+    else
+        timeout <<= 5;
+
+    /* Get the timeout in units of ticks_per_sec. */
+    timeout = ticks_per_sec * timeout / 33000000;
+
+    i6300esb_debug ("stage %d, timeout %" PRIi64 "\n", d->stage, timeout);
+
+    qemu_mod_timer (d->timer, qemu_get_clock (vm_clock) + timeout);
+}
+
+/* This is called when the guest disables the watchdog. */
+static void i6300esb_disable_timer(I6300State *d)
+{
+    i6300esb_debug ("timer disabled\n");
+
+    qemu_del_timer (d->timer);
+}
+
+static void i6300esb_reset(I6300State *d)
+{
+    /* XXX We should probably reset other parts of the state here,
+     * but we should also reset our state on general machine reset
+     * too.  For now just disable the timer so it doesn't fire
+     * again after the reboot.
+     */
+    i6300esb_disable_timer (d);
+}
+
+/* This function is called when the watchdog expires.  Note that
+ * the hardware has two timers, and so expiry happens in two stages.
+ * If d->stage == 1 then we perform the first stage action (usually,
+ * sending an interrupt) and then restart the timer again for the
+ * second stage.  If the second stage expires then the watchdog
+ * really has run out.
+ */
+static void i6300esb_timer_expired(void *vp)
+{
+    I6300State *d = (I6300State *) vp;
+
+    i6300esb_debug ("stage %d\n", d->stage);
+
+    if (d->stage == 1) {
+        /* What to do at the end of stage 1? */
+        switch (d->int_type) {
+        case INT_TYPE_IRQ:
+            fprintf (stderr, "i6300esb_timer_expired: I would send APIC 1 INT 10 here if I knew how (XXX)\n");
+            break;
+        case INT_TYPE_SMI:
+            fprintf (stderr, "i6300esb_timer_expired: I would send SMI here if I knew how (XXX)\n");
+            break;
+        }
+
+        /* Start the second stage. */
+        i6300esb_restart_timer (d, 2);
+    } else {
+        /* Second stage expired, reboot for real. */
+        if (d->reboot_enabled) {
+            d->previous_reboot_flag = 1;
+            watchdog_perform_action (); /* This reboots, exits, etc */
+            i6300esb_reset (d);
+        }
+
+        /* In "free running mode" we start stage 1 again. */
+        if (d->free_run)
+            i6300esb_restart_timer (d, 1);
+    }
+}
+
+static void i6300esb_config_write(PCIDevice *dev, uint32_t addr,
+                                  uint32_t data, int len)
+{
+    I6300State *d = (I6300State *) dev;
+    int old;
+
+    i6300esb_debug ("addr = %x, data = %x, len = %d\n", addr, data, len);
+
+    if (addr == ESB_CONFIG_REG && len == 2) {
+        d->reboot_enabled = (data & ESB_WDT_REBOOT) == 0;
+        d->clock_scale =
+            (data & ESB_WDT_FREQ) != 0 ? CLOCK_SCALE_1MHZ : CLOCK_SCALE_1KHZ;
+        d->int_type = (data & ESB_WDT_INTTYPE);
+    } else if (addr == ESB_LOCK_REG && len == 1) {
+        if (!d->locked) {
+            d->locked = (data & ESB_WDT_LOCK) != 0;
+            d->free_run = (data & ESB_WDT_FUNC) != 0;
+            old = d->enabled;
+            d->enabled = (data & ESB_WDT_ENABLE) != 0;
+            if (!old && d->enabled) /* Enabled transitioned from 0 -> 1 */
+                i6300esb_restart_timer (d, 1);
+            else if (!d->enabled)
+                i6300esb_disable_timer (d);
+        }
+    } else {
+        pci_default_write_config (dev, addr, data, len);
+    }
+}
+
+static uint32_t i6300esb_config_read(PCIDevice *dev, uint32_t addr, int len)
+{
+    I6300State *d = (I6300State *) dev;
+    uint32_t data;
+
+    i6300esb_debug ("addr = %x, len = %d\n", addr, len);
+
+    if (addr == ESB_CONFIG_REG && len == 2) {
+        data =
+            (d->reboot_enabled ? 0 : ESB_WDT_REBOOT) |
+            (d->clock_scale == CLOCK_SCALE_1MHZ ? ESB_WDT_FREQ : 0) |
+            d->int_type;
+        return data;
+    } else if (addr == ESB_LOCK_REG && len == 1) {
+        data =
+            (d->free_run ? ESB_WDT_FUNC : 0) |
+            (d->locked ? ESB_WDT_LOCK : 0) |
+            (d->enabled ? ESB_WDT_ENABLE : 0);
+        return data;
+    } else {
+        return pci_default_read_config (dev, addr, len);
+    }
+}
+
+static uint32_t i6300esb_mem_readb(void *vp, target_phys_addr_t addr)
+{
+    i6300esb_debug ("addr = %x\n", (int) addr);
+
+    return 0;
+}
+
+static uint32_t i6300esb_mem_readw(void *vp, target_phys_addr_t addr)
+{
+    uint32_t data = 0;
+    I6300State *d = (I6300State *) vp;
+
+    i6300esb_debug ("addr = %x\n", (int) addr);
+
+    if (addr == 0xc) {
+        /* The previous reboot flag is really bit 9, but there is
+         * a bug in the Linux driver where it thinks it's bit 12.
+         * Set both.
+         */
+        data = d->previous_reboot_flag ? 0x1200 : 0;
+    }
+
+    return data;
+}
+
+static uint32_t i6300esb_mem_readl(void *vp, target_phys_addr_t addr)
+{
+    i6300esb_debug ("addr = %x\n", (int) addr);
+
+    return 0;
+}
+
+static void i6300esb_mem_writeb(void *vp, target_phys_addr_t addr, uint32_t val)
+{
+    I6300State *d = (I6300State *) vp;
+
+    i6300esb_debug ("addr = %x, val = %x\n", (int) addr, val);
+
+    if (addr == 0xc && val == 0x80)
+        d->unlock_state = 1;
+    else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
+        d->unlock_state = 2;
+}
+
+static void i6300esb_mem_writew(void *vp, target_phys_addr_t addr, uint32_t val)
+{
+    I6300State *d = (I6300State *) vp;
+
+    i6300esb_debug ("addr = %x, val = %x\n", (int) addr, val);
+
+    if (addr == 0xc && val == 0x80)
+        d->unlock_state = 1;
+    else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
+        d->unlock_state = 2;
+    else {
+        if (d->unlock_state == 2) {
+            if (addr == 0xc) {
+                if ((val & 0x100) != 0)
+                    /* This is the "ping" from the userspace watchdog in
+                     * the guest ...
+                     */
+                    i6300esb_restart_timer (d, 1);
+
+                /* Setting bit 9 resets the previous reboot flag.
+                 * There's a bug in the Linux driver where it sets
+                 * bit 12 instead.
+                 */
+                if ((val & 0x200) != 0 || (val & 0x1000) != 0) {
+                    d->previous_reboot_flag = 0;
+                }
+            }
+
+            d->unlock_state = 0;
+        }
+    }
+}
+
+static void i6300esb_mem_writel(void *vp, target_phys_addr_t addr, uint32_t val)
+{
+    I6300State *d = (I6300State *) vp;
+
+    i6300esb_debug ("addr = %x, val = %x\n", (int) addr, val);
+
+    if (addr == 0xc && val == 0x80)
+        d->unlock_state = 1;
+    else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
+        d->unlock_state = 2;
+    else {
+        if (d->unlock_state == 2) {
+            if (addr == 0)
+                d->timer1_preload = val & 0xfffff;
+            else if (addr == 4)
+                d->timer2_preload = val & 0xfffff;
+
+            d->unlock_state = 0;
+        }
+    }
+}
+
+static void i6300esb_map(PCIDevice *dev, int region_num,
+                         uint32_t addr, uint32_t size, int type)
+{
+    static CPUReadMemoryFunc *mem_read[3] = {
+        i6300esb_mem_readb,
+        i6300esb_mem_readw,
+        i6300esb_mem_readl,
+    };
+    static CPUWriteMemoryFunc *mem_write[3] = {
+        i6300esb_mem_writeb,
+        i6300esb_mem_writew,
+        i6300esb_mem_writel,
+    };
+    I6300State *d = (I6300State *) dev;
+    int io_mem;
+
+    i6300esb_debug ("addr = %x, size = %x, type = %d\n", addr, size, type);
+
+    io_mem = cpu_register_io_memory (0, mem_read, mem_write, d);
+    cpu_register_physical_memory (addr, 0x10, io_mem);
+    /* qemu_register_coalesced_mmio (addr, 0x10); ? */
+}
+
+static void i6300esb_save(QEMUFile *f, void *vp)
+{
+    I6300State *d = (I6300State *) vp;
+
+    pci_device_save (&d->dev, f);
+    qemu_put_be32 (f, d->reboot_enabled);
+    qemu_put_be32 (f, d->clock_scale);
+    qemu_put_be32 (f, d->int_type);
+    qemu_put_be32 (f, d->free_run);
+    qemu_put_be32 (f, d->locked);
+    qemu_put_be32 (f, d->enabled);
+    qemu_put_timer (f, d->timer);
+    qemu_put_be32 (f, d->timer1_preload);
+    qemu_put_be32 (f, d->timer2_preload);
+    qemu_put_be32 (f, d->stage);
+    qemu_put_be32 (f, d->unlock_state);
+    qemu_put_be32 (f, d->previous_reboot_flag);
+}
+
+static int i6300esb_load(QEMUFile *f, void *vp, int version)
+{
+    I6300State *d = (I6300State *) vp;
+
+    if (version != sizeof (I6300State))
+        return -EINVAL;
+
+    pci_device_load (&d->dev, f);
+    d->reboot_enabled = qemu_get_be32 (f);
+    d->clock_scale = qemu_get_be32 (f);
+    d->int_type = qemu_get_be32 (f);
+    d->free_run = qemu_get_be32 (f);
+    d->locked = qemu_get_be32 (f);
+    d->enabled = qemu_get_be32 (f);
+    qemu_get_timer (f, d->timer);
+    d->timer1_preload = qemu_get_be32 (f);
+    d->timer2_preload = qemu_get_be32 (f);
+    d->stage = qemu_get_be32 (f);
+    d->unlock_state = qemu_get_be32 (f);
+    d->previous_reboot_flag = qemu_get_be32 (f);
+
+    return 0;
+}
+
+/* Create and initialize a virtual Intel 6300ESB during PC creation. */
+static void i6300esb_pc_init(PCIBus *pci_bus)
+{
+    I6300State *d;
+    uint8_t *pci_conf;
+
+    if (!pci_bus) {
+        fprintf (stderr, "wdt_i6300esb: no PCI bus in this machine\n");
+        return;
+    }
+
+    d = (I6300State *)
+        pci_register_device (pci_bus, "i6300esb_wdt", sizeof (I6300State),
+                             -1,
+                             i6300esb_config_read, i6300esb_config_write);
+
+    d->reboot_enabled = 1;
+    d->clock_scale = CLOCK_SCALE_1KHZ;
+    d->int_type = INT_TYPE_IRQ;
+    d->free_run = 0;
+    d->locked = 0;
+    d->enabled = 0;
+    d->timer = qemu_new_timer (vm_clock, i6300esb_timer_expired, d);
+    d->timer1_preload = 0xfffff;
+    d->timer2_preload = 0xfffff;
+    d->stage = 1;
+    d->unlock_state = 0;
+    d->previous_reboot_flag = 0;
+
+    pci_conf = d->dev.config;
+    pci_config_set_vendor_id (pci_conf, PCI_VENDOR_ID_INTEL);
+    pci_config_set_device_id (pci_conf, PCI_DEVICE_ID_INTEL_ESB_9);
+    pci_config_set_class (pci_conf, PCI_CLASS_SYSTEM_OTHER);
+    pci_conf[0x0e] = 0x00;
+
+    pci_register_io_region (&d->dev, 0, 0x10,
+                            PCI_ADDRESS_SPACE_MEM, i6300esb_map);
+
+    register_savevm ("i6300esb_wdt", -1, sizeof (I6300State),
+                     i6300esb_save, i6300esb_load, d);
+}
+
+static WatchdogTimerModel model = {
+    .wdt_name = "i6300esb",
+    .wdt_description = "Intel 6300ESB",
+    .wdt_pc_init = i6300esb_pc_init,
+};
+
+void wdt_i6300esb_init(void)
+{
+    watchdog_add_model (&model);
+}
Index: hw/wdt_ib700.c
===================================================================
--- hw/wdt_ib700.c	(revision 0)
+++ hw/wdt_ib700.c	(revision 0)
@@ -0,0 +1,112 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "watchdog.h"
+#include "hw.h"
+#include "isa.h"
+#include "pc.h"
+
+/*#define IB700_DEBUG 1*/
+
+#ifdef IB700_DEBUG
+#define ib700_debug(fs,...)					\
+    fprintf(stderr,"ib700: %s: "fs,__func__,##__VA_ARGS__)
+#else
+#define ib700_debug(fs,...)
+#endif
+
+/* This is the timer.  We use a global here because the watchdog
+ * code ensures there is only one watchdog (it is located at a fixed,
+ * unchangable IO port, so there could only ever be one anyway).
+ */
+static QEMUTimer *timer = NULL;
+
+/* A write to this register enables the timer. */
+static void ib700_write_enable_reg(void *vp, uint32_t addr, uint32_t data)
+{
+    static int time_map[] = {
+        30, 28, 26, 24, 22, 20, 18, 16,
+        14, 12, 10,  8,  6,  4,  2,  0
+    };
+    int64 timeout;
+
+    ib700_debug ("addr = %x, data = %x\n", addr, data);
+
+    timeout = (int64_t) time_map[data & 0xF] * ticks_per_sec;
+    qemu_mod_timer (timer, qemu_get_clock (vm_clock) + timeout);
+}
+
+/* A write (of any value) to this register disables the timer. */
+static void ib700_write_disable_reg(void *vp, uint32_t addr, uint32_t data)
+{
+    ib700_debug ("addr = %x, data = %x\n", addr, data);
+
+    qemu_del_timer (timer);
+}
+
+/* This is called when the watchdog expires. */
+static void ib700_timer_expired(void *vp)
+{
+    ib700_debug ("watchdog expired\n");
+
+    watchdog_perform_action ();
+    qemu_del_timer (timer);
+}
+
+static void ib700_save(QEMUFile *f, void *vp)
+{
+    qemu_put_timer (f, timer);
+}
+
+static int ib700_load(QEMUFile *f, void *vp, int version)
+{
+    if (version != 0)
+        return -EINVAL;
+
+    qemu_get_timer (f, timer);
+
+    return 0;
+}
+
+/* Create and initialize a virtual IB700 during PC creation. */
+static void ib700_pc_init(PCIBus *unused)
+{
+    register_savevm ("ib700_wdt", -1, 0, ib700_save, ib700_load, NULL);
+
+    register_ioport_write (0x441, 2, 1, ib700_write_disable_reg, NULL);
+    register_ioport_write (0x443, 2, 1, ib700_write_enable_reg, NULL);
+}
+
+static WatchdogTimerModel model = {
+    .wdt_name = "ib700",
+    .wdt_description = "iBASE 700",
+    .wdt_pc_init = ib700_pc_init,
+};
+
+void wdt_ib700_init(void)
+{
+    watchdog_add_model (&model);
+    timer = qemu_new_timer (vm_clock, ib700_timer_expired, NULL);
+}
Index: hw/pc.c
===================================================================
--- hw/pc.c	(revision 6963)
+++ hw/pc.c	(working copy)
@@ -37,6 +37,7 @@
 #include "virtio-balloon.h"
 #include "virtio-console.h"
 #include "hpet_emul.h"
+#include "watchdog.h"
 
 /* output Bochs bios info messages */
 //#define DEBUG_BIOS
@@ -1014,6 +1015,8 @@
         }
     }
 
+    watchdog_pc_init (pci_bus);
+
     for(i = 0; i < nb_nics; i++) {
         NICInfo *nd = &nd_table[i];
 

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Qemu-devel] Re: [PATCH] Hardware watchdog patch, version 6
  2009-04-01  8:46       ` Richard W.M. Jones
@ 2009-04-01 13:18         ` Anthony Liguori
  2009-04-25 12:56           ` [Qemu-devel] [PATCH] Hardware watchdog patch, version 7 Richard W.M. Jones
  2009-04-07 21:19         ` [PATCH] Hardware watchdog patch, version 6 (was: Re: [Qemu-devel] [PATCH] Hardware watchdog patch, version 5) Hollis Blanchard
  1 sibling, 1 reply; 14+ messages in thread
From: Anthony Liguori @ 2009-04-01 13:18 UTC (permalink / raw)
  To: qemu-devel

Richard W.M. Jones wrote:
> On Tue, Mar 31, 2009 at 07:07:39PM +0300, Blue Swirl wrote:
>   
>> +                if (i > 0) exit (i == 1 ? 1 : 0);
>> +    if (!d->enabled) return;
>>
>> Please split these into two lines.
>>     
>
> Version 7 of the patch is attached:
>
>  - Fixed the 'if' statements above, plus a couple more I found.
>
>  - Rebased to latest SVN.
>
> Rich.
>   

I swear I sent a note with some review comments but I cannot find it in 
the mailing list archives.  Perhaps my mail server ate it :-(  I'll 
reproduce below.

First, there's still no Signed-off-by.

> @@ -4727,6 +4732,17 @@
>                  serial_devices[serial_device_index] = optarg;
>                  serial_device_index++;
>                  break;
> +            case QEMU_OPTION_watchdog:
> +                i = select_watchdog (optarg);
>   

You have extra spaces before all your punctuation.  This is not how QEMU 
does it (just look below).

> +                if (i > 0)
> +                    exit (i == 1 ? 1 : 0);
> +                break;
> +            case QEMU_OPTION_watchdog_action:
> +                if (select_watchdog_action (optarg) == -1) {
> +                    fprintf (stderr, "Unknown -watchdog-action parameter\n");
> +                    exit (1);
> +                }
> +                break;
>              case QEMU_OPTION_virtiocon:
>                  if (virtio_console_index >= MAX_VIRTIO_CONSOLES) {
>                      fprintf(stderr, "qemu: too many virtio consoles\n");
>   

> +void register_watchdogs(void)
> +{
> +#ifdef TARGET_I386
> +    wdt_ib700_init ();
> +    wdt_i6300esb_init ();
> +#endif
> +}
>   

Limiting to target-i386 is not correct.  These are ISA and PCI devices 
so they should be limited based on that property.

Really, you only need to limit it based on !CONFIG_USER_ONLY.  It's not 
a huge problem to add unused devices to other platforms.  After the 
config file stuff is ready, we can move to a more modular model where we 
can choose which devices are included for any given target.

Regards,

Anthony Liguori

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] Hardware watchdog patch, version 6 (was: Re: [Qemu-devel] [PATCH] Hardware watchdog patch, version 5)
  2009-04-01  8:46       ` Richard W.M. Jones
  2009-04-01 13:18         ` [Qemu-devel] Re: [PATCH] Hardware watchdog patch, version 6 Anthony Liguori
@ 2009-04-07 21:19         ` Hollis Blanchard
  2009-04-08  8:39           ` Richard W.M. Jones
  2009-04-08  9:22           ` [libvirt] " Daniel P. Berrange
  1 sibling, 2 replies; 14+ messages in thread
From: Hollis Blanchard @ 2009-04-07 21:19 UTC (permalink / raw)
  To: Richard W.M. Jones; +Cc: libvir-list, qemu-devel

Hi Rich, your watchdog qemu patch is working fine for me (i6300esb,
default reset behavior), but I was wondering... how should libvirt find
out about the watchdog firing?

At that point, I think the next step from libvirt is to send a
VIR_DOMAIN_EVENT_STOPPED_CRASHED domain event, correct?

-- 
Hollis Blanchard
IBM Linux Technology Center

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] Hardware watchdog patch, version 6 (was: Re: [Qemu-devel] [PATCH] Hardware watchdog patch, version 5)
  2009-04-07 21:19         ` [PATCH] Hardware watchdog patch, version 6 (was: Re: [Qemu-devel] [PATCH] Hardware watchdog patch, version 5) Hollis Blanchard
@ 2009-04-08  8:39           ` Richard W.M. Jones
  2009-04-08  9:22           ` [libvirt] " Daniel P. Berrange
  1 sibling, 0 replies; 14+ messages in thread
From: Richard W.M. Jones @ 2009-04-08  8:39 UTC (permalink / raw)
  To: Hollis Blanchard; +Cc: libvir-list, qemu-devel

On Tue, Apr 07, 2009 at 04:19:42PM -0500, Hollis Blanchard wrote:
> Hi Rich, your watchdog qemu patch is working fine for me (i6300esb,
> default reset behavior),

Hi, first of all thanks for testing this.  I'm going to put forward an
updated/finalized patch but it won't be til next week.

> but I was wondering... how should libvirt find
> out about the watchdog firing?

Good question.  When we've talked about adding support for the
watchdog in libvirt, I've only been thinking about configuring it, not
about how to find out when it fires.  This is a bit tricky because we
have to both get qemu to generate an event (eg. a monitor message),
and then get libvirt to handle that event.

Thus I'll throw this open to libvir-list to see if anyone has
an idea how to implement that ...

Reply-To set to libvir-list ...

> At that point, I think the next step from libvirt is to send a
> VIR_DOMAIN_EVENT_STOPPED_CRASHED domain event, correct?

Rich.

-- 
Richard Jones, Emerging Technologies, Red Hat  http://et.redhat.com/~rjones
virt-top is 'top' for virtual machines.  Tiny program with many
powerful monitoring features, net stats, disk stats, logging, etc.
http://et.redhat.com/~rjones/virt-top

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [libvirt] Re: [PATCH] Hardware watchdog patch, version 6 (was: Re: [Qemu-devel] [PATCH] Hardware watchdog patch, version 5)
  2009-04-07 21:19         ` [PATCH] Hardware watchdog patch, version 6 (was: Re: [Qemu-devel] [PATCH] Hardware watchdog patch, version 5) Hollis Blanchard
  2009-04-08  8:39           ` Richard W.M. Jones
@ 2009-04-08  9:22           ` Daniel P. Berrange
  2009-04-08 14:18             ` [Qemu-devel] Re: [libvirt] Re: [PATCH] Hardware watchdog patch, version 6 Anthony Liguori
  1 sibling, 1 reply; 14+ messages in thread
From: Daniel P. Berrange @ 2009-04-08  9:22 UTC (permalink / raw)
  To: Hollis Blanchard; +Cc: libvir-list, Richard W.M. Jones, qemu-devel

On Tue, Apr 07, 2009 at 04:19:42PM -0500, Hollis Blanchard wrote:
> Hi Rich, your watchdog qemu patch is working fine for me (i6300esb,
> default reset behavior), but I was wondering... how should libvirt find
> out about the watchdog firing?

This falls into the general problem space of how does QEMU notify its
management app  of interesting events. We already really want the
ability to be notify of ENOSPC errors in disks, and also would like
to know if the VM is paused for any reason outside our control.
There have been proposals to just print an async message to the 
monitor upon events, that mgmt app could then parse. This is a little
fragile though, so for a long term solutiuon we really need the generic
libqemumonitor.so type API with formal async notification support.

For notification of the watchdog firing, I guess the answer depends
on what sort of action you've told QEMU todo. If the action is a
'shutdown', then there won't be much opportunity to notify the mgmt
app before QEMU exits. We could define a watchdog action of 'pause'
which just pauses the VM CPUs, and then notifies via the monitor
that the watchdog fired. The mgmt app could receive this, and then
destroy the VM if desired.

> At that point, I think the next step from libvirt is to send a
> VIR_DOMAIN_EVENT_STOPPED_CRASHED domain event, correct?

That would be the ideal event to emit if we could get a notification of
the fact that watchdog fired. If QEMU does the shutdown itself, then we
won't be able to distinguish this from a regular graceful VM shutdown
and so would just end up with VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN event
being emitted when we see QEMU exit.


Daniel
-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Qemu-devel] Re: [libvirt] Re: [PATCH] Hardware watchdog patch, version 6
  2009-04-08  9:22           ` [libvirt] " Daniel P. Berrange
@ 2009-04-08 14:18             ` Anthony Liguori
  2009-04-08 14:35               ` Daniel P. Berrange
  0 siblings, 1 reply; 14+ messages in thread
From: Anthony Liguori @ 2009-04-08 14:18 UTC (permalink / raw)
  To: Daniel P. Berrange, qemu-devel
  Cc: libvir-list, Richard W.M. Jones, Hollis Blanchard

Daniel P. Berrange wrote:
> On Tue, Apr 07, 2009 at 04:19:42PM -0500, Hollis Blanchard wrote:
>   
>> Hi Rich, your watchdog qemu patch is working fine for me (i6300esb,
>> default reset behavior), but I was wondering... how should libvirt find
>> out about the watchdog firing?
>>     
>
> This falls into the general problem space of how does QEMU notify its
> management app  of interesting events. We already really want the
> ability to be notify of ENOSPC errors in disks, and also would like
> to know if the VM is paused for any reason outside our control.
> There have been proposals to just print an async message to the 
> monitor upon events, that mgmt app could then parse. This is a little
> fragile though, so for a long term solutiuon we really need the generic
> libqemumonitor.so type API with formal async notification support.
>   

I just posted a patch series that demonstrate how I think it should 
work.  FWIW, I think notifications are orthogonal to libqemumonitor.so 
and a non-human monitor mode.

Regards,

Anthony Liguori

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Qemu-devel] Re: [libvirt] Re: [PATCH] Hardware watchdog patch, version 6
  2009-04-08 14:18             ` [Qemu-devel] Re: [libvirt] Re: [PATCH] Hardware watchdog patch, version 6 Anthony Liguori
@ 2009-04-08 14:35               ` Daniel P. Berrange
  0 siblings, 0 replies; 14+ messages in thread
From: Daniel P. Berrange @ 2009-04-08 14:35 UTC (permalink / raw)
  To: Anthony Liguori
  Cc: libvir-list, qemu-devel, Hollis Blanchard, Richard W.M. Jones

On Wed, Apr 08, 2009 at 09:18:55AM -0500, Anthony Liguori wrote:
> Daniel P. Berrange wrote:
> >On Tue, Apr 07, 2009 at 04:19:42PM -0500, Hollis Blanchard wrote:
> >  
> >>Hi Rich, your watchdog qemu patch is working fine for me (i6300esb,
> >>default reset behavior), but I was wondering... how should libvirt find
> >>out about the watchdog firing?
> >>    
> >
> >This falls into the general problem space of how does QEMU notify its
> >management app  of interesting events. We already really want the
> >ability to be notify of ENOSPC errors in disks, and also would like
> >to know if the VM is paused for any reason outside our control.
> >There have been proposals to just print an async message to the 
> >monitor upon events, that mgmt app could then parse. This is a little
> >fragile though, so for a long term solutiuon we really need the generic
> >libqemumonitor.so type API with formal async notification support.
> >  
> 
> I just posted a patch series that demonstrate how I think it should 
> work.  FWIW, I think notifications are orthogonal to libqemumonitor.so 
> and a non-human monitor mode.

If we get a way to have async events without waiting for libqemumonitor.so
that'd make me very happy :-)

Daniel
-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH] Hardware watchdog patch, version 7
  2009-04-01 13:18         ` [Qemu-devel] Re: [PATCH] Hardware watchdog patch, version 6 Anthony Liguori
@ 2009-04-25 12:56           ` Richard W.M. Jones
  0 siblings, 0 replies; 14+ messages in thread
From: Richard W.M. Jones @ 2009-04-25 12:56 UTC (permalink / raw)
  To: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 432 bytes --]


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>

Rich.

-- 
Richard Jones, Emerging Technologies, Red Hat  http://et.redhat.com/~rjones
virt-top is 'top' for virtual machines.  Tiny program with many
powerful monitoring features, net stats, disk stats, logging, etc.
http://et.redhat.com/~rjones/virt-top

[-- Attachment #2: qemu-watchdog-7.patch --]
[-- Type: text/plain, Size: 31131 bytes --]

Index: Makefile.target
===================================================================
--- Makefile.target	(revision 7249)
+++ Makefile.target	(working copy)
@@ -582,6 +582,10 @@
 OBJS += rtl8139.o
 OBJS += e1000.o
 
+# Generic watchdog support and some watchdog devices
+OBJS += watchdog.o
+OBJS += wdt_ib700.o wdt_i6300esb.o
+
 ifeq ($(TARGET_BASE_ARCH), i386)
 # Hardware support
 OBJS+= ide.o pckbd.o vga.o $(SOUND_HW) dma.o
Index: vl.c
===================================================================
--- vl.c	(revision 7249)
+++ vl.c	(working copy)
@@ -138,6 +138,7 @@
 #include "hw/isa.h"
 #include "hw/baum.h"
 #include "hw/bt.h"
+#include "hw/watchdog.h"
 #include "hw/smbios.h"
 #include "hw/xen.h"
 #include "bt-host.h"
@@ -252,6 +253,8 @@
 #ifndef _WIN32
 int daemonize = 0;
 #endif
+WatchdogTimerModel *watchdog = NULL;
+int watchdog_action = WDT_RESET;
 const char *option_rom[MAX_OPTION_ROMS];
 int nb_option_roms;
 int semihosting_enabled = 0;
@@ -4899,6 +4902,8 @@
     tb_size = 0;
     autostart= 1;
 
+    register_watchdogs();
+
     optind = 1;
     for(;;) {
         if (optind >= argc)
@@ -5290,6 +5295,17 @@
                 serial_devices[serial_device_index] = optarg;
                 serial_device_index++;
                 break;
+            case QEMU_OPTION_watchdog:
+                i = select_watchdog(optarg);
+                if (i > 0)
+                    exit (i == 1 ? 1 : 0);
+                break;
+            case QEMU_OPTION_watchdog_action:
+                if (select_watchdog_action(optarg) == -1) {
+                    fprintf(stderr, "Unknown -watchdog-action parameter\n");
+                    exit(1);
+                }
+                break;
             case QEMU_OPTION_virtiocon:
                 if (virtio_console_index >= MAX_VIRTIO_CONSOLES) {
                     fprintf(stderr, "qemu: too many virtio consoles\n");
Index: qemu-options.hx
===================================================================
--- qemu-options.hx	(revision 7249)
+++ qemu-options.hx	(working copy)
@@ -1448,6 +1448,55 @@
 executed often has little or no correlation with actual performance.
 ETEXI
 
+DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \
+    "-watchdog i6300esb|ib700\n" \
+    "                enable virtual hardware watchdog [default=none]\n")
+STEXI
+@item -watchdog @var{model}
+Create a virtual hardware watchdog device.  Once enabled (by a guest
+action), the watchdog must be periodically polled by an agent inside
+the guest or else the guest will be restarted.
+
+The @var{model} is the model of hardware watchdog to emulate.  Choices
+for model are: @code{ib700} (iBASE 700) which is a very simple ISA
+watchdog with a single timer, or @code{i6300esb} (Intel 6300ESB I/O
+controller hub) which is a much more featureful PCI-based dual-timer
+watchdog.  Choose a model for which your guest has drivers.
+
+Use @code{-watchdog ?} to list available hardware models.  Only one
+watchdog can be enabled for a guest.
+ETEXI
+
+DEF("watchdog-action", HAS_ARG, QEMU_OPTION_watchdog_action, \
+    "-watchdog-action reset|shutdown|poweroff|pause|debug|none\n" \
+    "                action when watchdog fires [default=reset]\n")
+STEXI
+@item -watchdog-action @var{action}
+
+The @var{action} controls what QEMU will do when the watchdog timer
+expires.
+The default is
+@code{reset} (forcefully reset the guest).
+Other possible actions are:
+@code{shutdown} (attempt to gracefully shutdown the guest),
+@code{poweroff} (forcefully poweroff the guest),
+@code{pause} (pause the guest),
+@code{debug} (print a debug message and continue), or
+@code{none} (do nothing).
+
+Note that the @code{shutdown} action requires that the guest responds
+to ACPI signals, which it may not be able to do in the sort of
+situations where the watchdog would have expired, and thus
+@code{-watchdog-action shutdown} is not recommended for production use.
+
+Examples:
+
+@table @code
+@item -watchdog i6300esb -watchdog-action pause
+@item -watchdog ib700
+@end table
+ETEXI
+
 DEF("echr", HAS_ARG, QEMU_OPTION_echr, \
     "-echr chr       set terminal escape character instead of ctrl-a\n")
 STEXI
Index: monitor.c
===================================================================
--- monitor.c	(revision 7249)
+++ monitor.c	(working copy)
@@ -27,6 +27,7 @@
 #include "hw/pcmcia.h"
 #include "hw/pc.h"
 #include "hw/pci.h"
+#include "hw/watchdog.h"
 #include "gdbstub.h"
 #include "net.h"
 #include "qemu-char.h"
@@ -597,6 +598,13 @@
 }
 #endif
 
+static void do_watchdog_action(Monitor *mon, const char *action)
+{
+    if (select_watchdog_action(action) == -1) {
+        monitor_printf(mon, "Unknown watchdog action '%s'\n", action);
+    }
+}
+
 static void monitor_printc(Monitor *mon, int c)
 {
     monitor_printf(mon, "'");
@@ -1762,6 +1770,8 @@
       "target", "request VM to change it's memory allocation (in MB)" },
     { "set_link", "ss", do_set_link,
       "name up|down", "change the link status of a network adapter" },
+    { "watchdog_action", "s", do_watchdog_action,
+      "[reset|shutdown|poweroff|pause|debug|none]", "change watchdog action" },
     { "acl", "sss?i?", do_acl, "<command> <aclname> [<match> [<index>]]\n",
                                "acl show vnc.username\n"
                                "acl policy vnc.username deny\n"
Index: hw/watchdog.c
===================================================================
--- hw/watchdog.c	(revision 0)
+++ hw/watchdog.c	(revision 0)
@@ -0,0 +1,136 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#include "qemu-common.h"
+#include "sys-queue.h"
+#include "sysemu.h"
+#include "hw/watchdog.h"
+
+static LIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list;
+
+void watchdog_add_model(WatchdogTimerModel *model)
+{
+    LIST_INSERT_HEAD(&watchdog_list, model, entry);
+}
+
+/* Returns:
+ *   0 = continue
+ *   1 = exit program with error
+ *   2 = exit program without error
+ */
+int select_watchdog(const char *p)
+{
+    WatchdogTimerModel *model;
+
+    if (watchdog) {
+        fprintf(stderr,
+                 "qemu: only one watchdog option may be given\n");
+        return 1;
+    }
+
+    /* -watchdog ? lists available devices and exits cleanly. */
+    if (strcmp(p, "?") == 0) {
+        LIST_FOREACH(model, &watchdog_list, entry) {
+            fprintf(stderr, "\t%s\t%s\n",
+                     model->wdt_name, model->wdt_description);
+        }
+        return 2;
+    }
+
+    LIST_FOREACH(model, &watchdog_list, entry) {
+        if (strcasecmp(model->wdt_name, p) == 0) {
+            watchdog = model;
+            return 0;
+        }
+    }
+
+    fprintf(stderr, "Unknown -watchdog device. Supported devices are:\n");
+    LIST_FOREACH(model, &watchdog_list, entry) {
+        fprintf(stderr, "\t%s\t%s\n",
+                 model->wdt_name, model->wdt_description);
+    }
+    return 1;
+}
+
+int select_watchdog_action(const char *p)
+{
+    if (strcasecmp(p, "reset") == 0)
+        watchdog_action = WDT_RESET;
+    else if (strcasecmp(p, "shutdown") == 0)
+        watchdog_action = WDT_SHUTDOWN;
+    else if (strcasecmp(p, "poweroff") == 0)
+        watchdog_action = WDT_POWEROFF;
+    else if (strcasecmp(p, "pause") == 0)
+        watchdog_action = WDT_PAUSE;
+    else if (strcasecmp(p, "debug") == 0)
+        watchdog_action = WDT_DEBUG;
+    else if (strcasecmp(p, "none") == 0)
+        watchdog_action = WDT_NONE;
+    else
+        return -1;
+
+    return 0;
+}
+
+/* This actually performs the "action" once a watchdog has expired,
+ * ie. reboot, shutdown, exit, etc.
+ */
+void watchdog_perform_action(void)
+{
+    switch(watchdog_action) {
+    case WDT_RESET:             /* same as 'system_reset' in monitor */
+        qemu_system_reset_request();
+        break;
+
+    case WDT_SHUTDOWN:          /* same as 'system_powerdown' in monitor */
+        qemu_system_powerdown_request();
+        break;
+
+    case WDT_POWEROFF:          /* same as 'quit' command in monitor */
+        exit(0);
+        break;
+
+    case WDT_PAUSE:             /* same as 'stop' command in monitor */
+        vm_stop(0);
+        break;
+
+    case WDT_DEBUG:
+        fprintf(stderr, "watchdog: timer fired\n");
+        break;
+
+    case WDT_NONE:
+        break;
+    }
+}
+
+void watchdog_pc_init(PCIBus *pci_bus)
+{
+    if (watchdog)
+        watchdog->wdt_pc_init(pci_bus);
+}
+
+void register_watchdogs(void)
+{
+    wdt_ib700_init();
+    wdt_i6300esb_init();
+}
Index: hw/watchdog.h
===================================================================
--- hw/watchdog.h	(revision 0)
+++ hw/watchdog.h	(revision 0)
@@ -0,0 +1,65 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#ifndef QEMU_WATCHDOG_H
+#define QEMU_WATCHDOG_H
+
+extern void wdt_i6300esb_init(void);
+extern void wdt_ib700_init(void);
+
+/* Possible values for action parameter. */
+#define WDT_RESET        1	/* Hard reset. */
+#define WDT_SHUTDOWN     2	/* Shutdown. */
+#define WDT_POWEROFF     3	/* Quit. */
+#define WDT_PAUSE        4	/* Pause. */
+#define WDT_DEBUG        5	/* Prints a message and continues running. */
+#define WDT_NONE         6	/* Do nothing. */
+
+struct WatchdogTimerModel {
+    LIST_ENTRY(WatchdogTimerModel) entry;
+
+    /* Short name of the device - used to select it on the command line. */
+    const char *wdt_name;
+    /* Longer description (eg. manufacturer and full model number). */
+    const char *wdt_description;
+
+    /* This callback should create/register the device.  It is called
+     * indirectly from hw/pc.c when the virtual PC is being set up.
+     */
+    void (*wdt_pc_init)(PCIBus *pci_bus);
+};
+typedef struct WatchdogTimerModel WatchdogTimerModel;
+
+/* in vl.c */
+extern WatchdogTimerModel *watchdog;
+extern int watchdog_action;
+
+/* in hw/watchdog.c */
+extern int select_watchdog(const char *p);
+extern int select_watchdog_action(const char *action);
+extern void watchdog_add_model(WatchdogTimerModel *model);
+extern void watchdog_perform_action(void);
+extern void watchdog_pc_init(PCIBus *pci_bus);
+extern void register_watchdogs(void);
+
+#endif /* QEMU_WATCHDOG_H */
Index: hw/wdt_i6300esb.c
===================================================================
--- hw/wdt_i6300esb.c	(revision 0)
+++ hw/wdt_i6300esb.c	(revision 0)
@@ -0,0 +1,470 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#include <inttypes.h>
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "watchdog.h"
+#include "hw.h"
+#include "isa.h"
+#include "pc.h"
+#include "pci.h"
+
+/*#define I6300ESB_DEBUG 1*/
+
+#ifdef I6300ESB_DEBUG
+#define i6300esb_debug(fs,...) \
+    fprintf(stderr,"i6300esb: %s: "fs,__func__,##__VA_ARGS__)
+#else
+#define i6300esb_debug(fs,...)
+#endif
+
+#ifndef PCI_DEVICE_ID_INTEL_ESB_9
+#define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab
+#endif
+
+/* PCI configuration registers */
+#define ESB_CONFIG_REG  0x60            /* Config register                   */
+#define ESB_LOCK_REG    0x68            /* WDT lock register                 */
+
+/* Memory mapped registers (offset from base address) */
+#define ESB_TIMER1_REG  0x00            /* Timer1 value after each reset     */
+#define ESB_TIMER2_REG  0x04            /* Timer2 value after each reset     */
+#define ESB_GINTSR_REG  0x08            /* General Interrupt Status Register */
+#define ESB_RELOAD_REG  0x0c            /* Reload register                   */
+
+/* Lock register bits */
+#define ESB_WDT_FUNC    (0x01 << 2)   /* Watchdog functionality            */
+#define ESB_WDT_ENABLE  (0x01 << 1)   /* Enable WDT                        */
+#define ESB_WDT_LOCK    (0x01 << 0)   /* Lock (nowayout)                   */
+
+/* Config register bits */
+#define ESB_WDT_REBOOT  (0x01 << 5)   /* Enable reboot on timeout          */
+#define ESB_WDT_FREQ    (0x01 << 2)   /* Decrement frequency               */
+#define ESB_WDT_INTTYPE (0x11 << 0)   /* Interrupt type on timer1 timeout  */
+
+/* Reload register bits */
+#define ESB_WDT_RELOAD  (0x01 << 8)    /* prevent timeout                   */
+
+/* Magic constants */
+#define ESB_UNLOCK1     0x80            /* Step 1 to unlock reset registers  */
+#define ESB_UNLOCK2     0x86            /* Step 2 to unlock reset registers  */
+
+/* Device state. */
+struct I6300State {
+    PCIDevice dev;              /* PCI device state, must be first field. */
+
+    int reboot_enabled;         /* "Reboot" on timer expiry.  The real action
+                                 * performed depends on the -watchdog-action
+                                 * param passed on QEMU command line.
+                                 */
+    int clock_scale;            /* Clock scale. */
+#define CLOCK_SCALE_1KHZ 0
+#define CLOCK_SCALE_1MHZ 1
+
+    int int_type;               /* Interrupt type generated. */
+#define INT_TYPE_IRQ 0          /* APIC 1, INT 10 */
+#define INT_TYPE_SMI 2
+#define INT_TYPE_DISABLED 3
+
+    int free_run;               /* If true, reload timer on expiry. */
+    int locked;                 /* If true, enabled field cannot be changed. */
+    int enabled;                /* If true, watchdog is enabled. */
+
+    QEMUTimer *timer;           /* The actual watchdog timer. */
+
+    uint32_t timer1_preload;    /* Values preloaded into timer1, timer2. */
+    uint32_t timer2_preload;
+    int stage;                  /* Stage (1 or 2). */
+
+    int unlock_state;           /* Guest writes 0x80, 0x86 to unlock the
+                                 * registers, and we transition through
+                                 * states 0 -> 1 -> 2 when this happens.
+                                 */
+
+    int previous_reboot_flag;   /* If the watchdog caused the previous
+                                 * reboot, this flag will be set.
+                                 */
+};
+
+typedef struct I6300State I6300State;
+
+/* This function is called when the watchdog has either been enabled
+ * (hence it starts counting down) or has been keep-alived.
+ */
+static void i6300esb_restart_timer(I6300State *d, int stage)
+{
+    int64_t timeout;
+
+    if (!d->enabled)
+        return;
+
+    d->stage = stage;
+
+    if (d->stage <= 1)
+        timeout = d->timer1_preload;
+    else
+        timeout = d->timer2_preload;
+
+    if (d->clock_scale == CLOCK_SCALE_1KHZ)
+        timeout <<= 15;
+    else
+        timeout <<= 5;
+
+    /* Get the timeout in units of ticks_per_sec. */
+    timeout = ticks_per_sec * timeout / 33000000;
+
+    i6300esb_debug("stage %d, timeout %" PRIi64 "\n", d->stage, timeout);
+
+    qemu_mod_timer(d->timer, qemu_get_clock(vm_clock) + timeout);
+}
+
+/* This is called when the guest disables the watchdog. */
+static void i6300esb_disable_timer(I6300State *d)
+{
+    i6300esb_debug("timer disabled\n");
+
+    qemu_del_timer(d->timer);
+}
+
+static void i6300esb_reset(I6300State *d)
+{
+    /* XXX We should probably reset other parts of the state here,
+     * but we should also reset our state on general machine reset
+     * too.  For now just disable the timer so it doesn't fire
+     * again after the reboot.
+     */
+    i6300esb_disable_timer(d);
+}
+
+/* This function is called when the watchdog expires.  Note that
+ * the hardware has two timers, and so expiry happens in two stages.
+ * If d->stage == 1 then we perform the first stage action (usually,
+ * sending an interrupt) and then restart the timer again for the
+ * second stage.  If the second stage expires then the watchdog
+ * really has run out.
+ */
+static void i6300esb_timer_expired(void *vp)
+{
+    I6300State *d = (I6300State *) vp;
+
+    i6300esb_debug("stage %d\n", d->stage);
+
+    if (d->stage == 1) {
+        /* What to do at the end of stage 1? */
+        switch (d->int_type) {
+        case INT_TYPE_IRQ:
+            fprintf(stderr, "i6300esb_timer_expired: I would send APIC 1 INT 10 here if I knew how (XXX)\n");
+            break;
+        case INT_TYPE_SMI:
+            fprintf(stderr, "i6300esb_timer_expired: I would send SMI here if I knew how (XXX)\n");
+            break;
+        }
+
+        /* Start the second stage. */
+        i6300esb_restart_timer(d, 2);
+    } else {
+        /* Second stage expired, reboot for real. */
+        if (d->reboot_enabled) {
+            d->previous_reboot_flag = 1;
+            watchdog_perform_action(); /* This reboots, exits, etc */
+            i6300esb_reset(d);
+        }
+
+        /* In "free running mode" we start stage 1 again. */
+        if (d->free_run)
+            i6300esb_restart_timer(d, 1);
+    }
+}
+
+static void i6300esb_config_write(PCIDevice *dev, uint32_t addr,
+                                  uint32_t data, int len)
+{
+    I6300State *d = (I6300State *) dev;
+    int old;
+
+    i6300esb_debug("addr = %x, data = %x, len = %d\n", addr, data, len);
+
+    if (addr == ESB_CONFIG_REG && len == 2) {
+        d->reboot_enabled = (data & ESB_WDT_REBOOT) == 0;
+        d->clock_scale =
+            (data & ESB_WDT_FREQ) != 0 ? CLOCK_SCALE_1MHZ : CLOCK_SCALE_1KHZ;
+        d->int_type = (data & ESB_WDT_INTTYPE);
+    } else if (addr == ESB_LOCK_REG && len == 1) {
+        if (!d->locked) {
+            d->locked = (data & ESB_WDT_LOCK) != 0;
+            d->free_run = (data & ESB_WDT_FUNC) != 0;
+            old = d->enabled;
+            d->enabled = (data & ESB_WDT_ENABLE) != 0;
+            if (!old && d->enabled) /* Enabled transitioned from 0 -> 1 */
+                i6300esb_restart_timer(d, 1);
+            else if (!d->enabled)
+                i6300esb_disable_timer(d);
+        }
+    } else {
+        pci_default_write_config(dev, addr, data, len);
+    }
+}
+
+static uint32_t i6300esb_config_read(PCIDevice *dev, uint32_t addr, int len)
+{
+    I6300State *d = (I6300State *) dev;
+    uint32_t data;
+
+    i6300esb_debug ("addr = %x, len = %d\n", addr, len);
+
+    if (addr == ESB_CONFIG_REG && len == 2) {
+        data =
+            (d->reboot_enabled ? 0 : ESB_WDT_REBOOT) |
+            (d->clock_scale == CLOCK_SCALE_1MHZ ? ESB_WDT_FREQ : 0) |
+            d->int_type;
+        return data;
+    } else if (addr == ESB_LOCK_REG && len == 1) {
+        data =
+            (d->free_run ? ESB_WDT_FUNC : 0) |
+            (d->locked ? ESB_WDT_LOCK : 0) |
+            (d->enabled ? ESB_WDT_ENABLE : 0);
+        return data;
+    } else {
+        return pci_default_read_config(dev, addr, len);
+    }
+}
+
+static uint32_t i6300esb_mem_readb(void *vp, target_phys_addr_t addr)
+{
+    i6300esb_debug ("addr = %x\n", (int) addr);
+
+    return 0;
+}
+
+static uint32_t i6300esb_mem_readw(void *vp, target_phys_addr_t addr)
+{
+    uint32_t data = 0;
+    I6300State *d = (I6300State *) vp;
+
+    i6300esb_debug("addr = %x\n", (int) addr);
+
+    if (addr == 0xc) {
+        /* The previous reboot flag is really bit 9, but there is
+         * a bug in the Linux driver where it thinks it's bit 12.
+         * Set both.
+         */
+        data = d->previous_reboot_flag ? 0x1200 : 0;
+    }
+
+    return data;
+}
+
+static uint32_t i6300esb_mem_readl(void *vp, target_phys_addr_t addr)
+{
+    i6300esb_debug("addr = %x\n", (int) addr);
+
+    return 0;
+}
+
+static void i6300esb_mem_writeb(void *vp, target_phys_addr_t addr, uint32_t val)
+{
+    I6300State *d = (I6300State *) vp;
+
+    i6300esb_debug("addr = %x, val = %x\n", (int) addr, val);
+
+    if (addr == 0xc && val == 0x80)
+        d->unlock_state = 1;
+    else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
+        d->unlock_state = 2;
+}
+
+static void i6300esb_mem_writew(void *vp, target_phys_addr_t addr, uint32_t val)
+{
+    I6300State *d = (I6300State *) vp;
+
+    i6300esb_debug("addr = %x, val = %x\n", (int) addr, val);
+
+    if (addr == 0xc && val == 0x80)
+        d->unlock_state = 1;
+    else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
+        d->unlock_state = 2;
+    else {
+        if (d->unlock_state == 2) {
+            if (addr == 0xc) {
+                if ((val & 0x100) != 0)
+                    /* This is the "ping" from the userspace watchdog in
+                     * the guest ...
+                     */
+                    i6300esb_restart_timer(d, 1);
+
+                /* Setting bit 9 resets the previous reboot flag.
+                 * There's a bug in the Linux driver where it sets
+                 * bit 12 instead.
+                 */
+                if ((val & 0x200) != 0 || (val & 0x1000) != 0) {
+                    d->previous_reboot_flag = 0;
+                }
+            }
+
+            d->unlock_state = 0;
+        }
+    }
+}
+
+static void i6300esb_mem_writel(void *vp, target_phys_addr_t addr, uint32_t val)
+{
+    I6300State *d = (I6300State *) vp;
+
+    i6300esb_debug ("addr = %x, val = %x\n", (int) addr, val);
+
+    if (addr == 0xc && val == 0x80)
+        d->unlock_state = 1;
+    else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
+        d->unlock_state = 2;
+    else {
+        if (d->unlock_state == 2) {
+            if (addr == 0)
+                d->timer1_preload = val & 0xfffff;
+            else if (addr == 4)
+                d->timer2_preload = val & 0xfffff;
+
+            d->unlock_state = 0;
+        }
+    }
+}
+
+static void i6300esb_map(PCIDevice *dev, int region_num,
+                         uint32_t addr, uint32_t size, int type)
+{
+    static CPUReadMemoryFunc *mem_read[3] = {
+        i6300esb_mem_readb,
+        i6300esb_mem_readw,
+        i6300esb_mem_readl,
+    };
+    static CPUWriteMemoryFunc *mem_write[3] = {
+        i6300esb_mem_writeb,
+        i6300esb_mem_writew,
+        i6300esb_mem_writel,
+    };
+    I6300State *d = (I6300State *) dev;
+    int io_mem;
+
+    i6300esb_debug("addr = %x, size = %x, type = %d\n", addr, size, type);
+
+    io_mem = cpu_register_io_memory (0, mem_read, mem_write, d);
+    cpu_register_physical_memory (addr, 0x10, io_mem);
+    /* qemu_register_coalesced_mmio (addr, 0x10); ? */
+}
+
+static void i6300esb_save(QEMUFile *f, void *vp)
+{
+    I6300State *d = (I6300State *) vp;
+
+    pci_device_save(&d->dev, f);
+    qemu_put_be32(f, d->reboot_enabled);
+    qemu_put_be32(f, d->clock_scale);
+    qemu_put_be32(f, d->int_type);
+    qemu_put_be32(f, d->free_run);
+    qemu_put_be32(f, d->locked);
+    qemu_put_be32(f, d->enabled);
+    qemu_put_timer(f, d->timer);
+    qemu_put_be32(f, d->timer1_preload);
+    qemu_put_be32(f, d->timer2_preload);
+    qemu_put_be32(f, d->stage);
+    qemu_put_be32(f, d->unlock_state);
+    qemu_put_be32(f, d->previous_reboot_flag);
+}
+
+static int i6300esb_load(QEMUFile *f, void *vp, int version)
+{
+    I6300State *d = (I6300State *) vp;
+
+    if (version != sizeof (I6300State))
+        return -EINVAL;
+
+    pci_device_load(&d->dev, f);
+    d->reboot_enabled = qemu_get_be32(f);
+    d->clock_scale = qemu_get_be32(f);
+    d->int_type = qemu_get_be32(f);
+    d->free_run = qemu_get_be32(f);
+    d->locked = qemu_get_be32(f);
+    d->enabled = qemu_get_be32(f);
+    qemu_get_timer(f, d->timer);
+    d->timer1_preload = qemu_get_be32(f);
+    d->timer2_preload = qemu_get_be32(f);
+    d->stage = qemu_get_be32(f);
+    d->unlock_state = qemu_get_be32(f);
+    d->previous_reboot_flag = qemu_get_be32(f);
+
+    return 0;
+}
+
+/* Create and initialize a virtual Intel 6300ESB during PC creation. */
+static void i6300esb_pc_init(PCIBus *pci_bus)
+{
+    I6300State *d;
+    uint8_t *pci_conf;
+
+    if (!pci_bus) {
+        fprintf(stderr, "wdt_i6300esb: no PCI bus in this machine\n");
+        return;
+    }
+
+    d = (I6300State *)
+        pci_register_device (pci_bus, "i6300esb_wdt", sizeof (I6300State),
+                             -1,
+                             i6300esb_config_read, i6300esb_config_write);
+
+    d->reboot_enabled = 1;
+    d->clock_scale = CLOCK_SCALE_1KHZ;
+    d->int_type = INT_TYPE_IRQ;
+    d->free_run = 0;
+    d->locked = 0;
+    d->enabled = 0;
+    d->timer = qemu_new_timer(vm_clock, i6300esb_timer_expired, d);
+    d->timer1_preload = 0xfffff;
+    d->timer2_preload = 0xfffff;
+    d->stage = 1;
+    d->unlock_state = 0;
+    d->previous_reboot_flag = 0;
+
+    pci_conf = d->dev.config;
+    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
+    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_ESB_9);
+    pci_config_set_class(pci_conf, PCI_CLASS_SYSTEM_OTHER);
+    pci_conf[0x0e] = 0x00;
+
+    pci_register_io_region(&d->dev, 0, 0x10,
+                            PCI_ADDRESS_SPACE_MEM, i6300esb_map);
+
+    register_savevm("i6300esb_wdt", -1, sizeof(I6300State),
+                     i6300esb_save, i6300esb_load, d);
+}
+
+static WatchdogTimerModel model = {
+    .wdt_name = "i6300esb",
+    .wdt_description = "Intel 6300ESB",
+    .wdt_pc_init = i6300esb_pc_init,
+};
+
+void wdt_i6300esb_init(void)
+{
+    watchdog_add_model(&model);
+}
Index: hw/wdt_ib700.c
===================================================================
--- hw/wdt_ib700.c	(revision 0)
+++ hw/wdt_ib700.c	(revision 0)
@@ -0,0 +1,112 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "watchdog.h"
+#include "hw.h"
+#include "isa.h"
+#include "pc.h"
+
+/*#define IB700_DEBUG 1*/
+
+#ifdef IB700_DEBUG
+#define ib700_debug(fs,...)					\
+    fprintf(stderr,"ib700: %s: "fs,__func__,##__VA_ARGS__)
+#else
+#define ib700_debug(fs,...)
+#endif
+
+/* This is the timer.  We use a global here because the watchdog
+ * code ensures there is only one watchdog (it is located at a fixed,
+ * unchangable IO port, so there could only ever be one anyway).
+ */
+static QEMUTimer *timer = NULL;
+
+/* A write to this register enables the timer. */
+static void ib700_write_enable_reg(void *vp, uint32_t addr, uint32_t data)
+{
+    static int time_map[] = {
+        30, 28, 26, 24, 22, 20, 18, 16,
+        14, 12, 10,  8,  6,  4,  2,  0
+    };
+    int64 timeout;
+
+    ib700_debug("addr = %x, data = %x\n", addr, data);
+
+    timeout = (int64_t) time_map[data & 0xF] * ticks_per_sec;
+    qemu_mod_timer(timer, qemu_get_clock (vm_clock) + timeout);
+}
+
+/* A write (of any value) to this register disables the timer. */
+static void ib700_write_disable_reg(void *vp, uint32_t addr, uint32_t data)
+{
+    ib700_debug("addr = %x, data = %x\n", addr, data);
+
+    qemu_del_timer(timer);
+}
+
+/* This is called when the watchdog expires. */
+static void ib700_timer_expired(void *vp)
+{
+    ib700_debug("watchdog expired\n");
+
+    watchdog_perform_action();
+    qemu_del_timer(timer);
+}
+
+static void ib700_save(QEMUFile *f, void *vp)
+{
+    qemu_put_timer(f, timer);
+}
+
+static int ib700_load(QEMUFile *f, void *vp, int version)
+{
+    if (version != 0)
+        return -EINVAL;
+
+    qemu_get_timer(f, timer);
+
+    return 0;
+}
+
+/* Create and initialize a virtual IB700 during PC creation. */
+static void ib700_pc_init(PCIBus *unused)
+{
+    register_savevm("ib700_wdt", -1, 0, ib700_save, ib700_load, NULL);
+
+    register_ioport_write(0x441, 2, 1, ib700_write_disable_reg, NULL);
+    register_ioport_write(0x443, 2, 1, ib700_write_enable_reg, NULL);
+}
+
+static WatchdogTimerModel model = {
+    .wdt_name = "ib700",
+    .wdt_description = "iBASE 700",
+    .wdt_pc_init = ib700_pc_init,
+};
+
+void wdt_ib700_init(void)
+{
+    watchdog_add_model(&model);
+    timer = qemu_new_timer(vm_clock, ib700_timer_expired, NULL);
+}
Index: hw/pc.c
===================================================================
--- hw/pc.c	(revision 7249)
+++ hw/pc.c	(working copy)
@@ -37,6 +37,7 @@
 #include "virtio-balloon.h"
 #include "virtio-console.h"
 #include "hpet_emul.h"
+#include "watchdog.h"
 #include "smbios.h"
 
 /* output Bochs bios info messages */
@@ -1023,6 +1024,8 @@
         }
     }
 
+    watchdog_pc_init(pci_bus);
+
     for(i = 0; i < nb_nics; i++) {
         NICInfo *nd = &nd_table[i];
 

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2009-04-25 12:56 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-03-16 18:49 [Qemu-devel] [PATCH] Hardware watchdog patch, version 5 Richard W.M. Jones
2009-03-25  8:43 ` Richard W.M. Jones
2009-03-26 13:03 ` Anthony Liguori
2009-03-31 11:23   ` [PATCH] Hardware watchdog patch, version 6 (was: Re: [Qemu-devel] [PATCH] Hardware watchdog patch, version 5) Richard W.M. Jones
2009-03-31 11:24     ` Richard W.M. Jones
2009-03-31 16:07     ` Blue Swirl
2009-04-01  8:46       ` Richard W.M. Jones
2009-04-01 13:18         ` [Qemu-devel] Re: [PATCH] Hardware watchdog patch, version 6 Anthony Liguori
2009-04-25 12:56           ` [Qemu-devel] [PATCH] Hardware watchdog patch, version 7 Richard W.M. Jones
2009-04-07 21:19         ` [PATCH] Hardware watchdog patch, version 6 (was: Re: [Qemu-devel] [PATCH] Hardware watchdog patch, version 5) Hollis Blanchard
2009-04-08  8:39           ` Richard W.M. Jones
2009-04-08  9:22           ` [libvirt] " Daniel P. Berrange
2009-04-08 14:18             ` [Qemu-devel] Re: [libvirt] Re: [PATCH] Hardware watchdog patch, version 6 Anthony Liguori
2009-04-08 14:35               ` Daniel P. Berrange

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).