Use a file-backed copy-on-write mmap region for snapshots. Restores are handled by remmaping the fixed region. Currently, the snapshot file save path (`filepath`) is hardcoded (to a path that is memory-backed on my machine).
Signed-off-by: Richard Liu <richy.liu.2...@gmail.com> --- hw/misc/snapshot.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/hw/misc/snapshot.c b/hw/misc/snapshot.c index 2690b331fd..510bf59dce 100644 --- a/hw/misc/snapshot.c +++ b/hw/misc/snapshot.c @@ -18,8 +18,63 @@ DECLARE_INSTANCE_CHECKER(SnapshotState, SNAPSHOT, struct SnapshotState { PCIDevice pdev; MemoryRegion mmio; + + // track saved stated to prevent re-saving + bool is_saved; + + // saved cpu and devices state + QIOChannelBuffer *ioc; }; +// memory save location (for better performance, use tmpfs) +const char *filepath = "/Volumes/RAMDisk/snapshot_0"; + +static void save_snapshot(struct SnapshotState *s) { + if (s->is_saved) { + return; + } + s->is_saved = true; + + // save memory state to file + int fd = -1; + uint8_t *guest_mem = current_machine->ram->ram_block->host; + size_t guest_size = current_machine->ram->ram_block->max_length; + + fd = open(filepath, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600); + ftruncate(fd, guest_size); + + char *map = mmap(0, guest_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + memcpy(map, guest_mem, guest_size); + msync(map, guest_size, MS_SYNC); + munmap(map, guest_size); + close(fd); + + // unmap the guest, we will now use a MAP_PRIVATE + munmap(guest_mem, guest_size); + + // map as MAP_PRIVATE to avoid carrying writes back to the saved file + fd = open(filepath, O_RDONLY); + mmap(guest_mem, guest_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, fd, 0); +} + +static void restore_snapshot(struct SnapshotState *s) { + int fd = -1; + uint8_t *guest_mem = current_machine->ram->ram_block->host; + size_t guest_size = current_machine->ram->ram_block->max_length; + + if (!s->is_saved) { + fprintf(stderr, "[QEMU] ERROR: attempting to restore but state has not been saved!\n"); + return; + } + + munmap(guest_mem, guest_size); + + // remap the snapshot at the same location + fd = open(filepath, O_RDONLY); + mmap(guest_mem, guest_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, fd, 0); + close(fd); +} + static uint64_t snapshot_mmio_read(void *opaque, hwaddr addr, unsigned size) { return 0; @@ -28,6 +83,21 @@ static uint64_t snapshot_mmio_read(void *opaque, hwaddr addr, unsigned size) static void snapshot_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { + SnapshotState *snapshot = opaque; + (void)snapshot; + + switch (addr) { + case 0x00: + switch (val) { + case 0x101: + save_snapshot(snapshot); + break; + case 0x102: + restore_snapshot(snapshot); + break; + } + break; + } } static const MemoryRegionOps snapshot_mmio_ops = { @@ -48,6 +118,8 @@ static const MemoryRegionOps snapshot_mmio_ops = { static void pci_snapshot_realize(PCIDevice *pdev, Error **errp) { SnapshotState *snapshot = SNAPSHOT(pdev); + snapshot->is_saved = false; + snapshot->ioc = NULL; memory_region_init_io(&snapshot->mmio, OBJECT(snapshot), &snapshot_mmio_ops, snapshot, "snapshot-mmio", 1 * MiB); -- 2.35.1