--- /dev/null
+= Save Devices =
+
+QEMU has code to load/save the state of the guest that it is running.
+These are two complementary operations. Saving the state just does
+that, saves the state for each device that the guest is running.
+
+These operations are normally used with migration (see migration.txt),
+however it is also possible to save the state of all devices to file,
+without saving the RAM or the block devices of the VM.
+
+This operation is called "xen-save-devices-state" (see
+QMP/qmp-commands.txt)
+
+
+The binary format used in the file is the following:
+
+
+-------------------------------------------
+
+32 bit big endian: QEMU_VM_FILE_MAGIC
+32 bit big endian: QEMU_VM_FILE_VERSION
+
+for_each_device
+{
+ 8 bit: QEMU_VM_SECTION_FULL
+ 32 bit big endian: section_id
+ 8 bit: idstr (ID string) length
+ string: idstr (ID string)
+ 32 bit big endian: instance_id
+ 32 bit big endian: version_id
+ buffer: device specific data
+}
+
+8 bit: QEMU_VM_EOF
}
s->vga.cr[0x27] = s->device_id;
- /* Win2K seems to assume that the pattern buffer is at 0xff
- initially ! */
- memset(s->vga.vram_ptr, 0xff, s->real_vram_size);
-
s->cirrus_hidden_dac_lockindex = 5;
s->cirrus_hidden_dac_data = 0;
}
##
{ 'command': 'migrate',
'data': {'uri': 'str', '*blk': 'bool', '*inc': 'bool', '*detach': 'bool' } }
+
+# @xen-save-devices-state:
+#
+# Save the state of all devices to file. The RAM and the block devices
+# of the VM are not saved by this command.
+#
+# @filename: the file to save the state of the devices to as binary
+# data. See xen-save-devices-state.txt for a description of the binary
+# format.
+#
+# Returns: Nothing on success
+# If @filename cannot be opened, OpenFileFailed
+# If an I/O error occurs while writing the file, IOError
+#
+# Since: 1.1
+##
+{ 'command': 'xen-save-devices-state', 'data': {'filename': 'str'} }
Note: inject-nmi is only supported for x86 guest currently, it will
returns "Unsupported" error for non-x86 guest.
+EQMP
+
+ {
+ .name = "xen-save-devices-state",
+ .args_type = "filename:F",
+ .mhandler.cmd_new = qmp_marshal_input_xen_save_devices_state,
+ },
+
+SQMP
+xen-save-devices-state
+-------
+
+Save the state of all devices to file. The RAM and the block devices
+of the VM are not saved by this command.
+
+Arguments:
+
+- "filename": the file to save the state of the devices to as binary
+data. See xen-save-devices-state.txt for a description of the binary
+format.
+
+Example:
+
+-> { "execute": "xen-save-devices-state",
+ "arguments": { "filename": "/tmp/save" } }
+<- { "return": {} }
+
EQMP
{
#include "qemu-timer.h"
#include "cpus.h"
#include "memory.h"
+#include "qmp-commands.h"
#define SELF_ANNOUNCE_ROUNDS 5
void *opaque;
CompatEntry *compat;
int no_migrate;
+ int is_ram;
} SaveStateEntry;
se->opaque = opaque;
se->vmsd = NULL;
se->no_migrate = 0;
+ /* if this is a live_savem then set is_ram */
+ if (save_live_state != NULL) {
+ se->is_ram = 1;
+ }
if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) {
char *id = dev->parent_bus->info->get_dev_path(dev);
return ret;
}
+static int qemu_save_device_state(QEMUFile *f)
+{
+ SaveStateEntry *se;
+
+ qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
+ qemu_put_be32(f, QEMU_VM_FILE_VERSION);
+
+ cpu_synchronize_all_states();
+
+ QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ int len;
+
+ if (se->is_ram) {
+ continue;
+ }
+ if (se->save_state == NULL && se->vmsd == NULL) {
+ continue;
+ }
+
+ /* Section type */
+ qemu_put_byte(f, QEMU_VM_SECTION_FULL);
+ qemu_put_be32(f, se->section_id);
+
+ /* ID string */
+ len = strlen(se->idstr);
+ qemu_put_byte(f, len);
+ qemu_put_buffer(f, (uint8_t *)se->idstr, len);
+
+ qemu_put_be32(f, se->instance_id);
+ qemu_put_be32(f, se->version_id);
+
+ vmstate_save(f, se);
+ }
+
+ qemu_put_byte(f, QEMU_VM_EOF);
+
+ return qemu_file_get_error(f);
+}
+
static SaveStateEntry *find_se(const char *idstr, int instance_id)
{
SaveStateEntry *se;
vm_start();
}
+void qmp_xen_save_devices_state(const char *filename, Error **errp)
+{
+ QEMUFile *f;
+ int saved_vm_running;
+ int ret;
+
+ saved_vm_running = runstate_is_running();
+ vm_stop(RUN_STATE_SAVE_VM);
+
+ f = qemu_fopen(filename, "wb");
+ if (!f) {
+ error_set(errp, QERR_OPEN_FILE_FAILED, filename);
+ goto the_end;
+ }
+ ret = qemu_save_device_state(f);
+ qemu_fclose(f);
+ if (ret < 0) {
+ error_set(errp, QERR_IO_ERROR);
+ }
+
+ the_end:
+ if (saved_vm_running)
+ vm_start();
+ return;
+}
+
int load_vmstate(const char *name)
{
BlockDriverState *bs, *bs_vm_state;
break;
case QEMU_OPTION_incoming:
incoming = optarg;
+ runstate_set(RUN_STATE_INMIGRATE);
break;
case QEMU_OPTION_nodefaults:
default_serial = 0;
}
if (incoming) {
- runstate_set(RUN_STATE_INMIGRATE);
int ret = qemu_start_incoming_migration(incoming);
if (ret < 0) {
fprintf(stderr, "Migration failed. Exit code %s(%d), exiting.\n",
typedef struct XenPhysmap {
target_phys_addr_t start_addr;
ram_addr_t size;
- MemoryRegion *mr;
+ char *name;
target_phys_addr_t phys_offset;
QLIST_ENTRY(XenPhysmap) list;
xen_pfn_t *pfn_list;
int i;
+ if (runstate_check(RUN_STATE_INMIGRATE)) {
+ /* RAM already populated in Xen */
+ fprintf(stderr, "%s: do not alloc "RAM_ADDR_FMT
+ " bytes of ram at "RAM_ADDR_FMT" when runstate is INMIGRATE\n",
+ __func__, size, ram_addr);
+ return;
+ }
+
if (mr == &ram_memory) {
return;
}
return NULL;
}
+static target_phys_addr_t xen_phys_offset_to_gaddr(target_phys_addr_t start_addr,
+ ram_addr_t size, void *opaque)
+{
+ target_phys_addr_t addr = start_addr & TARGET_PAGE_MASK;
+ XenIOState *xen_io_state = opaque;
+ XenPhysmap *physmap = NULL;
+
+ QLIST_FOREACH(physmap, &xen_io_state->physmap, list) {
+ if (range_covers_byte(physmap->phys_offset, physmap->size, addr)) {
+ return physmap->start_addr;
+ }
+ }
+
+ return start_addr;
+}
+
#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 340
static int xen_add_to_physmap(XenIOState *state,
target_phys_addr_t start_addr,
XenPhysmap *physmap = NULL;
target_phys_addr_t pfn, start_gpfn;
target_phys_addr_t phys_offset = memory_region_get_ram_addr(mr);
+ char path[80], value[17];
if (get_physmapping(state, start_addr, size)) {
return 0;
physmap->start_addr = start_addr;
physmap->size = size;
+ physmap->name = (char *)mr->name;
physmap->phys_offset = phys_offset;
QLIST_INSERT_HEAD(&state->physmap, physmap, list);
start_addr >> TARGET_PAGE_BITS,
(start_addr + size) >> TARGET_PAGE_BITS,
XEN_DOMCTL_MEM_CACHEATTR_WB);
+
+ snprintf(path, sizeof(path),
+ "/local/domain/0/device-model/%d/physmap/%"PRIx64"/start_addr",
+ xen_domid, (uint64_t)phys_offset);
+ snprintf(value, sizeof(value), "%"PRIx64, (uint64_t)start_addr);
+ if (!xs_write(state->xenstore, 0, path, value, strlen(value))) {
+ return -1;
+ }
+ snprintf(path, sizeof(path),
+ "/local/domain/0/device-model/%d/physmap/%"PRIx64"/size",
+ xen_domid, (uint64_t)phys_offset);
+ snprintf(value, sizeof(value), "%"PRIx64, (uint64_t)size);
+ if (!xs_write(state->xenstore, 0, path, value, strlen(value))) {
+ return -1;
+ }
+ if (mr->name) {
+ snprintf(path, sizeof(path),
+ "/local/domain/0/device-model/%d/physmap/%"PRIx64"/name",
+ xen_domid, (uint64_t)phys_offset);
+ if (!xs_write(state->xenstore, 0, path, mr->name, strlen(mr->name))) {
+ return -1;
+ }
+ }
+
return 0;
}
return 0;
}
+static void xen_read_physmap(XenIOState *state)
+{
+ XenPhysmap *physmap = NULL;
+ unsigned int len, num, i;
+ char path[80], *value = NULL;
+ char **entries = NULL;
+
+ snprintf(path, sizeof(path),
+ "/local/domain/0/device-model/%d/physmap", xen_domid);
+ entries = xs_directory(state->xenstore, 0, path, &num);
+ if (entries == NULL)
+ return;
+
+ for (i = 0; i < num; i++) {
+ physmap = g_malloc(sizeof (XenPhysmap));
+ physmap->phys_offset = strtoull(entries[i], NULL, 16);
+ snprintf(path, sizeof(path),
+ "/local/domain/0/device-model/%d/physmap/%s/start_addr",
+ xen_domid, entries[i]);
+ value = xs_read(state->xenstore, 0, path, &len);
+ if (value == NULL) {
+ free(physmap);
+ continue;
+ }
+ physmap->start_addr = strtoull(value, NULL, 16);
+ free(value);
+
+ snprintf(path, sizeof(path),
+ "/local/domain/0/device-model/%d/physmap/%s/size",
+ xen_domid, entries[i]);
+ value = xs_read(state->xenstore, 0, path, &len);
+ if (value == NULL) {
+ free(physmap);
+ continue;
+ }
+ physmap->size = strtoull(value, NULL, 16);
+ free(value);
+
+ snprintf(path, sizeof(path),
+ "/local/domain/0/device-model/%d/physmap/%s/name",
+ xen_domid, entries[i]);
+ physmap->name = xs_read(state->xenstore, 0, path, &len);
+
+ QLIST_INSERT_HEAD(&state->physmap, physmap, list);
+ }
+ free(entries);
+ return;
+}
+
int xen_hvm_init(void)
{
int i, rc;
}
/* Init RAM management */
- xen_map_cache_init();
+ xen_map_cache_init(xen_phys_offset_to_gaddr, state);
xen_ram_init(ram_size);
qemu_add_vm_change_state_handler(xen_hvm_change_state_handler, state);
xen_be_register("console", &xen_console_ops);
xen_be_register("vkbd", &xen_kbdmouse_ops);
xen_be_register("qdisk", &xen_blkdev_ops);
+ xen_read_physmap(state);
return 0;
}
uint8_t *last_address_vaddr;
unsigned long max_mcache_size;
unsigned int mcache_bucket_shift;
+
+ phys_offset_to_gaddr_t phys_offset_to_gaddr;
+ void *opaque;
} MapCache;
static MapCache *mapcache;
return 0;
}
-void xen_map_cache_init(void)
+void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque)
{
unsigned long size;
struct rlimit rlimit_as;
mapcache = g_malloc0(sizeof (MapCache));
+ mapcache->phys_offset_to_gaddr = f;
+ mapcache->opaque = opaque;
+
QTAILQ_INIT(&mapcache->locked_entries);
mapcache->last_address_index = -1;
uint8_t lock)
{
MapCacheEntry *entry, *pentry = NULL;
- target_phys_addr_t address_index = phys_addr >> MCACHE_BUCKET_SHIFT;
- target_phys_addr_t address_offset = phys_addr & (MCACHE_BUCKET_SIZE - 1);
+ target_phys_addr_t address_index;
+ target_phys_addr_t address_offset;
target_phys_addr_t __size = size;
+ bool translated = false;
+
+tryagain:
+ address_index = phys_addr >> MCACHE_BUCKET_SHIFT;
+ address_offset = phys_addr & (MCACHE_BUCKET_SIZE - 1);
trace_xen_map_cache(phys_addr);
if(!test_bits(address_offset >> XC_PAGE_SHIFT, size >> XC_PAGE_SHIFT,
entry->valid_mapping)) {
mapcache->last_address_index = -1;
+ if (!translated && mapcache->phys_offset_to_gaddr) {
+ phys_addr = mapcache->phys_offset_to_gaddr(phys_addr, size, mapcache->opaque);
+ translated = true;
+ goto tryagain;
+ }
trace_xen_map_cache_return(NULL);
return NULL;
}
#include <stdlib.h>
+typedef target_phys_addr_t (*phys_offset_to_gaddr_t)(target_phys_addr_t start_addr,
+ ram_addr_t size,
+ void *opaque);
#ifdef CONFIG_XEN
-void xen_map_cache_init(void);
+void xen_map_cache_init(phys_offset_to_gaddr_t f,
+ void *opaque);
uint8_t *xen_map_cache(target_phys_addr_t phys_addr, target_phys_addr_t size,
uint8_t lock);
ram_addr_t xen_ram_addr_from_mapcache(void *ptr);
#else
-static inline void xen_map_cache_init(void)
+static inline void xen_map_cache_init(phys_offset_to_gaddr_t f,
+ void *opaque)
{
}