--- /dev/null
+Index: new/qapi-schema.json
+===================================================================
+--- new.orig/qapi-schema.json 2012-09-21 11:10:23.000000000 +0200
++++ new/qapi-schema.json 2012-09-21 12:58:47.000000000 +0200
+@@ -358,6 +358,40 @@
+ '*total-time': 'int'} }
+
+ ##
++# @SaveVMInfo
++#
++# Information about current migration process.
++#
++# @status: #optional string describing the current savevm status.
++# This can be 'active', 'completed', 'failed'.
++# If this field is not returned, no savevm process
++# has been initiated
++#
++# @error: #optional string containing error message is status is failed.
++#
++# @total-time: #optional total amount of milliseconds since savevm started.
++# If savevm has ended, it returns the total save time
++#
++# @bytes: #optional total amount of data transfered
++#
++# Since: 1.3
++##
++{ 'type': 'SaveVMInfo',
++ 'data': {'*status': 'str', '*error': 'str',
++ '*total-time': 'int', '*bytes': 'int'} }
++
++##
++# @query-savevm
++#
++# Returns information about current savevm process.
++#
++# Returns: @SaveVMInfo
++#
++# Since: 1.3
++##
++{ 'command': 'query-savevm', 'returns': 'SaveVMInfo' }
++
++##
+ # @query-migrate
+ #
+ # Returns information about current migration process.
+@@ -2493,3 +2527,12 @@
+ # Since: 1.2.0
+ ##
+ { 'command': 'query-target', 'returns': 'TargetInfo' }
++
++
++{ 'command': 'snapshot-start' 'data': { '*statefile': 'str' } }
++
++{ 'command': 'snapshot-drive', 'data': { 'device': 'str', 'name': 'str' } }
++
++{ 'command': 'delete-drive-snapshot', 'data': { 'device': 'str', 'name': 'str' } }
++
++{ 'command': 'snapshot-end' }
+Index: new/qmp-commands.hx
+===================================================================
+--- new.orig/qmp-commands.hx 2012-09-21 11:10:23.000000000 +0200
++++ new/qmp-commands.hx 2012-09-21 11:11:22.000000000 +0200
+@@ -2514,3 +2514,27 @@
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_target,
+ },
++
++ {
++ .name = "snapshot-start",
++ .args_type = "statefile:s?",
++ .mhandler.cmd_new = qmp_marshal_input_snapshot_start,
++ },
++
++ {
++ .name = "snapshot-drive",
++ .args_type = "device:s,name:s",
++ .mhandler.cmd_new = qmp_marshal_input_snapshot_drive,
++ },
++
++ {
++ .name = "delete-drive-snapshot",
++ .args_type = "device:s,name:s",
++ .mhandler.cmd_new = qmp_marshal_input_delete_drive_snapshot,
++ },
++
++ {
++ .name = "snapshot-end",
++ .args_type = "",
++ .mhandler.cmd_new = qmp_marshal_input_snapshot_end,
++ },
+Index: new/hmp.c
+===================================================================
+--- new.orig/hmp.c 2012-09-21 11:10:23.000000000 +0200
++++ new/hmp.c 2012-09-21 12:59:44.000000000 +0200
+@@ -1102,3 +1102,60 @@
+ qmp_closefd(fdname, &errp);
+ hmp_handle_error(mon, &errp);
+ }
++
++void hmp_snapshot_start(Monitor *mon, const QDict *qdict)
++{
++ Error *errp = NULL;
++ const char *statefile = qdict_get_try_str(qdict, "statefile");
++
++ qmp_snapshot_start(statefile != NULL, statefile, &errp);
++ hmp_handle_error(mon, &errp);
++}
++
++void hmp_snapshot_drive(Monitor *mon, const QDict *qdict)
++{
++ Error *errp = NULL;
++ const char *name = qdict_get_str(qdict, "name");
++ const char *device = qdict_get_str(qdict, "device");
++
++ qmp_snapshot_drive(device, name, &errp);
++ hmp_handle_error(mon, &errp);
++}
++
++void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict)
++{
++ Error *errp = NULL;
++ const char *name = qdict_get_str(qdict, "name");
++ const char *device = qdict_get_str(qdict, "device");
++
++ qmp_delete_drive_snapshot(device, name, &errp);
++ hmp_handle_error(mon, &errp);
++}
++
++void hmp_snapshot_end(Monitor *mon, const QDict *qdict)
++{
++ Error *errp = NULL;
++
++ qmp_snapshot_end(&errp);
++ hmp_handle_error(mon, &errp);
++}
++
++void hmp_info_savevm(Monitor *mon)
++{
++ SaveVMInfo *info;
++ info = qmp_query_savevm(NULL);
++
++ if (info->has_status) {
++ monitor_printf(mon, "savevm status: %s\n", info->status);
++ monitor_printf(mon, "total time: %" PRIu64 " milliseconds\n",
++ info->total_time);
++ } else {
++ monitor_printf(mon, "savevm status: not running\n");
++ }
++ if (info->has_bytes) {
++ monitor_printf(mon, "Bytes saved: %"PRIu64"\n", info->bytes);
++ }
++ if (info->has_error) {
++ monitor_printf(mon, "Error: %s\n", info->error);
++ }
++}
+Index: new/hmp.h
+===================================================================
+--- new.orig/hmp.h 2012-09-21 11:10:23.000000000 +0200
++++ new/hmp.h 2012-09-21 12:35:32.000000000 +0200
+@@ -25,6 +25,7 @@
+ void hmp_info_uuid(Monitor *mon);
+ void hmp_info_chardev(Monitor *mon);
+ void hmp_info_mice(Monitor *mon);
++void hmp_info_savevm(Monitor *mon);
+ void hmp_info_migrate(Monitor *mon);
+ void hmp_info_migrate_capabilities(Monitor *mon);
+ void hmp_info_migrate_cache_size(Monitor *mon);
+@@ -71,5 +72,9 @@
+ void hmp_netdev_del(Monitor *mon, const QDict *qdict);
+ void hmp_getfd(Monitor *mon, const QDict *qdict);
+ void hmp_closefd(Monitor *mon, const QDict *qdict);
++void hmp_snapshot_start(Monitor *mon, const QDict *qdict);
++void hmp_snapshot_drive(Monitor *mon, const QDict *qdict);
++void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict);
++void hmp_snapshot_end(Monitor *mon, const QDict *qdict);
+
+ #endif
+Index: new/hmp-commands.hx
+===================================================================
+--- new.orig/hmp-commands.hx 2012-09-21 11:10:23.000000000 +0200
++++ new/hmp-commands.hx 2012-09-21 12:30:35.000000000 +0200
+@@ -1468,6 +1468,8 @@
+ show current migration capabilities
+ @item info migrate_cache_size
+ show current migration XBZRLE cache size
++@item info savevm
++show savevm status
+ @item info balloon
+ show balloon information
+ @item info qtree
+@@ -1494,3 +1496,35 @@
+ STEXI
+ @end table
+ ETEXI
++
++ {
++ .name = "snapshot-start",
++ .args_type = "statefile:s?",
++ .params = "[statefile]",
++ .help = "Prepare for snapshot and halt VM. Save VM state to statefile.",
++ .mhandler.cmd = hmp_snapshot_start,
++ },
++
++ {
++ .name = "snapshot-drive",
++ .args_type = "device:s,name:s",
++ .params = "device name",
++ .help = "Create internal snapshot.",
++ .mhandler.cmd = hmp_snapshot_drive,
++ },
++
++ {
++ .name = "delete-drive-snapshot",
++ .args_type = "device:s,name:s",
++ .params = "device name",
++ .help = "Delete internal snapshot.",
++ .mhandler.cmd = hmp_delete_drive_snapshot,
++ },
++
++ {
++ .name = "snapshot-end",
++ .args_type = "",
++ .params = "",
++ .help = "Resume VM after snaphot.",
++ .mhandler.cmd = hmp_snapshot_end,
++ },
+Index: new/savevm-async.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ new/savevm-async.c 2012-09-21 13:42:14.000000000 +0200
+@@ -0,0 +1,441 @@
++#include "qemu-common.h"
++#include "qerror.h"
++#include "sysemu.h"
++#include "qmp-commands.h"
++#include "blockdev.h"
++#include "qemu/qom-qobject.h"
++#include "buffered_file.h"
++#include "migration.h"
++
++//#define DEBUG_SAVEVM_STATE
++
++#ifdef DEBUG_SAVEVM_STATE
++#define DPRINTF(fmt, ...) \
++ do { printf("savevm-async: " fmt, ## __VA_ARGS__); } while (0)
++#else
++#define DPRINTF(fmt, ...) \
++ do { } while (0)
++#endif
++
++enum {
++ SAVE_STATE_DONE,
++ SAVE_STATE_ERROR,
++ SAVE_STATE_ACTIVE,
++ SAVE_STATE_COMPLETED,
++};
++
++static struct SnapshotState {
++ BlockDriverState *bs;
++ size_t bs_pos;
++ int state;
++ Error *error;
++ int saved_vm_running;
++ QEMUFile *file;
++ int64_t total_time;
++} snap_state;
++
++SaveVMInfo *qmp_query_savevm(Error **errp)
++{
++ SaveVMInfo *info = g_malloc0(sizeof(*info));
++ struct SnapshotState *s = &snap_state;
++
++ if (s->state != SAVE_STATE_DONE) {
++ info->has_bytes = true;
++ info->bytes = s->bs_pos;
++ switch (s->state) {
++ case SAVE_STATE_ERROR:
++ info->has_status = true;
++ info->status = g_strdup("failed");
++ info->has_total_time = true;
++ info->total_time = s->total_time;
++ if (s->error) {
++ info->has_error = true;
++ info->error = g_strdup(error_get_pretty(s->error));
++ }
++ break;
++ case SAVE_STATE_ACTIVE:
++ info->has_status = true;
++ info->status = g_strdup("active");
++ info->has_total_time = true;
++ info->total_time = qemu_get_clock_ms(rt_clock)
++ - s->total_time;
++ break;
++ case SAVE_STATE_COMPLETED:
++ info->has_status = true;
++ info->status = g_strdup("completed");
++ info->has_total_time = true;
++ info->total_time = s->total_time;
++ break;
++ }
++ }
++
++ return info;
++}
++
++static int save_snapshot_cleanup(void)
++{
++ int ret = 0;
++
++ DPRINTF("save_snapshot_cleanup\n");
++
++ snap_state.total_time = qemu_get_clock_ms(rt_clock) -
++ snap_state.total_time;
++
++ if (snap_state.file) {
++ ret = qemu_fclose(snap_state.file);
++ }
++
++ if (snap_state.bs) {
++ // try to truncate, but ignore errors (will fail on block devices).
++ // note: bdrv_read() need whole blocks, so we round up
++ size_t size = (snap_state.bs_pos + BDRV_SECTOR_SIZE) & BDRV_SECTOR_MASK;
++ bdrv_truncate(snap_state.bs, size);
++
++ bdrv_delete(snap_state.bs);
++ snap_state.bs = NULL;
++ }
++
++ return ret;
++}
++
++static void save_snapshot_error(const char *fmt, ...)
++{
++ va_list ap;
++ char *msg;
++
++ va_start(ap, fmt);
++ msg = g_strdup_vprintf(fmt, ap);
++ va_end(ap);
++
++ DPRINTF("save_snapshot_error: %s\n", msg);
++
++ if (!snap_state.error) {
++ error_set(&snap_state.error, ERROR_CLASS_GENERIC_ERROR, "%s", msg);
++ }
++
++ g_free (msg);
++
++ snap_state.state = SAVE_STATE_ERROR;
++
++ save_snapshot_cleanup();
++}
++
++static void save_snapshot_completed(void)
++{
++ DPRINTF("save_snapshot_completed\n");
++
++ if (save_snapshot_cleanup() < 0) {
++ snap_state.state = SAVE_STATE_ERROR;
++ } else {
++ snap_state.state = SAVE_STATE_COMPLETED;
++ }
++}
++
++static int block_state_close(void *opaque)
++{
++ snap_state.file = NULL;
++ return bdrv_flush(snap_state.bs);
++}
++
++static ssize_t block_state_put_buffer(void *opaque, const void *buf,
++ size_t size)
++{
++ int ret;
++
++ if ((ret = bdrv_pwrite(snap_state.bs, snap_state.bs_pos, buf, size)) > 0) {
++ snap_state.bs_pos += ret;
++ }
++
++ return ret;
++}
++
++static void block_state_put_ready(void *opaque)
++{
++ int ret;
++
++ if (snap_state.state != SAVE_STATE_ACTIVE) {
++ save_snapshot_error("put_ready returning because of non-active state");
++ return;
++ }
++
++ if (!runstate_check(RUN_STATE_SAVE_VM)) {
++ save_snapshot_error("put_ready returning because of wrong run state");
++ return;
++ }
++
++ ret = qemu_savevm_state_iterate(snap_state.file);
++ if (ret < 0) {
++ save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
++ return;
++ } else if (ret == 1) {
++ DPRINTF("savevm inerate finished\n");
++ if ((ret = qemu_savevm_state_complete(snap_state.file)) < 0) {
++ save_snapshot_error("qemu_savevm_state_complete error %d", ret);
++ return;
++ } else {
++ DPRINTF("save complete\n");
++ save_snapshot_completed();
++ return;
++ }
++ }
++}
++
++static void block_state_wait_for_unfreeze(void *opaque)
++{
++ /* do nothing here - should not be called */
++}
++
++void qmp_snapshot_start(bool has_statefile, const char *statefile, Error **errp)
++{
++ BlockDriver *drv = NULL;
++ int bdrv_oflags = BDRV_O_CACHE_WB | BDRV_O_RDWR;
++ MigrationParams params = {
++ .blk = 0,
++ .shared = 0
++ };
++ int ret;
++
++ if (snap_state.state != SAVE_STATE_DONE) {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
++ "VM snapshot already started\n");
++ return;
++ }
++
++ /* initialize snapshot info */
++ snap_state.saved_vm_running = runstate_is_running();
++ snap_state.bs_pos = 0;
++ snap_state.total_time = qemu_get_clock_ms(rt_clock);
++
++ if (snap_state.error) {
++ error_free(snap_state.error);
++ snap_state.error = NULL;
++ }
++
++ /* stop the VM */
++ vm_stop(RUN_STATE_SAVE_VM);
++
++ if (!has_statefile) {
++ snap_state.state = SAVE_STATE_COMPLETED;
++ return;
++ }
++
++ if (qemu_savevm_state_blocked(errp)) {
++ return;
++ }
++
++ /* Open the image */
++ snap_state.bs = bdrv_new("vmstate");
++ ret = bdrv_open(snap_state.bs, statefile, bdrv_oflags, drv);
++ if (ret < 0) {
++ error_set(errp, QERR_OPEN_FILE_FAILED, statefile);
++ goto restart;
++ }
++
++ snap_state.file = qemu_fopen_ops_buffered(&snap_state, 1000, //000000,
++ block_state_put_buffer,
++ block_state_put_ready,
++ block_state_wait_for_unfreeze,
++ block_state_close);
++
++ if (!snap_state.file) {
++ error_set(errp, QERR_OPEN_FILE_FAILED, statefile);
++ goto restart;
++ }
++
++ snap_state.state = SAVE_STATE_ACTIVE;
++
++ ret = qemu_savevm_state_begin(snap_state.file, ¶ms);
++ if (ret < 0) {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
++ "qemu_savevm_state_begin failed\n");
++ goto restart;
++ }
++
++ block_state_put_ready(&snap_state);
++
++ return;
++
++restart:
++
++ save_snapshot_error("setup failed");
++
++ if (snap_state.saved_vm_running) {
++ vm_start();
++ }
++}
++
++void qmp_snapshot_end(Error **errp)
++{
++ if (snap_state.state == SAVE_STATE_DONE) {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
++ "VM snapshot not started\n");
++ return;
++ }
++
++ if (snap_state.saved_vm_running) {
++ vm_start();
++ }
++
++ snap_state.state = SAVE_STATE_DONE;
++}
++
++void qmp_snapshot_drive(const char *device, const char *name, Error **errp)
++{
++ BlockDriverState *bs;
++ QEMUSnapshotInfo sn1, *sn = &sn1;
++ int ret;
++#ifdef _WIN32
++ struct _timeb tb;
++#else
++ struct timeval tv;
++#endif
++
++ if (snap_state.state != SAVE_STATE_COMPLETED) {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
++ "VM snapshot not ready/started\n");
++ return;
++ }
++
++ bs = bdrv_find(device);
++ if (!bs) {
++ error_set(errp, QERR_DEVICE_NOT_FOUND, device);
++ return;
++ }
++
++ if (!bdrv_is_inserted(bs)) {
++ error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
++ return;
++ }
++
++ if (bdrv_is_read_only(bs)) {
++ error_set(errp, QERR_DEVICE_IS_READ_ONLY, device);
++ return;
++ }
++
++ if (!bdrv_can_snapshot(bs)) {
++ error_set(errp, QERR_NOT_SUPPORTED);
++ return;
++ }
++
++ if (bdrv_snapshot_find(bs, sn, name) >= 0) {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
++ "snapshot '%s' already exists", name);
++ return;
++ }
++
++ sn = &sn1;
++ memset(sn, 0, sizeof(*sn));
++
++#ifdef _WIN32
++ _ftime(&tb);
++ sn->date_sec = tb.time;
++ sn->date_nsec = tb.millitm * 1000000;
++#else
++ gettimeofday(&tv, NULL);
++ sn->date_sec = tv.tv_sec;
++ sn->date_nsec = tv.tv_usec * 1000;
++#endif
++ sn->vm_clock_nsec = qemu_get_clock_ns(vm_clock);
++
++ pstrcpy(sn->name, sizeof(sn->name), name);
++
++ sn->vm_state_size = 0; /* do not save state */
++
++ ret = bdrv_snapshot_create(bs, sn);
++ if (ret < 0) {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
++ "Error while creating snapshot on '%s'\n", device);
++ return;
++ }
++}
++
++void qmp_delete_drive_snapshot(const char *device, const char *name,
++ Error **errp)
++{
++ BlockDriverState *bs;
++ QEMUSnapshotInfo sn1, *sn = &sn1;
++ int ret;
++
++ bs = bdrv_find(device);
++ if (!bs) {
++ error_set(errp, QERR_DEVICE_NOT_FOUND, device);
++ return;
++ }
++ if (bdrv_is_read_only(bs)) {
++ error_set(errp, QERR_DEVICE_IS_READ_ONLY, device);
++ return;
++ }
++
++ if (!bdrv_can_snapshot(bs)) {
++ error_set(errp, QERR_NOT_SUPPORTED);
++ return;
++ }
++
++ if (bdrv_snapshot_find(bs, sn, name) < 0) {
++ /* return success if snapshot does not exists */
++ return;
++ }
++
++ ret = bdrv_snapshot_delete(bs, name);
++ if (ret < 0) {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
++ "Error while deleting snapshot on '%s'\n", device);
++ return;
++ }
++}
++
++static int loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
++{
++ BlockDriverState *bs = (BlockDriverState *)opaque;
++ int64_t maxlen = bdrv_getlength(bs);
++ if (pos > maxlen) {
++ return -EIO;
++ }
++ if ((pos + size) > maxlen) {
++ size = maxlen - pos - 1;
++ }
++ if (size == 0) {
++ return 0;
++ }
++ return bdrv_pread(bs, pos, buf, size);
++}
++
++int load_state_from_blockdev(const char *filename)
++{
++ BlockDriverState *bs = NULL;
++ BlockDriver *drv = NULL;
++ QEMUFile *f;
++ int ret = -1;
++
++ bs = bdrv_new("vmstate");
++ ret = bdrv_open(bs, filename, BDRV_O_CACHE_WB, drv);
++ if (ret < 0) {
++ error_report("Could not open VM state file");
++ goto the_end;
++ }
++
++ /* restore the VM state */
++ f = qemu_fopen_ops(bs, NULL, loadstate_get_buffer, NULL, NULL, NULL, NULL);
++ if (!f) {
++ error_report("Could not open VM state file");
++ ret = -EINVAL;
++ goto the_end;
++ }
++
++ qemu_system_reset(VMRESET_SILENT);
++ ret = qemu_loadvm_state(f);
++
++ qemu_fclose(f);
++ if (ret < 0) {
++ error_report("Error %d while loading VM state", ret);
++ goto the_end;
++ }
++
++ ret = 0;
++
++ the_end:
++ if (bs) {
++ bdrv_delete(bs);
++ }
++ return ret;
++}
+Index: new/Makefile.objs
+===================================================================
+--- new.orig/Makefile.objs 2012-09-21 11:10:23.000000000 +0200
++++ new/Makefile.objs 2012-09-21 11:11:22.000000000 +0200
+@@ -78,6 +78,7 @@
+ common-obj-y += pflib.o
+ common-obj-y += bitmap.o bitops.o
+ common-obj-y += page_cache.o
++common-obj-y += savevm-async.o
+
+ common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
+ common-obj-$(CONFIG_WIN32) += version.o
+Index: new/sysemu.h
+===================================================================
+--- new.orig/sysemu.h 2012-09-21 13:30:06.000000000 +0200
++++ new/sysemu.h 2012-09-21 13:30:36.000000000 +0200
+@@ -72,6 +72,7 @@
+
+ void do_savevm(Monitor *mon, const QDict *qdict);
+ int load_vmstate(const char *name);
++int load_state_from_blockdev(const char *filename);
+ void do_delvm(Monitor *mon, const QDict *qdict);
+ void do_info_snapshots(Monitor *mon);
+
+Index: new/qemu-options.hx
+===================================================================
+--- new.orig/qemu-options.hx 2012-09-21 13:31:18.000000000 +0200
++++ new/qemu-options.hx 2012-09-21 13:32:02.000000000 +0200
+@@ -2477,6 +2477,19 @@
+ Start right away with a saved state (@code{loadvm} in monitor)
+ ETEXI
+
++DEF("loadstate", HAS_ARG, QEMU_OPTION_loadstate, \
++ "-loadstate file\n" \
++ " start right away with a saved state\n",
++ QEMU_ARCH_ALL)
++STEXI
++@item -loadstate @var{file}
++@findex -loadstate
++Start right away with a saved state. This option does not rollback
++disk state like @code{loadvm}, so user must make sure that disk
++have correct state. @var{file} can be any valid device URL. See the section
++for "Device URL Syntax" for more information.
++ETEXI
++
+ #ifndef _WIN32
+ DEF("daemonize", 0, QEMU_OPTION_daemonize, \
+ "-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL)
+Index: new/vl.c
+===================================================================
+--- new.orig/vl.c 2012-09-21 13:32:39.000000000 +0200
++++ new/vl.c 2012-09-21 13:34:30.000000000 +0200
+@@ -2364,6 +2364,7 @@
+ int optind;
+ const char *optarg;
+ const char *loadvm = NULL;
++ const char *loadstate = NULL;
+ QEMUMachine *machine;
+ const char *cpu_model;
+ const char *vga_model = "none";
+@@ -2998,6 +2999,9 @@
+ case QEMU_OPTION_loadvm:
+ loadvm = optarg;
+ break;
++ case QEMU_OPTION_loadstate:
++ loadstate = optarg;
++ break;
+ case QEMU_OPTION_full_screen:
+ full_screen = 1;
+ break;
+@@ -3821,6 +3825,10 @@
+ if (load_vmstate(loadvm) < 0) {
+ autostart = 0;
+ }
++ } else if (loadstate) {
++ if (load_state_from_blockdev(loadstate) < 0) {
++ autostart = 0;
++ }
+ }
+
+ if (incoming) {