Merge remote-tracking branch 'qmp/queue/qmp' into staging
diff --git a/audio/audio.c b/audio/audio.c
index 5649075..50d0d71 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -1743,7 +1743,7 @@
 }
 
 static void audio_vm_change_state_handler (void *opaque, int running,
-                                           int reason)
+                                           RunState state)
 {
     AudioState *s = opaque;
     HWVoiceOut *hwo = NULL;
diff --git a/cpus.c b/cpus.c
index 54c188c..883c27a 100644
--- a/cpus.c
+++ b/cpus.c
@@ -115,16 +115,16 @@
 
 int cpu_is_stopped(CPUState *env)
 {
-    return !vm_running || env->stopped;
+    return !runstate_is_running() || env->stopped;
 }
 
-static void do_vm_stop(int reason)
+static void do_vm_stop(RunState state)
 {
-    if (vm_running) {
+    if (runstate_is_running()) {
         cpu_disable_ticks();
-        vm_running = 0;
         pause_all_vcpus();
-        vm_state_notify(0, reason);
+        runstate_set(state);
+        vm_state_notify(0, state);
         qemu_aio_flush();
         bdrv_flush_all();
         monitor_protocol_event(QEVENT_STOP, NULL);
@@ -136,7 +136,7 @@
     if (env->stop) {
         return 0;
     }
-    if (env->stopped || !vm_running) {
+    if (env->stopped || !runstate_is_running()) {
         return 0;
     }
     return 1;
@@ -147,7 +147,7 @@
     if (env->stop || env->queued_work_first) {
         return false;
     }
-    if (env->stopped || !vm_running) {
+    if (env->stopped || !runstate_is_running()) {
         return true;
     }
     if (!env->halted || qemu_cpu_has_work(env) ||
@@ -878,10 +878,10 @@
     }
 }
 
-void vm_stop(int reason)
+void vm_stop(RunState state)
 {
     if (!qemu_thread_is_self(&io_thread)) {
-        qemu_system_vmstop_request(reason);
+        qemu_system_vmstop_request(state);
         /*
          * FIXME: should not return to device code in case
          * vm_stop() has been requested.
@@ -889,7 +889,7 @@
         cpu_stop_current();
         return;
     }
-    do_vm_stop(reason);
+    do_vm_stop(state);
 }
 
 static int tcg_cpu_exec(CPUState *env)
diff --git a/cpus.h b/cpus.h
index f42b54e..5885885 100644
--- a/cpus.h
+++ b/cpus.h
@@ -15,7 +15,6 @@
 /* vl.c */
 extern int smp_cores;
 extern int smp_threads;
-void vm_state_notify(int running, int reason);
 bool cpu_exec_all(void);
 void set_numa_modes(void);
 void set_cpu_log(const char *optarg);
diff --git a/gdbstub.c b/gdbstub.c
index 90683a4..12dd100 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -2373,7 +2373,7 @@
 }
 
 #ifndef CONFIG_USER_ONLY
-static void gdb_vm_state_change(void *opaque, int running, int reason)
+static void gdb_vm_state_change(void *opaque, int running, RunState state)
 {
     GDBState *s = gdbserver_state;
     CPUState *env = s->c_cpu;
@@ -2384,8 +2384,8 @@
     if (running || s->state == RS_INACTIVE || s->state == RS_SYSCALL) {
         return;
     }
-    switch (reason) {
-    case VMSTOP_DEBUG:
+    switch (state) {
+    case RSTATE_DEBUG:
         if (env->watchpoint_hit) {
             switch (env->watchpoint_hit->flags & BP_MEM_ACCESS) {
             case BP_MEM_READ:
@@ -2408,25 +2408,25 @@
         tb_flush(env);
         ret = GDB_SIGNAL_TRAP;
         break;
-    case VMSTOP_USER:
+    case RSTATE_PAUSED:
         ret = GDB_SIGNAL_INT;
         break;
-    case VMSTOP_SHUTDOWN:
+    case RSTATE_SHUTDOWN:
         ret = GDB_SIGNAL_QUIT;
         break;
-    case VMSTOP_DISKFULL:
+    case RSTATE_IO_ERROR:
         ret = GDB_SIGNAL_IO;
         break;
-    case VMSTOP_WATCHDOG:
+    case RSTATE_WATCHDOG:
         ret = GDB_SIGNAL_ALRM;
         break;
-    case VMSTOP_PANIC:
+    case RSTATE_PANICKED:
         ret = GDB_SIGNAL_ABRT;
         break;
-    case VMSTOP_SAVEVM:
-    case VMSTOP_LOADVM:
+    case RSTATE_SAVEVM:
+    case RSTATE_RESTORE:
         return;
-    case VMSTOP_MIGRATE:
+    case RSTATE_PRE_MIGRATE:
         ret = GDB_SIGNAL_XCPU;
         break;
     default:
@@ -2463,7 +2463,7 @@
     gdb_current_syscall_cb = cb;
     s->state = RS_SYSCALL;
 #ifndef CONFIG_USER_ONLY
-    vm_stop(VMSTOP_DEBUG);
+    vm_stop(RSTATE_DEBUG);
 #endif
     s->state = RS_IDLE;
     va_start(va, fmt);
@@ -2534,10 +2534,10 @@
         if (ch != '$')
             return;
     }
-    if (vm_running) {
+    if (runstate_is_running()) {
         /* when the CPU is running, we cannot do anything except stop
            it when receiving a char */
-        vm_stop(VMSTOP_USER);
+        vm_stop(RSTATE_PAUSED);
     } else
 #endif
     {
@@ -2799,7 +2799,7 @@
 {
     switch (event) {
     case CHR_EVENT_OPENED:
-        vm_stop(VMSTOP_USER);
+        vm_stop(RSTATE_PAUSED);
         gdb_has_xml = 0;
         break;
     default:
@@ -2839,8 +2839,8 @@
 #ifndef _WIN32
 static void gdb_sigterm_handler(int signal)
 {
-    if (vm_running) {
-        vm_stop(VMSTOP_USER);
+    if (runstate_is_running()) {
+        vm_stop(RSTATE_PAUSED);
     }
 }
 #endif
diff --git a/hw/etraxfs_dma.c b/hw/etraxfs_dma.c
index e8ad9e6..d3082ac 100644
--- a/hw/etraxfs_dma.c
+++ b/hw/etraxfs_dma.c
@@ -732,7 +732,7 @@
     struct fs_dma_ctrl *etraxfs_dmac = opaque;
     int p = 1;
 
-    if (vm_running)
+    if (runstate_is_running())
         p = etraxfs_dmac_run(etraxfs_dmac);
 
     if (p)
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index a8659cf..b813236 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -1103,7 +1103,7 @@
 {
 }
 
-static void ahci_dma_restart_cb(void *opaque, int running, int reason)
+static void ahci_dma_restart_cb(void *opaque, int running, RunState state)
 {
 }
 
diff --git a/hw/ide/core.c b/hw/ide/core.c
index 9297b9e..4d5a076 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -527,7 +527,7 @@
         s->bus->dma->ops->set_unit(s->bus->dma, s->unit);
         s->bus->error_status = op;
         bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read);
-        vm_stop(VMSTOP_DISKFULL);
+        vm_stop(RSTATE_IO_ERROR);
     } else {
         if (op & BM_STATUS_DMA_RETRY) {
             dma_buf_commit(s, 0);
@@ -1910,7 +1910,7 @@
     return 0;
 }
 
-static void ide_nop_restart(void *opaque, int x, int y)
+static void ide_nop_restart(void *opaque, int x, RunState y)
 {
 }
 
diff --git a/hw/ide/internal.h b/hw/ide/internal.h
index 233915c..9046e96 100644
--- a/hw/ide/internal.h
+++ b/hw/ide/internal.h
@@ -9,6 +9,7 @@
 #include <hw/ide.h>
 #include "iorange.h"
 #include "dma.h"
+#include "sysemu.h"
 
 /* debug IDE devices */
 //#define DEBUG_IDE
@@ -387,7 +388,7 @@
 typedef void DMAStartFunc(IDEDMA *, IDEState *, BlockDriverCompletionFunc *);
 typedef int DMAFunc(IDEDMA *);
 typedef int DMAIntFunc(IDEDMA *, int);
-typedef void DMARestartFunc(void *, int, int);
+typedef void DMARestartFunc(void *, int, RunState);
 
 struct unreported_events {
     bool eject_request;
diff --git a/hw/ide/pci.c b/hw/ide/pci.c
index 9fded02..f133c42 100644
--- a/hw/ide/pci.c
+++ b/hw/ide/pci.c
@@ -222,7 +222,7 @@
     }
 }
 
-static void bmdma_restart_cb(void *opaque, int running, int reason)
+static void bmdma_restart_cb(void *opaque, int running, RunState state)
 {
     IDEDMA *dma = opaque;
     BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma);
diff --git a/hw/kvmclock.c b/hw/kvmclock.c
index b73aec4..5388bc4 100644
--- a/hw/kvmclock.c
+++ b/hw/kvmclock.c
@@ -46,7 +46,7 @@
      * it on next vmsave (which would return a different value). Will be reset
      * when the VM is continued.
      */
-    s->clock_valid = !vm_running;
+    s->clock_valid = !runstate_is_running();
 }
 
 static int kvmclock_post_load(void *opaque, int version_id)
@@ -59,7 +59,8 @@
     return kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data);
 }
 
-static void kvmclock_vm_state_change(void *opaque, int running, int reason)
+static void kvmclock_vm_state_change(void *opaque, int running,
+                                     RunState state)
 {
     KVMClockState *s = opaque;
 
diff --git a/hw/qxl.c b/hw/qxl.c
index a282d23..6db2f1a 100644
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -1453,10 +1453,11 @@
     }
 }
 
-static void qxl_vm_change_state_handler(void *opaque, int running, int reason)
+static void qxl_vm_change_state_handler(void *opaque, int running,
+                                        RunState state)
 {
     PCIQXLDevice *qxl = opaque;
-    qemu_spice_vm_change_state_handler(&qxl->ssd, running, reason);
+    qemu_spice_vm_change_state_handler(&qxl->ssd, running, state);
 
     if (running) {
         /*
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 4a60820..96c554f 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -217,7 +217,7 @@
         r->status |= SCSI_REQ_STATUS_RETRY | type;
 
         bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read);
-        vm_stop(VMSTOP_DISKFULL);
+        vm_stop(RSTATE_IO_ERROR);
     } else {
         switch (error) {
         case ENOMEM:
@@ -338,7 +338,7 @@
     }
 }
 
-static void scsi_dma_restart_cb(void *opaque, int running, int reason)
+static void scsi_dma_restart_cb(void *opaque, int running, RunState state)
 {
     SCSIDiskState *s = opaque;
 
diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c
index c2ee000..daa8e42 100644
--- a/hw/virtio-blk.c
+++ b/hw/virtio-blk.c
@@ -77,7 +77,7 @@
         req->next = s->rq;
         s->rq = req;
         bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read);
-        vm_stop(VMSTOP_DISKFULL);
+        vm_stop(RSTATE_IO_ERROR);
     } else {
         virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
         bdrv_acct_done(s->bs, &req->acct);
@@ -439,7 +439,8 @@
     virtio_submit_multiwrite(s->bs, &mrb);
 }
 
-static void virtio_blk_dma_restart_cb(void *opaque, int running, int reason)
+static void virtio_blk_dma_restart_cb(void *opaque, int running,
+                                      RunState state)
 {
     VirtIOBlock *s = opaque;
 
diff --git a/hw/virtio.c b/hw/virtio.c
index 946d911..d9bf266 100644
--- a/hw/virtio.c
+++ b/hw/virtio.c
@@ -847,7 +847,7 @@
     g_free(vdev);
 }
 
-static void virtio_vmstate_change(void *opaque, int running, int reason)
+static void virtio_vmstate_change(void *opaque, int running, RunState state)
 {
     VirtIODevice *vdev = opaque;
     bool backend_run = running && (vdev->status & VIRTIO_CONFIG_S_DRIVER_OK);
@@ -880,7 +880,7 @@
     vdev->queue_sel = 0;
     vdev->config_vector = VIRTIO_NO_VECTOR;
     vdev->vq = g_malloc0(sizeof(VirtQueue) * VIRTIO_PCI_QUEUE_MAX);
-    vdev->vm_running = vm_running;
+    vdev->vm_running = runstate_is_running();
     for(i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) {
         vdev->vq[i].vector = VIRTIO_NO_VECTOR;
         vdev->vq[i].vdev = vdev;
diff --git a/hw/watchdog.c b/hw/watchdog.c
index 1c900a1..71c6c7d 100644
--- a/hw/watchdog.c
+++ b/hw/watchdog.c
@@ -132,7 +132,7 @@
 
     case WDT_PAUSE:             /* same as 'stop' command in monitor */
         watchdog_mon_event("pause");
-        vm_stop(VMSTOP_WATCHDOG);
+        vm_stop(RSTATE_WATCHDOG);
         break;
 
     case WDT_DEBUG:
diff --git a/kvm-all.c b/kvm-all.c
index fbb9ff3..228655b 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -1014,7 +1014,7 @@
 
     if (ret < 0) {
         cpu_dump_state(env, stderr, fprintf, CPU_DUMP_CODE);
-        vm_stop(VMSTOP_PANIC);
+        vm_stop(RSTATE_PANICKED);
     }
 
     env->exit_request = 0;
diff --git a/migration.c b/migration.c
index f5959b4..7dd8f4e 100644
--- a/migration.c
+++ b/migration.c
@@ -70,10 +70,11 @@
     qemu_announce_self();
     DPRINTF("successfully loaded vm state\n");
 
-    incoming_expected = false;
-
-    if (autostart)
+    if (autostart) {
         vm_start();
+    } else {
+        runstate_set(RSTATE_PRE_LAUNCH);
+    }
 }
 
 int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data)
@@ -371,10 +372,10 @@
     DPRINTF("iterate\n");
     if (qemu_savevm_state_iterate(s->mon, s->file) == 1) {
         int state;
-        int old_vm_running = vm_running;
+        int old_vm_running = runstate_is_running();
 
         DPRINTF("done iterating\n");
-        vm_stop(VMSTOP_MIGRATE);
+        vm_stop(RSTATE_PRE_MIGRATE);
 
         if ((qemu_savevm_state_complete(s->mon, s->file)) < 0) {
             if (old_vm_running) {
@@ -390,6 +391,9 @@
             }
             state = MIG_STATE_ERROR;
         }
+        if (state == MIG_STATE_COMPLETED) {
+            runstate_set(RSTATE_POST_MIGRATE);
+        }
         s->state = state;
         notifier_list_notify(&migration_state_notifiers, NULL);
     }
diff --git a/monitor.c b/monitor.c
index 03ae997..8ec2c5e 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1293,7 +1293,7 @@
  */
 static int do_stop(Monitor *mon, const QDict *qdict, QObject **ret_data)
 {
-    vm_stop(VMSTOP_USER);
+    vm_stop(RSTATE_PAUSED);
     return 0;
 }
 
@@ -1311,10 +1311,15 @@
 {
     struct bdrv_iterate_context context = { mon, 0 };
 
-    if (incoming_expected) {
+    if (runstate_check(RSTATE_IN_MIGRATE)) {
         qerror_report(QERR_MIGRATION_EXPECTED);
         return -1;
+    } else if (runstate_check(RSTATE_PANICKED) ||
+               runstate_check(RSTATE_SHUTDOWN)) {
+        qerror_report(QERR_RESET_REQUIRED);
+        return -1;
     }
+
     bdrv_iterate(encrypted_bdrv_it, &context);
     /* only resume the vm if all keys are set and valid */
     if (!context.err) {
@@ -2613,6 +2618,7 @@
 static void do_info_status_print(Monitor *mon, const QObject *data)
 {
     QDict *qdict;
+    const char *status;
 
     qdict = qobject_to_qdict(data);
 
@@ -2626,13 +2632,17 @@
         monitor_printf(mon, "paused");
     }
 
+    status = qdict_get_str(qdict, "status");
+    if (strcmp(status, "paused") && strcmp(status, "running")) {
+        monitor_printf(mon, " (%s)", status);
+    }
+
     monitor_printf(mon, "\n");
 }
 
 static void do_info_status(Monitor *mon, QObject **ret_data)
 {
-    *ret_data = qobject_from_jsonf("{ 'running': %i, 'singlestep': %i }",
-                                    vm_running, singlestep);
+    *ret_data = qobject_from_jsonf("{ 'running': %i, 'singlestep': %i, 'status': %s }", runstate_is_running(), singlestep, runstate_as_string());
 }
 
 static qemu_acl *find_acl(Monitor *mon, const char *name)
@@ -2825,10 +2835,10 @@
 
 static void do_loadvm(Monitor *mon, const QDict *qdict)
 {
-    int saved_vm_running  = vm_running;
+    int saved_vm_running  = runstate_is_running();
     const char *name = qdict_get_str(qdict, "name");
 
-    vm_stop(VMSTOP_LOADVM);
+    vm_stop(RSTATE_RESTORE);
 
     if (load_vmstate(name) == 0 && saved_vm_running) {
         vm_start();
diff --git a/qemu-timer.c b/qemu-timer.c
index 46dd483..ad1fc8b 100644
--- a/qemu-timer.c
+++ b/qemu-timer.c
@@ -230,7 +230,7 @@
     int64_t delta;
     static int64_t last_delta;
     /* If the VM is not running, then do nothing.  */
-    if (!vm_running)
+    if (!runstate_is_running())
         return;
 
     cur_time = cpu_get_clock();
@@ -388,7 +388,7 @@
         return;
     }
 
-    if (vm_running) {
+    if (runstate_is_running()) {
         int64_t clock = qemu_get_clock_ns(rt_clock);
         int64_t warp_delta = clock - vm_clock_warp_start;
         if (use_icount == 1) {
@@ -710,7 +710,7 @@
     }
 
     /* vm time timers */
-    if (vm_running) {
+    if (runstate_is_running()) {
         qemu_run_timers(vm_clock);
     }
 
@@ -1116,7 +1116,8 @@
 
 #endif /* _WIN32 */
 
-static void alarm_timer_on_change_state_rearm(void *opaque, int running, int reason)
+static void alarm_timer_on_change_state_rearm(void *opaque, int running,
+                                              RunState state)
 {
     if (running)
         qemu_rearm_alarm_timer((struct qemu_alarm_timer *) opaque);
diff --git a/qerror.c b/qerror.c
index 3d64b80..c591a54 100644
--- a/qerror.c
+++ b/qerror.c
@@ -194,6 +194,10 @@
         .desc      = "QMP input object member '%(member)' is unexpected",
     },
     {
+        .error_fmt = QERR_RESET_REQUIRED,
+        .desc      = "Resetting the Virtual Machine is required",
+    },
+    {
         .error_fmt = QERR_SET_PASSWD_FAILED,
         .desc      = "Could not set password",
     },
diff --git a/qerror.h b/qerror.h
index 8058456..d407001 100644
--- a/qerror.h
+++ b/qerror.h
@@ -163,6 +163,9 @@
 #define QERR_QMP_EXTRA_MEMBER \
     "{ 'class': 'QMPExtraInputObjectMember', 'data': { 'member': %s } }"
 
+#define QERR_RESET_REQUIRED \
+    "{ 'class': 'ResetRequired', 'data': {} }"
+
 #define QERR_SET_PASSWD_FAILED \
     "{ 'class': 'SetPasswdFailed', 'data': {} }"
 
diff --git a/qmp-commands.hx b/qmp-commands.hx
index d1c2c59..d83bce5 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -1573,11 +1573,28 @@
 - "running": true if the VM is running, or false if it is paused (json-bool)
 - "singlestep": true if the VM is in single step mode,
                 false otherwise (json-bool)
+- "status": one of the following values (json-string)
+    "debug" - QEMU is running on a debugger
+    "inmigrate" - guest is paused waiting for an incoming migration
+    "internal-error" - An internal error that prevents further guest
+    execution has occurred
+    "io-error" - the last IOP has failed and the device is configured
+    to pause on I/O errors
+    "paused" - guest has been paused via the 'stop' command
+    "postmigrate" - guest is paused following a successful 'migrate'
+    "prelaunch" - QEMU was started with -S and guest has not started
+    "finish-migrate" - guest is paused to finish the migration process
+    "restore-vm" - guest is paused to restore VM state
+    "running" - guest is actively running
+    "save-vm" - guest is paused to save the VM state
+    "shutdown" - guest is shut down (and -no-shutdown is in use)
+    "watchdog" - the watchdog action is configured to pause and
+     has been triggered
 
 Example:
 
 -> { "execute": "query-status" }
-<- { "return": { "running": true, "singlestep": false } }
+<- { "return": { "running": true, "singlestep": false, "status": "running" } }
 
 EQMP
 
diff --git a/savevm.c b/savevm.c
index 1feaa70..46f2447 100644
--- a/savevm.c
+++ b/savevm.c
@@ -1602,8 +1602,8 @@
     int saved_vm_running;
     int ret;
 
-    saved_vm_running = vm_running;
-    vm_stop(VMSTOP_SAVEVM);
+    saved_vm_running = runstate_is_running();
+    vm_stop(RSTATE_SAVEVM);
 
     if (qemu_savevm_state_blocked(mon)) {
         ret = -EINVAL;
@@ -1931,8 +1931,8 @@
         return;
     }
 
-    saved_vm_running = vm_running;
-    vm_stop(VMSTOP_SAVEVM);
+    saved_vm_running = runstate_is_running();
+    vm_stop(RSTATE_SAVEVM);
 
     memset(sn, 0, sizeof(*sn));
 
diff --git a/sysemu.h b/sysemu.h
index 9090457..43ff546 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -9,42 +9,56 @@
 #include "notify.h"
 
 /* vl.c */
+
+typedef enum {
+    RSTATE_NO_STATE,
+    RSTATE_DEBUG,          /* qemu is running under gdb */
+    RSTATE_IN_MIGRATE,     /* paused waiting for an incoming migration */
+    RSTATE_PANICKED,       /* paused due to an internal error */
+    RSTATE_IO_ERROR,       /* paused due to an I/O error */
+    RSTATE_PAUSED,         /* paused by the user (ie. the 'stop' command) */
+    RSTATE_POST_MIGRATE,   /* paused following a successful migration */
+    RSTATE_PRE_LAUNCH,     /* qemu was started with -S and haven't started */
+    RSTATE_PRE_MIGRATE,    /* paused preparing to finish migrate */
+    RSTATE_RESTORE,        /* paused restoring the VM state */
+    RSTATE_RUNNING,        /* qemu is running */
+    RSTATE_SAVEVM,         /* paused saving VM state */
+    RSTATE_SHUTDOWN,       /* guest shut down and -no-shutdown is in use */
+    RSTATE_WATCHDOG,       /* watchdog fired and qemu is configured to pause */
+    RSTATE_MAX
+} RunState;
+
 extern const char *bios_name;
 
-extern int vm_running;
 extern const char *qemu_name;
 extern uint8_t qemu_uuid[];
 int qemu_uuid_parse(const char *str, uint8_t *uuid);
 #define UUID_FMT "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
 
+void runstate_init(void);
+bool runstate_check(RunState state);
+void runstate_set(RunState new_state);
+int runstate_is_running(void);
+const char *runstate_as_string(void);
 typedef struct vm_change_state_entry VMChangeStateEntry;
-typedef void VMChangeStateHandler(void *opaque, int running, int reason);
+typedef void VMChangeStateHandler(void *opaque, int running, RunState state);
 
 VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb,
                                                      void *opaque);
 void qemu_del_vm_change_state_handler(VMChangeStateEntry *e);
-
-#define VMSTOP_USER      0
-#define VMSTOP_DEBUG     1
-#define VMSTOP_SHUTDOWN  2
-#define VMSTOP_DISKFULL  3
-#define VMSTOP_WATCHDOG  4
-#define VMSTOP_PANIC     5
-#define VMSTOP_SAVEVM    6
-#define VMSTOP_LOADVM    7
-#define VMSTOP_MIGRATE   8
+void vm_state_notify(int running, RunState state);
 
 #define VMRESET_SILENT   false
 #define VMRESET_REPORT   true
 
 void vm_start(void);
-void vm_stop(int reason);
+void vm_stop(RunState state);
 
 void qemu_system_reset_request(void);
 void qemu_system_shutdown_request(void);
 void qemu_system_powerdown_request(void);
 void qemu_system_debug_request(void);
-void qemu_system_vmstop_request(int reason);
+void qemu_system_vmstop_request(RunState reason);
 int qemu_shutdown_requested_get(void);
 int qemu_reset_requested_get(void);
 int qemu_shutdown_requested(void);
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index 22b1dd0..b6eef04 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -334,7 +334,7 @@
     return 0;
 }
 
-static void cpu_update_state(void *opaque, int running, int reason)
+static void cpu_update_state(void *opaque, int running, RunState state)
 {
     CPUState *env = opaque;
 
@@ -1130,7 +1130,7 @@
 
     if (!env->tsc_valid) {
         msrs[n++].index = MSR_IA32_TSC;
-        env->tsc_valid = !vm_running;
+        env->tsc_valid = !runstate_is_running();
     }
 
 #ifdef TARGET_X86_64
diff --git a/ui/sdl.c b/ui/sdl.c
index c7aaedf..8cafc44 100644
--- a/ui/sdl.c
+++ b/ui/sdl.c
@@ -409,7 +409,7 @@
     char icon_title[1024];
     const char *status = "";
 
-    if (!vm_running)
+    if (!runstate_is_running())
         status = " [Stopped]";
     else if (gui_grab) {
         if (alt_grab)
@@ -853,8 +853,8 @@
 {
     SDL_Event ev1, *ev = &ev1;
 
-    if (last_vm_running != vm_running) {
-        last_vm_running = vm_running;
+    if (last_vm_running != runstate_is_running()) {
+        last_vm_running = runstate_is_running();
         sdl_update_caption();
     }
 
diff --git a/ui/spice-display.c b/ui/spice-display.c
index e385361..3a92a12 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -255,7 +255,8 @@
     qemu_spice_destroy_primary_surface(ssd, 0, QXL_SYNC);
 }
 
-void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason)
+void qemu_spice_vm_change_state_handler(void *opaque, int running,
+                                        RunState state)
 {
     SimpleSpiceDisplay *ssd = opaque;
 
diff --git a/ui/spice-display.h b/ui/spice-display.h
index 1388641..5e52df9 100644
--- a/ui/spice-display.h
+++ b/ui/spice-display.h
@@ -22,6 +22,7 @@
 #include "qemu-thread.h"
 #include "console.h"
 #include "pflib.h"
+#include "sysemu.h"
 
 #define NUM_MEMSLOTS 8
 #define MEMSLOT_GENERATION_BITS 8
@@ -88,7 +89,8 @@
 void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd);
 void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd);
 void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd);
-void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason);
+void qemu_spice_vm_change_state_handler(void *opaque, int running,
+                                        RunState state);
 void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd, DisplayState *ds);
 
 void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
diff --git a/vl.c b/vl.c
index 0c2fc3f..bd4a5ce 100644
--- a/vl.c
+++ b/vl.c
@@ -185,9 +185,7 @@
 #endif
 int nb_nics;
 NICInfo nd_table[MAX_NICS];
-int vm_running;
 int autostart;
-int incoming_expected; /* Started with -incoming and waiting for incoming */
 static int rtc_utc = 1;
 static int rtc_date_offset = -1; /* -1 means no change */
 QEMUClock *rtc_clock;
@@ -323,6 +321,120 @@
 }
 
 /***********************************************************/
+/* QEMU state */
+
+static RunState current_run_state = RSTATE_NO_STATE;
+
+typedef struct {
+    RunState from;
+    RunState to;
+} RunStateTransition;
+
+static const RunStateTransition runstate_transitions_def[] = {
+    /*     from      ->     to      */
+    { RSTATE_NO_STATE, RSTATE_RUNNING },
+    { RSTATE_NO_STATE, RSTATE_IN_MIGRATE },
+    { RSTATE_NO_STATE, RSTATE_PRE_LAUNCH },
+
+    { RSTATE_DEBUG, RSTATE_RUNNING },
+
+    { RSTATE_IN_MIGRATE, RSTATE_RUNNING },
+    { RSTATE_IN_MIGRATE, RSTATE_PRE_LAUNCH },
+
+    { RSTATE_PANICKED, RSTATE_PAUSED },
+
+    { RSTATE_IO_ERROR, RSTATE_RUNNING },
+
+    { RSTATE_PAUSED, RSTATE_RUNNING },
+
+    { RSTATE_POST_MIGRATE, RSTATE_RUNNING },
+
+    { RSTATE_PRE_LAUNCH, RSTATE_RUNNING },
+    { RSTATE_PRE_LAUNCH, RSTATE_POST_MIGRATE },
+
+    { RSTATE_PRE_MIGRATE, RSTATE_RUNNING },
+    { RSTATE_PRE_MIGRATE, RSTATE_POST_MIGRATE },
+
+    { RSTATE_RESTORE, RSTATE_RUNNING },
+
+    { RSTATE_RUNNING, RSTATE_DEBUG },
+    { RSTATE_RUNNING, RSTATE_PANICKED },
+    { RSTATE_RUNNING, RSTATE_IO_ERROR },
+    { RSTATE_RUNNING, RSTATE_PAUSED },
+    { RSTATE_RUNNING, RSTATE_PRE_MIGRATE },
+    { RSTATE_RUNNING, RSTATE_RESTORE },
+    { RSTATE_RUNNING, RSTATE_SAVEVM },
+    { RSTATE_RUNNING, RSTATE_SHUTDOWN },
+    { RSTATE_RUNNING, RSTATE_WATCHDOG },
+
+    { RSTATE_SAVEVM, RSTATE_RUNNING },
+
+    { RSTATE_SHUTDOWN, RSTATE_PAUSED },
+
+    { RSTATE_WATCHDOG, RSTATE_RUNNING },
+
+    { RSTATE_MAX, RSTATE_MAX },
+};
+
+static bool runstate_valid_transitions[RSTATE_MAX][RSTATE_MAX];
+
+static const char *const runstate_name_tbl[RSTATE_MAX] = {
+    [RSTATE_DEBUG] = "debug",
+    [RSTATE_IN_MIGRATE] = "incoming-migration",
+    [RSTATE_PANICKED] = "internal-error",
+    [RSTATE_IO_ERROR] = "io-error",
+    [RSTATE_PAUSED] = "paused",
+    [RSTATE_POST_MIGRATE] = "post-migrate",
+    [RSTATE_PRE_LAUNCH] = "prelaunch",
+    [RSTATE_PRE_MIGRATE] = "finish-migrate",
+    [RSTATE_RESTORE] = "restore-vm",
+    [RSTATE_RUNNING] = "running",
+    [RSTATE_SAVEVM] = "save-vm",
+    [RSTATE_SHUTDOWN] = "shutdown",
+    [RSTATE_WATCHDOG] = "watchdog",
+};
+
+bool runstate_check(RunState state)
+{
+    return current_run_state == state;
+}
+
+void runstate_init(void)
+{
+    const RunStateTransition *p;
+
+    memset(&runstate_valid_transitions, 0, sizeof(runstate_valid_transitions));
+
+    for (p = &runstate_transitions_def[0]; p->from != RSTATE_MAX; p++) {
+        runstate_valid_transitions[p->from][p->to] = true;
+    }
+}
+
+/* This function will abort() on invalid state transitions */
+void runstate_set(RunState new_state)
+{
+    if (new_state >= RSTATE_MAX ||
+        !runstate_valid_transitions[current_run_state][new_state]) {
+        fprintf(stderr, "invalid runstate transition\n");
+        abort();
+    }
+
+    current_run_state = new_state;
+}
+
+const char *runstate_as_string(void)
+{
+    assert(current_run_state > RSTATE_NO_STATE &&
+           current_run_state < RSTATE_MAX);
+    return runstate_name_tbl[current_run_state];
+}
+
+int runstate_is_running(void)
+{
+    return runstate_check(RSTATE_RUNNING);
+}
+
+/***********************************************************/
 /* real time host monotonic timer */
 
 /***********************************************************/
@@ -1145,23 +1257,23 @@
     g_free (e);
 }
 
-void vm_state_notify(int running, int reason)
+void vm_state_notify(int running, RunState state)
 {
     VMChangeStateEntry *e;
 
-    trace_vm_state_notify(running, reason);
+    trace_vm_state_notify(running, state);
 
     for (e = vm_change_state_head.lh_first; e; e = e->entries.le_next) {
-        e->cb(e->opaque, running, reason);
+        e->cb(e->opaque, running, state);
     }
 }
 
 void vm_start(void)
 {
-    if (!vm_running) {
+    if (!runstate_is_running()) {
         cpu_enable_ticks();
-        vm_running = 1;
-        vm_state_notify(1, 0);
+        runstate_set(RSTATE_RUNNING);
+        vm_state_notify(1, RSTATE_RUNNING);
         resume_all_vcpus();
         monitor_protocol_event(QEVENT_RESUME, NULL);
     }
@@ -1182,7 +1294,7 @@
 static pid_t shutdown_pid;
 static int powerdown_requested;
 static int debug_requested;
-static int vmstop_requested;
+static RunState vmstop_requested = RSTATE_NO_STATE;
 
 int qemu_shutdown_requested_get(void)
 {
@@ -1238,11 +1350,11 @@
     return r;
 }
 
-static int qemu_vmstop_requested(void)
+static RunState qemu_vmstop_requested(void)
 {
-    int r = vmstop_requested;
-    vmstop_requested = 0;
-    return r;
+    RunState s = vmstop_requested;
+    vmstop_requested = RSTATE_NO_STATE;
+    return s;
 }
 
 void qemu_register_reset(QEMUResetHandler *func, void *opaque)
@@ -1318,9 +1430,9 @@
     qemu_notify_event();
 }
 
-void qemu_system_vmstop_request(int reason)
+void qemu_system_vmstop_request(RunState state)
 {
-    vmstop_requested = reason;
+    vmstop_requested = state;
     qemu_notify_event();
 }
 
@@ -1470,13 +1582,13 @@
 #endif
 
         if (qemu_debug_requested()) {
-            vm_stop(VMSTOP_DEBUG);
+            vm_stop(RSTATE_DEBUG);
         }
         if (qemu_shutdown_requested()) {
             qemu_kill_report();
             monitor_protocol_event(QEVENT_SHUTDOWN, NULL);
             if (no_shutdown) {
-                vm_stop(VMSTOP_SHUTDOWN);
+                vm_stop(RSTATE_SHUTDOWN);
             } else
                 break;
         }
@@ -1485,6 +1597,10 @@
             cpu_synchronize_all_states();
             qemu_system_reset(VMRESET_REPORT);
             resume_all_vcpus();
+            if (runstate_check(RSTATE_PANICKED) ||
+                runstate_check(RSTATE_SHUTDOWN)) {
+                runstate_set(RSTATE_PAUSED);
+            }
         }
         if (qemu_powerdown_requested()) {
             monitor_protocol_event(QEVENT_POWERDOWN, NULL);
@@ -2203,6 +2319,8 @@
     g_mem_set_vtable(&mem_trace);
     g_thread_init(NULL);
 
+    runstate_init();
+
     init_clocks();
 
     qemu_cache_utils_init(envp);
@@ -2953,7 +3071,6 @@
                 break;
             case QEMU_OPTION_incoming:
                 incoming = optarg;
-                incoming_expected = true;
                 break;
             case QEMU_OPTION_nodefaults:
                 default_serial = 0;
@@ -3439,6 +3556,7 @@
     }
 
     if (incoming) {
+        runstate_set(RSTATE_IN_MIGRATE);
         int ret = qemu_start_incoming_migration(incoming);
         if (ret < 0) {
             fprintf(stderr, "Migration failed. Exit code %s(%d), exiting.\n",
@@ -3447,6 +3565,8 @@
         }
     } else if (autostart) {
         vm_start();
+    } else {
+        runstate_set(RSTATE_PRE_LAUNCH);
     }
 
     os_setup_post();
diff --git a/xen-all.c b/xen-all.c
index 1bc2c3c..b5e28ab 100644
--- a/xen-all.c
+++ b/xen-all.c
@@ -736,7 +736,7 @@
          * guest resumes and does a hlt with interrupts disabled which
          * causes Xen to powerdown the domain.
          */
-        if (vm_running) {
+        if (runstate_is_running()) {
             if (qemu_shutdown_requested_get()) {
                 destroy_hvm_domain();
             }
@@ -846,7 +846,8 @@
 
 /* Initialise Xen */
 
-static void xen_change_state_handler(void *opaque, int running, int reason)
+static void xen_change_state_handler(void *opaque, int running,
+                                     RunState state)
 {
     if (running) {
         /* record state running */
@@ -854,11 +855,12 @@
     }
 }
 
-static void xen_hvm_change_state_handler(void *opaque, int running, int reason)
+static void xen_hvm_change_state_handler(void *opaque, int running,
+                                         RunState rstate)
 {
-    XenIOState *state = opaque;
+    XenIOState *xstate = opaque;
     if (running) {
-        xen_main_loop_prepare(state);
+        xen_main_loop_prepare(xstate);
     }
 }