1483d953aSJohn Baldwin /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3483d953aSJohn Baldwin * 4483d953aSJohn Baldwin * Copyright (c) 2016 Flavius Anton 5483d953aSJohn Baldwin * Copyright (c) 2016 Mihai Tiganus 6483d953aSJohn Baldwin * Copyright (c) 2016-2019 Mihai Carabas 7483d953aSJohn Baldwin * Copyright (c) 2017-2019 Darius Mihai 8483d953aSJohn Baldwin * Copyright (c) 2017-2019 Elena Mihailescu 9483d953aSJohn Baldwin * Copyright (c) 2018-2019 Sergiu Weisz 10483d953aSJohn Baldwin * All rights reserved. 11483d953aSJohn Baldwin * The bhyve-snapshot feature was developed under sponsorships 12483d953aSJohn Baldwin * from Matthew Grooms. 13483d953aSJohn Baldwin * 14483d953aSJohn Baldwin * Redistribution and use in source and binary forms, with or without 15483d953aSJohn Baldwin * modification, are permitted provided that the following conditions 16483d953aSJohn Baldwin * are met: 17483d953aSJohn Baldwin * 1. Redistributions of source code must retain the above copyright 18483d953aSJohn Baldwin * notice, this list of conditions and the following disclaimer. 19483d953aSJohn Baldwin * 2. Redistributions in binary form must reproduce the above copyright 20483d953aSJohn Baldwin * notice, this list of conditions and the following disclaimer in the 21483d953aSJohn Baldwin * documentation and/or other materials provided with the distribution. 22483d953aSJohn Baldwin * 23483d953aSJohn Baldwin * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 24483d953aSJohn Baldwin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25483d953aSJohn Baldwin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26483d953aSJohn Baldwin * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 27483d953aSJohn Baldwin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28483d953aSJohn Baldwin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29483d953aSJohn Baldwin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30483d953aSJohn Baldwin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31483d953aSJohn Baldwin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32483d953aSJohn Baldwin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33483d953aSJohn Baldwin * SUCH DAMAGE. 34483d953aSJohn Baldwin */ 35483d953aSJohn Baldwin 36483d953aSJohn Baldwin #include <sys/cdefs.h> 37483d953aSJohn Baldwin __FBSDID("$FreeBSD$"); 38483d953aSJohn Baldwin 39483d953aSJohn Baldwin #include <sys/types.h> 40483d953aSJohn Baldwin #ifndef WITHOUT_CAPSICUM 41483d953aSJohn Baldwin #include <sys/capsicum.h> 42483d953aSJohn Baldwin #endif 43483d953aSJohn Baldwin #include <sys/mman.h> 44483d953aSJohn Baldwin #include <sys/socket.h> 45483d953aSJohn Baldwin #include <sys/stat.h> 46483d953aSJohn Baldwin #include <sys/time.h> 47483d953aSJohn Baldwin #include <sys/un.h> 48483d953aSJohn Baldwin 49483d953aSJohn Baldwin #include <machine/atomic.h> 50483d953aSJohn Baldwin #include <machine/segments.h> 51483d953aSJohn Baldwin 52483d953aSJohn Baldwin #ifndef WITHOUT_CAPSICUM 53483d953aSJohn Baldwin #include <capsicum_helpers.h> 54483d953aSJohn Baldwin #endif 55483d953aSJohn Baldwin #include <stdio.h> 56483d953aSJohn Baldwin #include <stdlib.h> 57483d953aSJohn Baldwin #include <string.h> 58483d953aSJohn Baldwin #include <err.h> 59483d953aSJohn Baldwin #include <errno.h> 60483d953aSJohn Baldwin #include <fcntl.h> 61483d953aSJohn Baldwin #include <libgen.h> 62483d953aSJohn Baldwin #include <signal.h> 63483d953aSJohn Baldwin #include <unistd.h> 64483d953aSJohn Baldwin #include <assert.h> 65483d953aSJohn Baldwin #include <errno.h> 66483d953aSJohn Baldwin #include <pthread.h> 67483d953aSJohn Baldwin #include <pthread_np.h> 68483d953aSJohn Baldwin #include <sysexits.h> 69483d953aSJohn Baldwin #include <stdbool.h> 70483d953aSJohn Baldwin #include <sys/ioctl.h> 71483d953aSJohn Baldwin 72483d953aSJohn Baldwin #include <machine/vmm.h> 73483d953aSJohn Baldwin #ifndef WITHOUT_CAPSICUM 74483d953aSJohn Baldwin #include <machine/vmm_dev.h> 75483d953aSJohn Baldwin #endif 76483d953aSJohn Baldwin #include <machine/vmm_snapshot.h> 77483d953aSJohn Baldwin #include <vmmapi.h> 78483d953aSJohn Baldwin 79483d953aSJohn Baldwin #include "bhyverun.h" 80483d953aSJohn Baldwin #include "acpi.h" 81483d953aSJohn Baldwin #include "atkbdc.h" 8238dfb062SRobert Wing #include "debug.h" 83483d953aSJohn Baldwin #include "inout.h" 84c79331a4SRobert Wing #include "ipc.h" 85483d953aSJohn Baldwin #include "fwctl.h" 86483d953aSJohn Baldwin #include "ioapic.h" 87483d953aSJohn Baldwin #include "mem.h" 88483d953aSJohn Baldwin #include "mevent.h" 89483d953aSJohn Baldwin #include "mptbl.h" 90483d953aSJohn Baldwin #include "pci_emul.h" 91483d953aSJohn Baldwin #include "pci_irq.h" 92483d953aSJohn Baldwin #include "pci_lpc.h" 93483d953aSJohn Baldwin #include "smbiostbl.h" 94483d953aSJohn Baldwin #include "snapshot.h" 95483d953aSJohn Baldwin #include "xmsr.h" 96483d953aSJohn Baldwin #include "spinup_ap.h" 97483d953aSJohn Baldwin #include "rtc.h" 98483d953aSJohn Baldwin 99483d953aSJohn Baldwin #include <libxo/xo.h> 100483d953aSJohn Baldwin #include <ucl.h> 101483d953aSJohn Baldwin 102483d953aSJohn Baldwin struct spinner_info { 103483d953aSJohn Baldwin const size_t *crtval; 104483d953aSJohn Baldwin const size_t maxval; 105483d953aSJohn Baldwin const size_t total; 106483d953aSJohn Baldwin }; 107483d953aSJohn Baldwin 108483d953aSJohn Baldwin extern int guest_ncpus; 109483d953aSJohn Baldwin 110483d953aSJohn Baldwin static struct winsize winsize; 111483d953aSJohn Baldwin static sig_t old_winch_handler; 112483d953aSJohn Baldwin 113483d953aSJohn Baldwin #define KB (1024UL) 114483d953aSJohn Baldwin #define MB (1024UL * KB) 115483d953aSJohn Baldwin #define GB (1024UL * MB) 116483d953aSJohn Baldwin 117483d953aSJohn Baldwin #define SNAPSHOT_CHUNK (4 * MB) 118483d953aSJohn Baldwin #define PROG_BUF_SZ (8192) 119483d953aSJohn Baldwin 120483d953aSJohn Baldwin #define SNAPSHOT_BUFFER_SIZE (20 * MB) 121483d953aSJohn Baldwin 12250aef9f6SVitaliy Gusev #define JSON_KERNEL_ARR_KEY "kern_structs" 123483d953aSJohn Baldwin #define JSON_DEV_ARR_KEY "devices" 124483d953aSJohn Baldwin #define JSON_BASIC_METADATA_KEY "basic metadata" 12550aef9f6SVitaliy Gusev #define JSON_SNAPSHOT_REQ_KEY "device" 126483d953aSJohn Baldwin #define JSON_SIZE_KEY "size" 127483d953aSJohn Baldwin #define JSON_FILE_OFFSET_KEY "file_offset" 128483d953aSJohn Baldwin 129483d953aSJohn Baldwin #define JSON_NCPUS_KEY "ncpus" 130483d953aSJohn Baldwin #define JSON_VMNAME_KEY "vmname" 131483d953aSJohn Baldwin #define JSON_MEMSIZE_KEY "memsize" 132483d953aSJohn Baldwin #define JSON_MEMFLAGS_KEY "memflags" 133483d953aSJohn Baldwin 134483d953aSJohn Baldwin #define min(a,b) \ 135483d953aSJohn Baldwin ({ \ 136483d953aSJohn Baldwin __typeof__ (a) _a = (a); \ 137483d953aSJohn Baldwin __typeof__ (b) _b = (b); \ 138483d953aSJohn Baldwin _a < _b ? _a : _b; \ 139483d953aSJohn Baldwin }) 140483d953aSJohn Baldwin 141c9faf698SMark Johnston static const struct vm_snapshot_dev_info snapshot_devs[] = { 142483d953aSJohn Baldwin { "atkbdc", atkbdc_snapshot, NULL, NULL }, 143483d953aSJohn Baldwin { "virtio-net", pci_snapshot, pci_pause, pci_resume }, 144483d953aSJohn Baldwin { "virtio-blk", pci_snapshot, pci_pause, pci_resume }, 145bb481f67SJohn Baldwin { "virtio-rnd", pci_snapshot, NULL, NULL }, 146483d953aSJohn Baldwin { "lpc", pci_snapshot, NULL, NULL }, 147483d953aSJohn Baldwin { "fbuf", pci_snapshot, NULL, NULL }, 148483d953aSJohn Baldwin { "xhci", pci_snapshot, NULL, NULL }, 149483d953aSJohn Baldwin { "e1000", pci_snapshot, NULL, NULL }, 150483d953aSJohn Baldwin { "ahci", pci_snapshot, pci_pause, pci_resume }, 151483d953aSJohn Baldwin { "ahci-hd", pci_snapshot, pci_pause, pci_resume }, 1521b9c7861SJohn Baldwin { "ahci-cd", pci_snapshot, pci_pause, pci_resume }, 153483d953aSJohn Baldwin }; 154483d953aSJohn Baldwin 155c9faf698SMark Johnston static const struct vm_snapshot_kern_info snapshot_kern_structs[] = { 156483d953aSJohn Baldwin { "vhpet", STRUCT_VHPET }, 157483d953aSJohn Baldwin { "vm", STRUCT_VM }, 158483d953aSJohn Baldwin { "vioapic", STRUCT_VIOAPIC }, 159483d953aSJohn Baldwin { "vlapic", STRUCT_VLAPIC }, 160483d953aSJohn Baldwin { "vmcx", STRUCT_VMCX }, 161483d953aSJohn Baldwin { "vatpit", STRUCT_VATPIT }, 162483d953aSJohn Baldwin { "vatpic", STRUCT_VATPIC }, 163483d953aSJohn Baldwin { "vpmtmr", STRUCT_VPMTMR }, 164483d953aSJohn Baldwin { "vrtc", STRUCT_VRTC }, 165483d953aSJohn Baldwin }; 166483d953aSJohn Baldwin 167483d953aSJohn Baldwin static cpuset_t vcpus_active, vcpus_suspended; 168483d953aSJohn Baldwin static pthread_mutex_t vcpu_lock; 169483d953aSJohn Baldwin static pthread_cond_t vcpus_idle, vcpus_can_run; 170483d953aSJohn Baldwin static bool checkpoint_active; 171483d953aSJohn Baldwin 172483d953aSJohn Baldwin /* 173483d953aSJohn Baldwin * TODO: Harden this function and all of its callers since 'base_str' is a user 174483d953aSJohn Baldwin * provided string. 175483d953aSJohn Baldwin */ 176483d953aSJohn Baldwin static char * 177483d953aSJohn Baldwin strcat_extension(const char *base_str, const char *ext) 178483d953aSJohn Baldwin { 179483d953aSJohn Baldwin char *res; 180483d953aSJohn Baldwin size_t base_len, ext_len; 181483d953aSJohn Baldwin 182961e6a12SRobert Wing base_len = strnlen(base_str, NAME_MAX); 183961e6a12SRobert Wing ext_len = strnlen(ext, NAME_MAX); 184483d953aSJohn Baldwin 185961e6a12SRobert Wing if (base_len + ext_len > NAME_MAX) { 186483d953aSJohn Baldwin fprintf(stderr, "Filename exceeds maximum length.\n"); 187483d953aSJohn Baldwin return (NULL); 188483d953aSJohn Baldwin } 189483d953aSJohn Baldwin 190483d953aSJohn Baldwin res = malloc(base_len + ext_len + 1); 191483d953aSJohn Baldwin if (res == NULL) { 192483d953aSJohn Baldwin perror("Failed to allocate memory."); 193483d953aSJohn Baldwin return (NULL); 194483d953aSJohn Baldwin } 195483d953aSJohn Baldwin 196483d953aSJohn Baldwin memcpy(res, base_str, base_len); 197483d953aSJohn Baldwin memcpy(res + base_len, ext, ext_len); 198483d953aSJohn Baldwin res[base_len + ext_len] = 0; 199483d953aSJohn Baldwin 200483d953aSJohn Baldwin return (res); 201483d953aSJohn Baldwin } 202483d953aSJohn Baldwin 203483d953aSJohn Baldwin void 204483d953aSJohn Baldwin destroy_restore_state(struct restore_state *rstate) 205483d953aSJohn Baldwin { 206483d953aSJohn Baldwin if (rstate == NULL) { 207483d953aSJohn Baldwin fprintf(stderr, "Attempting to destroy NULL restore struct.\n"); 208483d953aSJohn Baldwin return; 209483d953aSJohn Baldwin } 210483d953aSJohn Baldwin 211483d953aSJohn Baldwin if (rstate->kdata_map != MAP_FAILED) 212483d953aSJohn Baldwin munmap(rstate->kdata_map, rstate->kdata_len); 213483d953aSJohn Baldwin 214483d953aSJohn Baldwin if (rstate->kdata_fd > 0) 215483d953aSJohn Baldwin close(rstate->kdata_fd); 216483d953aSJohn Baldwin if (rstate->vmmem_fd > 0) 217483d953aSJohn Baldwin close(rstate->vmmem_fd); 218483d953aSJohn Baldwin 219483d953aSJohn Baldwin if (rstate->meta_root_obj != NULL) 220483d953aSJohn Baldwin ucl_object_unref(rstate->meta_root_obj); 221483d953aSJohn Baldwin if (rstate->meta_parser != NULL) 222483d953aSJohn Baldwin ucl_parser_free(rstate->meta_parser); 223483d953aSJohn Baldwin } 224483d953aSJohn Baldwin 225483d953aSJohn Baldwin static int 226483d953aSJohn Baldwin load_vmmem_file(const char *filename, struct restore_state *rstate) 227483d953aSJohn Baldwin { 228483d953aSJohn Baldwin struct stat sb; 229483d953aSJohn Baldwin int err; 230483d953aSJohn Baldwin 231483d953aSJohn Baldwin rstate->vmmem_fd = open(filename, O_RDONLY); 232483d953aSJohn Baldwin if (rstate->vmmem_fd < 0) { 233483d953aSJohn Baldwin perror("Failed to open restore file"); 234483d953aSJohn Baldwin return (-1); 235483d953aSJohn Baldwin } 236483d953aSJohn Baldwin 237483d953aSJohn Baldwin err = fstat(rstate->vmmem_fd, &sb); 238483d953aSJohn Baldwin if (err < 0) { 239483d953aSJohn Baldwin perror("Failed to stat restore file"); 240483d953aSJohn Baldwin goto err_load_vmmem; 241483d953aSJohn Baldwin } 242483d953aSJohn Baldwin 243483d953aSJohn Baldwin if (sb.st_size == 0) { 244483d953aSJohn Baldwin fprintf(stderr, "Restore file is empty.\n"); 245483d953aSJohn Baldwin goto err_load_vmmem; 246483d953aSJohn Baldwin } 247483d953aSJohn Baldwin 248483d953aSJohn Baldwin rstate->vmmem_len = sb.st_size; 249483d953aSJohn Baldwin 250483d953aSJohn Baldwin return (0); 251483d953aSJohn Baldwin 252483d953aSJohn Baldwin err_load_vmmem: 253483d953aSJohn Baldwin if (rstate->vmmem_fd > 0) 254483d953aSJohn Baldwin close(rstate->vmmem_fd); 255483d953aSJohn Baldwin return (-1); 256483d953aSJohn Baldwin } 257483d953aSJohn Baldwin 258483d953aSJohn Baldwin static int 259483d953aSJohn Baldwin load_kdata_file(const char *filename, struct restore_state *rstate) 260483d953aSJohn Baldwin { 261483d953aSJohn Baldwin struct stat sb; 262483d953aSJohn Baldwin int err; 263483d953aSJohn Baldwin 264483d953aSJohn Baldwin rstate->kdata_fd = open(filename, O_RDONLY); 265483d953aSJohn Baldwin if (rstate->kdata_fd < 0) { 266483d953aSJohn Baldwin perror("Failed to open kernel data file"); 267483d953aSJohn Baldwin return (-1); 268483d953aSJohn Baldwin } 269483d953aSJohn Baldwin 270483d953aSJohn Baldwin err = fstat(rstate->kdata_fd, &sb); 271483d953aSJohn Baldwin if (err < 0) { 272483d953aSJohn Baldwin perror("Failed to stat kernel data file"); 273483d953aSJohn Baldwin goto err_load_kdata; 274483d953aSJohn Baldwin } 275483d953aSJohn Baldwin 276483d953aSJohn Baldwin if (sb.st_size == 0) { 277483d953aSJohn Baldwin fprintf(stderr, "Kernel data file is empty.\n"); 278483d953aSJohn Baldwin goto err_load_kdata; 279483d953aSJohn Baldwin } 280483d953aSJohn Baldwin 281483d953aSJohn Baldwin rstate->kdata_len = sb.st_size; 282483d953aSJohn Baldwin rstate->kdata_map = mmap(NULL, rstate->kdata_len, PROT_READ, 283483d953aSJohn Baldwin MAP_SHARED, rstate->kdata_fd, 0); 284483d953aSJohn Baldwin if (rstate->kdata_map == MAP_FAILED) { 285483d953aSJohn Baldwin perror("Failed to map restore file"); 286483d953aSJohn Baldwin goto err_load_kdata; 287483d953aSJohn Baldwin } 288483d953aSJohn Baldwin 289483d953aSJohn Baldwin return (0); 290483d953aSJohn Baldwin 291483d953aSJohn Baldwin err_load_kdata: 292483d953aSJohn Baldwin if (rstate->kdata_fd > 0) 293483d953aSJohn Baldwin close(rstate->kdata_fd); 294483d953aSJohn Baldwin return (-1); 295483d953aSJohn Baldwin } 296483d953aSJohn Baldwin 297483d953aSJohn Baldwin static int 298483d953aSJohn Baldwin load_metadata_file(const char *filename, struct restore_state *rstate) 299483d953aSJohn Baldwin { 300c9faf698SMark Johnston ucl_object_t *obj; 301483d953aSJohn Baldwin struct ucl_parser *parser; 302483d953aSJohn Baldwin int err; 303483d953aSJohn Baldwin 304483d953aSJohn Baldwin parser = ucl_parser_new(UCL_PARSER_DEFAULT); 305483d953aSJohn Baldwin if (parser == NULL) { 306483d953aSJohn Baldwin fprintf(stderr, "Failed to initialize UCL parser.\n"); 307c9faf698SMark Johnston err = -1; 308483d953aSJohn Baldwin goto err_load_metadata; 309483d953aSJohn Baldwin } 310483d953aSJohn Baldwin 311483d953aSJohn Baldwin err = ucl_parser_add_file(parser, filename); 312483d953aSJohn Baldwin if (err == 0) { 313483d953aSJohn Baldwin fprintf(stderr, "Failed to parse metadata file: '%s'\n", 314483d953aSJohn Baldwin filename); 315483d953aSJohn Baldwin err = -1; 316483d953aSJohn Baldwin goto err_load_metadata; 317483d953aSJohn Baldwin } 318483d953aSJohn Baldwin 319483d953aSJohn Baldwin obj = ucl_parser_get_object(parser); 320483d953aSJohn Baldwin if (obj == NULL) { 321483d953aSJohn Baldwin fprintf(stderr, "Failed to parse object.\n"); 322483d953aSJohn Baldwin err = -1; 323483d953aSJohn Baldwin goto err_load_metadata; 324483d953aSJohn Baldwin } 325483d953aSJohn Baldwin 326483d953aSJohn Baldwin rstate->meta_parser = parser; 327483d953aSJohn Baldwin rstate->meta_root_obj = (ucl_object_t *)obj; 328483d953aSJohn Baldwin 329483d953aSJohn Baldwin return (0); 330483d953aSJohn Baldwin 331483d953aSJohn Baldwin err_load_metadata: 332483d953aSJohn Baldwin if (parser != NULL) 333483d953aSJohn Baldwin ucl_parser_free(parser); 334483d953aSJohn Baldwin return (err); 335483d953aSJohn Baldwin } 336483d953aSJohn Baldwin 337483d953aSJohn Baldwin int 338483d953aSJohn Baldwin load_restore_file(const char *filename, struct restore_state *rstate) 339483d953aSJohn Baldwin { 340483d953aSJohn Baldwin int err = 0; 341483d953aSJohn Baldwin char *kdata_filename = NULL, *meta_filename = NULL; 342483d953aSJohn Baldwin 343483d953aSJohn Baldwin assert(filename != NULL); 344483d953aSJohn Baldwin assert(rstate != NULL); 345483d953aSJohn Baldwin 346483d953aSJohn Baldwin memset(rstate, 0, sizeof(*rstate)); 347483d953aSJohn Baldwin rstate->kdata_map = MAP_FAILED; 348483d953aSJohn Baldwin 349483d953aSJohn Baldwin err = load_vmmem_file(filename, rstate); 350483d953aSJohn Baldwin if (err != 0) { 351483d953aSJohn Baldwin fprintf(stderr, "Failed to load guest RAM file.\n"); 352483d953aSJohn Baldwin goto err_restore; 353483d953aSJohn Baldwin } 354483d953aSJohn Baldwin 355483d953aSJohn Baldwin kdata_filename = strcat_extension(filename, ".kern"); 356483d953aSJohn Baldwin if (kdata_filename == NULL) { 357483d953aSJohn Baldwin fprintf(stderr, "Failed to construct kernel data filename.\n"); 358483d953aSJohn Baldwin goto err_restore; 359483d953aSJohn Baldwin } 360483d953aSJohn Baldwin 361483d953aSJohn Baldwin err = load_kdata_file(kdata_filename, rstate); 362483d953aSJohn Baldwin if (err != 0) { 363483d953aSJohn Baldwin fprintf(stderr, "Failed to load guest kernel data file.\n"); 364483d953aSJohn Baldwin goto err_restore; 365483d953aSJohn Baldwin } 366483d953aSJohn Baldwin 367483d953aSJohn Baldwin meta_filename = strcat_extension(filename, ".meta"); 368483d953aSJohn Baldwin if (meta_filename == NULL) { 369483d953aSJohn Baldwin fprintf(stderr, "Failed to construct kernel metadata filename.\n"); 370483d953aSJohn Baldwin goto err_restore; 371483d953aSJohn Baldwin } 372483d953aSJohn Baldwin 373483d953aSJohn Baldwin err = load_metadata_file(meta_filename, rstate); 374483d953aSJohn Baldwin if (err != 0) { 375483d953aSJohn Baldwin fprintf(stderr, "Failed to load guest metadata file.\n"); 376483d953aSJohn Baldwin goto err_restore; 377483d953aSJohn Baldwin } 378483d953aSJohn Baldwin 379483d953aSJohn Baldwin return (0); 380483d953aSJohn Baldwin 381483d953aSJohn Baldwin err_restore: 382483d953aSJohn Baldwin destroy_restore_state(rstate); 383483d953aSJohn Baldwin if (kdata_filename != NULL) 384483d953aSJohn Baldwin free(kdata_filename); 385483d953aSJohn Baldwin if (meta_filename != NULL) 386483d953aSJohn Baldwin free(meta_filename); 387483d953aSJohn Baldwin return (-1); 388483d953aSJohn Baldwin } 389483d953aSJohn Baldwin 390483d953aSJohn Baldwin #define JSON_GET_INT_OR_RETURN(key, obj, result_ptr, ret) \ 391483d953aSJohn Baldwin do { \ 392483d953aSJohn Baldwin const ucl_object_t *obj__; \ 393483d953aSJohn Baldwin obj__ = ucl_object_lookup(obj, key); \ 394483d953aSJohn Baldwin if (obj__ == NULL) { \ 395483d953aSJohn Baldwin fprintf(stderr, "Missing key: '%s'", key); \ 396483d953aSJohn Baldwin return (ret); \ 397483d953aSJohn Baldwin } \ 398483d953aSJohn Baldwin if (!ucl_object_toint_safe(obj__, result_ptr)) { \ 399483d953aSJohn Baldwin fprintf(stderr, "Cannot convert '%s' value to int.", key); \ 400483d953aSJohn Baldwin return (ret); \ 401483d953aSJohn Baldwin } \ 402483d953aSJohn Baldwin } while(0) 403483d953aSJohn Baldwin 404483d953aSJohn Baldwin #define JSON_GET_STRING_OR_RETURN(key, obj, result_ptr, ret) \ 405483d953aSJohn Baldwin do { \ 406483d953aSJohn Baldwin const ucl_object_t *obj__; \ 407483d953aSJohn Baldwin obj__ = ucl_object_lookup(obj, key); \ 408483d953aSJohn Baldwin if (obj__ == NULL) { \ 409483d953aSJohn Baldwin fprintf(stderr, "Missing key: '%s'", key); \ 410483d953aSJohn Baldwin return (ret); \ 411483d953aSJohn Baldwin } \ 412483d953aSJohn Baldwin if (!ucl_object_tostring_safe(obj__, result_ptr)) { \ 413483d953aSJohn Baldwin fprintf(stderr, "Cannot convert '%s' value to string.", key); \ 414483d953aSJohn Baldwin return (ret); \ 415483d953aSJohn Baldwin } \ 416483d953aSJohn Baldwin } while(0) 417483d953aSJohn Baldwin 418483d953aSJohn Baldwin static void * 419483d953aSJohn Baldwin lookup_check_dev(const char *dev_name, struct restore_state *rstate, 420483d953aSJohn Baldwin const ucl_object_t *obj, size_t *data_size) 421483d953aSJohn Baldwin { 422483d953aSJohn Baldwin const char *snapshot_req; 423483d953aSJohn Baldwin int64_t size, file_offset; 424483d953aSJohn Baldwin 425483d953aSJohn Baldwin snapshot_req = NULL; 426483d953aSJohn Baldwin JSON_GET_STRING_OR_RETURN(JSON_SNAPSHOT_REQ_KEY, obj, 427483d953aSJohn Baldwin &snapshot_req, NULL); 428483d953aSJohn Baldwin assert(snapshot_req != NULL); 429483d953aSJohn Baldwin if (!strcmp(snapshot_req, dev_name)) { 430483d953aSJohn Baldwin JSON_GET_INT_OR_RETURN(JSON_SIZE_KEY, obj, 431483d953aSJohn Baldwin &size, NULL); 432483d953aSJohn Baldwin assert(size >= 0); 433483d953aSJohn Baldwin 434483d953aSJohn Baldwin JSON_GET_INT_OR_RETURN(JSON_FILE_OFFSET_KEY, obj, 435483d953aSJohn Baldwin &file_offset, NULL); 436483d953aSJohn Baldwin assert(file_offset >= 0); 437ed721684SMark Johnston assert((uint64_t)file_offset + size <= rstate->kdata_len); 438483d953aSJohn Baldwin 439483d953aSJohn Baldwin *data_size = (size_t)size; 44063898728SMark Johnston return ((uint8_t *)rstate->kdata_map + file_offset); 441483d953aSJohn Baldwin } 442483d953aSJohn Baldwin 443483d953aSJohn Baldwin return (NULL); 444483d953aSJohn Baldwin } 445483d953aSJohn Baldwin 446483d953aSJohn Baldwin static void * 447ee5023f3SVitaliy Gusev lookup_dev(const char *dev_name, const char *key, struct restore_state *rstate, 448483d953aSJohn Baldwin size_t *data_size) 449483d953aSJohn Baldwin { 450483d953aSJohn Baldwin const ucl_object_t *devs = NULL, *obj = NULL; 451483d953aSJohn Baldwin ucl_object_iter_t it = NULL; 452483d953aSJohn Baldwin void *ret; 453483d953aSJohn Baldwin 454ee5023f3SVitaliy Gusev devs = ucl_object_lookup(rstate->meta_root_obj, key); 455483d953aSJohn Baldwin if (devs == NULL) { 456483d953aSJohn Baldwin fprintf(stderr, "Failed to find '%s' object.\n", 457483d953aSJohn Baldwin JSON_DEV_ARR_KEY); 458483d953aSJohn Baldwin return (NULL); 459483d953aSJohn Baldwin } 460483d953aSJohn Baldwin 461c9faf698SMark Johnston if (ucl_object_type(devs) != UCL_ARRAY) { 462483d953aSJohn Baldwin fprintf(stderr, "Object '%s' is not an array.\n", 463483d953aSJohn Baldwin JSON_DEV_ARR_KEY); 464483d953aSJohn Baldwin return (NULL); 465483d953aSJohn Baldwin } 466483d953aSJohn Baldwin 467483d953aSJohn Baldwin while ((obj = ucl_object_iterate(devs, &it, true)) != NULL) { 468483d953aSJohn Baldwin ret = lookup_check_dev(dev_name, rstate, obj, data_size); 469483d953aSJohn Baldwin if (ret != NULL) 470483d953aSJohn Baldwin return (ret); 471483d953aSJohn Baldwin } 472483d953aSJohn Baldwin 473483d953aSJohn Baldwin return (NULL); 474483d953aSJohn Baldwin } 475483d953aSJohn Baldwin 476483d953aSJohn Baldwin static const ucl_object_t * 477483d953aSJohn Baldwin lookup_basic_metadata_object(struct restore_state *rstate) 478483d953aSJohn Baldwin { 479483d953aSJohn Baldwin const ucl_object_t *basic_meta_obj = NULL; 480483d953aSJohn Baldwin 481483d953aSJohn Baldwin basic_meta_obj = ucl_object_lookup(rstate->meta_root_obj, 482483d953aSJohn Baldwin JSON_BASIC_METADATA_KEY); 483483d953aSJohn Baldwin if (basic_meta_obj == NULL) { 484483d953aSJohn Baldwin fprintf(stderr, "Failed to find '%s' object.\n", 485483d953aSJohn Baldwin JSON_BASIC_METADATA_KEY); 486483d953aSJohn Baldwin return (NULL); 487483d953aSJohn Baldwin } 488483d953aSJohn Baldwin 489c9faf698SMark Johnston if (ucl_object_type(basic_meta_obj) != UCL_OBJECT) { 490483d953aSJohn Baldwin fprintf(stderr, "Object '%s' is not a JSON object.\n", 491483d953aSJohn Baldwin JSON_BASIC_METADATA_KEY); 492483d953aSJohn Baldwin return (NULL); 493483d953aSJohn Baldwin } 494483d953aSJohn Baldwin 495483d953aSJohn Baldwin return (basic_meta_obj); 496483d953aSJohn Baldwin } 497483d953aSJohn Baldwin 498483d953aSJohn Baldwin const char * 499483d953aSJohn Baldwin lookup_vmname(struct restore_state *rstate) 500483d953aSJohn Baldwin { 501483d953aSJohn Baldwin const char *vmname; 502483d953aSJohn Baldwin const ucl_object_t *obj; 503483d953aSJohn Baldwin 504483d953aSJohn Baldwin obj = lookup_basic_metadata_object(rstate); 505483d953aSJohn Baldwin if (obj == NULL) 506483d953aSJohn Baldwin return (NULL); 507483d953aSJohn Baldwin 508483d953aSJohn Baldwin JSON_GET_STRING_OR_RETURN(JSON_VMNAME_KEY, obj, &vmname, NULL); 509483d953aSJohn Baldwin return (vmname); 510483d953aSJohn Baldwin } 511483d953aSJohn Baldwin 512483d953aSJohn Baldwin int 513483d953aSJohn Baldwin lookup_memflags(struct restore_state *rstate) 514483d953aSJohn Baldwin { 515483d953aSJohn Baldwin int64_t memflags; 516483d953aSJohn Baldwin const ucl_object_t *obj; 517483d953aSJohn Baldwin 518483d953aSJohn Baldwin obj = lookup_basic_metadata_object(rstate); 519483d953aSJohn Baldwin if (obj == NULL) 520483d953aSJohn Baldwin return (0); 521483d953aSJohn Baldwin 522483d953aSJohn Baldwin JSON_GET_INT_OR_RETURN(JSON_MEMFLAGS_KEY, obj, &memflags, 0); 523483d953aSJohn Baldwin 524483d953aSJohn Baldwin return ((int)memflags); 525483d953aSJohn Baldwin } 526483d953aSJohn Baldwin 527483d953aSJohn Baldwin size_t 528483d953aSJohn Baldwin lookup_memsize(struct restore_state *rstate) 529483d953aSJohn Baldwin { 530483d953aSJohn Baldwin int64_t memsize; 531483d953aSJohn Baldwin const ucl_object_t *obj; 532483d953aSJohn Baldwin 533483d953aSJohn Baldwin obj = lookup_basic_metadata_object(rstate); 534483d953aSJohn Baldwin if (obj == NULL) 535483d953aSJohn Baldwin return (0); 536483d953aSJohn Baldwin 537483d953aSJohn Baldwin JSON_GET_INT_OR_RETURN(JSON_MEMSIZE_KEY, obj, &memsize, 0); 538483d953aSJohn Baldwin if (memsize < 0) 539483d953aSJohn Baldwin memsize = 0; 540483d953aSJohn Baldwin 541483d953aSJohn Baldwin return ((size_t)memsize); 542483d953aSJohn Baldwin } 543483d953aSJohn Baldwin 544483d953aSJohn Baldwin 545483d953aSJohn Baldwin int 546483d953aSJohn Baldwin lookup_guest_ncpus(struct restore_state *rstate) 547483d953aSJohn Baldwin { 548483d953aSJohn Baldwin int64_t ncpus; 549483d953aSJohn Baldwin const ucl_object_t *obj; 550483d953aSJohn Baldwin 551483d953aSJohn Baldwin obj = lookup_basic_metadata_object(rstate); 552483d953aSJohn Baldwin if (obj == NULL) 553483d953aSJohn Baldwin return (0); 554483d953aSJohn Baldwin 555483d953aSJohn Baldwin JSON_GET_INT_OR_RETURN(JSON_NCPUS_KEY, obj, &ncpus, 0); 556483d953aSJohn Baldwin return ((int)ncpus); 557483d953aSJohn Baldwin } 558483d953aSJohn Baldwin 559483d953aSJohn Baldwin static void 56098d920d9SMark Johnston winch_handler(int signal __unused) 561483d953aSJohn Baldwin { 562483d953aSJohn Baldwin #ifdef TIOCGWINSZ 563483d953aSJohn Baldwin ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize); 564483d953aSJohn Baldwin #endif /* TIOCGWINSZ */ 565483d953aSJohn Baldwin } 566483d953aSJohn Baldwin 567483d953aSJohn Baldwin static int 568483d953aSJohn Baldwin print_progress(size_t crtval, const size_t maxval) 569483d953aSJohn Baldwin { 570483d953aSJohn Baldwin size_t rc; 571483d953aSJohn Baldwin double crtval_gb, maxval_gb; 572483d953aSJohn Baldwin size_t i, win_width, prog_start, prog_done, prog_end; 573483d953aSJohn Baldwin int mval_len; 574483d953aSJohn Baldwin 575483d953aSJohn Baldwin static char prog_buf[PROG_BUF_SZ]; 576483d953aSJohn Baldwin static const size_t len = sizeof(prog_buf); 577483d953aSJohn Baldwin 578483d953aSJohn Baldwin static size_t div; 579c9faf698SMark Johnston static const char *div_str; 580483d953aSJohn Baldwin 581483d953aSJohn Baldwin static char wip_bar[] = { '/', '-', '\\', '|' }; 582483d953aSJohn Baldwin static int wip_idx = 0; 583483d953aSJohn Baldwin 584483d953aSJohn Baldwin if (maxval == 0) { 585483d953aSJohn Baldwin printf("[0B / 0B]\r\n"); 586483d953aSJohn Baldwin return (0); 587483d953aSJohn Baldwin } 588483d953aSJohn Baldwin 589483d953aSJohn Baldwin if (crtval > maxval) 590483d953aSJohn Baldwin crtval = maxval; 591483d953aSJohn Baldwin 592483d953aSJohn Baldwin if (maxval > 10 * GB) { 593483d953aSJohn Baldwin div = GB; 594483d953aSJohn Baldwin div_str = "GiB"; 595483d953aSJohn Baldwin } else if (maxval > 10 * MB) { 596483d953aSJohn Baldwin div = MB; 597483d953aSJohn Baldwin div_str = "MiB"; 598483d953aSJohn Baldwin } else { 599483d953aSJohn Baldwin div = KB; 600483d953aSJohn Baldwin div_str = "KiB"; 601483d953aSJohn Baldwin } 602483d953aSJohn Baldwin 603483d953aSJohn Baldwin crtval_gb = (double) crtval / div; 604483d953aSJohn Baldwin maxval_gb = (double) maxval / div; 605483d953aSJohn Baldwin 606483d953aSJohn Baldwin rc = snprintf(prog_buf, len, "%.03lf", maxval_gb); 607483d953aSJohn Baldwin if (rc == len) { 608483d953aSJohn Baldwin fprintf(stderr, "Maxval too big\n"); 609483d953aSJohn Baldwin return (-1); 610483d953aSJohn Baldwin } 611483d953aSJohn Baldwin mval_len = rc; 612483d953aSJohn Baldwin 613483d953aSJohn Baldwin rc = snprintf(prog_buf, len, "\r[%*.03lf%s / %.03lf%s] |", 614483d953aSJohn Baldwin mval_len, crtval_gb, div_str, maxval_gb, div_str); 615483d953aSJohn Baldwin 616483d953aSJohn Baldwin if (rc == len) { 617483d953aSJohn Baldwin fprintf(stderr, "Buffer too small to print progress\n"); 618483d953aSJohn Baldwin return (-1); 619483d953aSJohn Baldwin } 620483d953aSJohn Baldwin 621483d953aSJohn Baldwin win_width = min(winsize.ws_col, len); 622483d953aSJohn Baldwin prog_start = rc; 623483d953aSJohn Baldwin 624483d953aSJohn Baldwin if (prog_start < (win_width - 2)) { 625483d953aSJohn Baldwin prog_end = win_width - prog_start - 2; 626483d953aSJohn Baldwin prog_done = prog_end * (crtval_gb / maxval_gb); 627483d953aSJohn Baldwin 628483d953aSJohn Baldwin for (i = prog_start; i < prog_start + prog_done; i++) 629483d953aSJohn Baldwin prog_buf[i] = '#'; 630483d953aSJohn Baldwin 631483d953aSJohn Baldwin if (crtval != maxval) { 632483d953aSJohn Baldwin prog_buf[i] = wip_bar[wip_idx]; 633483d953aSJohn Baldwin wip_idx = (wip_idx + 1) % sizeof(wip_bar); 634483d953aSJohn Baldwin i++; 635483d953aSJohn Baldwin } else { 636483d953aSJohn Baldwin prog_buf[i++] = '#'; 637483d953aSJohn Baldwin } 638483d953aSJohn Baldwin 639483d953aSJohn Baldwin for (; i < win_width - 2; i++) 640483d953aSJohn Baldwin prog_buf[i] = '_'; 641483d953aSJohn Baldwin 642483d953aSJohn Baldwin prog_buf[win_width - 2] = '|'; 643483d953aSJohn Baldwin } 644483d953aSJohn Baldwin 645483d953aSJohn Baldwin prog_buf[win_width - 1] = '\0'; 646483d953aSJohn Baldwin write(STDOUT_FILENO, prog_buf, win_width); 647483d953aSJohn Baldwin 648483d953aSJohn Baldwin return (0); 649483d953aSJohn Baldwin } 650483d953aSJohn Baldwin 651483d953aSJohn Baldwin static void * 652483d953aSJohn Baldwin snapshot_spinner_cb(void *arg) 653483d953aSJohn Baldwin { 654483d953aSJohn Baldwin int rc; 655483d953aSJohn Baldwin size_t crtval, maxval, total; 656483d953aSJohn Baldwin struct spinner_info *si; 657483d953aSJohn Baldwin struct timespec ts; 658483d953aSJohn Baldwin 659483d953aSJohn Baldwin si = arg; 660483d953aSJohn Baldwin if (si == NULL) 661483d953aSJohn Baldwin pthread_exit(NULL); 662483d953aSJohn Baldwin 663483d953aSJohn Baldwin ts.tv_sec = 0; 664483d953aSJohn Baldwin ts.tv_nsec = 50 * 1000 * 1000; /* 50 ms sleep time */ 665483d953aSJohn Baldwin 666483d953aSJohn Baldwin do { 667483d953aSJohn Baldwin crtval = *si->crtval; 668483d953aSJohn Baldwin maxval = si->maxval; 669483d953aSJohn Baldwin total = si->total; 670483d953aSJohn Baldwin 671483d953aSJohn Baldwin rc = print_progress(crtval, total); 672483d953aSJohn Baldwin if (rc < 0) { 673483d953aSJohn Baldwin fprintf(stderr, "Failed to parse progress\n"); 674483d953aSJohn Baldwin break; 675483d953aSJohn Baldwin } 676483d953aSJohn Baldwin 677483d953aSJohn Baldwin nanosleep(&ts, NULL); 678483d953aSJohn Baldwin } while (crtval < maxval); 679483d953aSJohn Baldwin 680483d953aSJohn Baldwin pthread_exit(NULL); 681483d953aSJohn Baldwin return NULL; 682483d953aSJohn Baldwin } 683483d953aSJohn Baldwin 684483d953aSJohn Baldwin static int 685483d953aSJohn Baldwin vm_snapshot_mem_part(const int snapfd, const size_t foff, void *src, 686483d953aSJohn Baldwin const size_t len, const size_t totalmem, const bool op_wr) 687483d953aSJohn Baldwin { 688483d953aSJohn Baldwin int rc; 689483d953aSJohn Baldwin size_t part_done, todo, rem; 690483d953aSJohn Baldwin ssize_t done; 691483d953aSJohn Baldwin bool show_progress; 692483d953aSJohn Baldwin pthread_t spinner_th; 693483d953aSJohn Baldwin struct spinner_info *si; 694483d953aSJohn Baldwin 695483d953aSJohn Baldwin if (lseek(snapfd, foff, SEEK_SET) < 0) { 696483d953aSJohn Baldwin perror("Failed to change file offset"); 697483d953aSJohn Baldwin return (-1); 698483d953aSJohn Baldwin } 699483d953aSJohn Baldwin 700483d953aSJohn Baldwin show_progress = false; 701483d953aSJohn Baldwin if (isatty(STDIN_FILENO) && (winsize.ws_col != 0)) 702483d953aSJohn Baldwin show_progress = true; 703483d953aSJohn Baldwin 704483d953aSJohn Baldwin part_done = foff; 705483d953aSJohn Baldwin rem = len; 706483d953aSJohn Baldwin 707483d953aSJohn Baldwin if (show_progress) { 708483d953aSJohn Baldwin si = &(struct spinner_info) { 709483d953aSJohn Baldwin .crtval = &part_done, 710483d953aSJohn Baldwin .maxval = foff + len, 711483d953aSJohn Baldwin .total = totalmem 712483d953aSJohn Baldwin }; 713483d953aSJohn Baldwin 714483d953aSJohn Baldwin rc = pthread_create(&spinner_th, 0, snapshot_spinner_cb, si); 715483d953aSJohn Baldwin if (rc) { 716483d953aSJohn Baldwin perror("Unable to create spinner thread"); 717483d953aSJohn Baldwin show_progress = false; 718483d953aSJohn Baldwin } 719483d953aSJohn Baldwin } 720483d953aSJohn Baldwin 721483d953aSJohn Baldwin while (rem > 0) { 722483d953aSJohn Baldwin if (show_progress) 723483d953aSJohn Baldwin todo = min(SNAPSHOT_CHUNK, rem); 724483d953aSJohn Baldwin else 725483d953aSJohn Baldwin todo = rem; 726483d953aSJohn Baldwin 727483d953aSJohn Baldwin if (op_wr) 728483d953aSJohn Baldwin done = write(snapfd, src, todo); 729483d953aSJohn Baldwin else 730483d953aSJohn Baldwin done = read(snapfd, src, todo); 731483d953aSJohn Baldwin if (done < 0) { 732483d953aSJohn Baldwin perror("Failed to write in file"); 733483d953aSJohn Baldwin return (-1); 734483d953aSJohn Baldwin } 735483d953aSJohn Baldwin 736c9faf698SMark Johnston src = (uint8_t *)src + done; 737483d953aSJohn Baldwin part_done += done; 738483d953aSJohn Baldwin rem -= done; 739483d953aSJohn Baldwin } 740483d953aSJohn Baldwin 741483d953aSJohn Baldwin if (show_progress) { 742483d953aSJohn Baldwin rc = pthread_join(spinner_th, NULL); 743483d953aSJohn Baldwin if (rc) 744483d953aSJohn Baldwin perror("Unable to end spinner thread"); 745483d953aSJohn Baldwin } 746483d953aSJohn Baldwin 747483d953aSJohn Baldwin return (0); 748483d953aSJohn Baldwin } 749483d953aSJohn Baldwin 750483d953aSJohn Baldwin static size_t 751483d953aSJohn Baldwin vm_snapshot_mem(struct vmctx *ctx, int snapfd, size_t memsz, const bool op_wr) 752483d953aSJohn Baldwin { 753483d953aSJohn Baldwin int ret; 754483d953aSJohn Baldwin size_t lowmem, highmem, totalmem; 755483d953aSJohn Baldwin char *baseaddr; 756483d953aSJohn Baldwin 757483d953aSJohn Baldwin ret = vm_get_guestmem_from_ctx(ctx, &baseaddr, &lowmem, &highmem); 758483d953aSJohn Baldwin if (ret) { 759483d953aSJohn Baldwin fprintf(stderr, "%s: unable to retrieve guest memory size\r\n", 760483d953aSJohn Baldwin __func__); 761483d953aSJohn Baldwin return (0); 762483d953aSJohn Baldwin } 763483d953aSJohn Baldwin totalmem = lowmem + highmem; 764483d953aSJohn Baldwin 765483d953aSJohn Baldwin if ((op_wr == false) && (totalmem != memsz)) { 766483d953aSJohn Baldwin fprintf(stderr, "%s: mem size mismatch: %ld vs %ld\r\n", 767483d953aSJohn Baldwin __func__, totalmem, memsz); 768483d953aSJohn Baldwin return (0); 769483d953aSJohn Baldwin } 770483d953aSJohn Baldwin 771483d953aSJohn Baldwin winsize.ws_col = 80; 772483d953aSJohn Baldwin #ifdef TIOCGWINSZ 773483d953aSJohn Baldwin ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize); 774483d953aSJohn Baldwin #endif /* TIOCGWINSZ */ 775483d953aSJohn Baldwin old_winch_handler = signal(SIGWINCH, winch_handler); 776483d953aSJohn Baldwin 777483d953aSJohn Baldwin ret = vm_snapshot_mem_part(snapfd, 0, baseaddr, lowmem, 778483d953aSJohn Baldwin totalmem, op_wr); 779483d953aSJohn Baldwin if (ret) { 780483d953aSJohn Baldwin fprintf(stderr, "%s: Could not %s lowmem\r\n", 781483d953aSJohn Baldwin __func__, op_wr ? "write" : "read"); 782483d953aSJohn Baldwin totalmem = 0; 783483d953aSJohn Baldwin goto done; 784483d953aSJohn Baldwin } 785483d953aSJohn Baldwin 786483d953aSJohn Baldwin if (highmem == 0) 787483d953aSJohn Baldwin goto done; 788483d953aSJohn Baldwin 789483d953aSJohn Baldwin ret = vm_snapshot_mem_part(snapfd, lowmem, baseaddr + 4*GB, 790483d953aSJohn Baldwin highmem, totalmem, op_wr); 791483d953aSJohn Baldwin if (ret) { 792483d953aSJohn Baldwin fprintf(stderr, "%s: Could not %s highmem\r\n", 793483d953aSJohn Baldwin __func__, op_wr ? "write" : "read"); 794483d953aSJohn Baldwin totalmem = 0; 795483d953aSJohn Baldwin goto done; 796483d953aSJohn Baldwin } 797483d953aSJohn Baldwin 798483d953aSJohn Baldwin done: 799483d953aSJohn Baldwin printf("\r\n"); 800483d953aSJohn Baldwin signal(SIGWINCH, old_winch_handler); 801483d953aSJohn Baldwin 802483d953aSJohn Baldwin return (totalmem); 803483d953aSJohn Baldwin } 804483d953aSJohn Baldwin 805483d953aSJohn Baldwin int 806483d953aSJohn Baldwin restore_vm_mem(struct vmctx *ctx, struct restore_state *rstate) 807483d953aSJohn Baldwin { 808483d953aSJohn Baldwin size_t restored; 809483d953aSJohn Baldwin 810483d953aSJohn Baldwin restored = vm_snapshot_mem(ctx, rstate->vmmem_fd, rstate->vmmem_len, 811483d953aSJohn Baldwin false); 812483d953aSJohn Baldwin 813483d953aSJohn Baldwin if (restored != rstate->vmmem_len) 814483d953aSJohn Baldwin return (-1); 815483d953aSJohn Baldwin 816483d953aSJohn Baldwin return (0); 817483d953aSJohn Baldwin } 818483d953aSJohn Baldwin 819ee5023f3SVitaliy Gusev int 820ee5023f3SVitaliy Gusev vm_restore_kern_structs(struct vmctx *ctx, struct restore_state *rstate) 821483d953aSJohn Baldwin { 822ee5023f3SVitaliy Gusev for (unsigned i = 0; i < nitems(snapshot_kern_structs); i++) { 823ee5023f3SVitaliy Gusev const struct vm_snapshot_kern_info *info; 824483d953aSJohn Baldwin struct vm_snapshot_meta *meta; 825ee5023f3SVitaliy Gusev void *data; 826ee5023f3SVitaliy Gusev size_t size; 827483d953aSJohn Baldwin 828ee5023f3SVitaliy Gusev info = &snapshot_kern_structs[i]; 829ee5023f3SVitaliy Gusev data = lookup_dev(info->struct_name, JSON_KERNEL_ARR_KEY, rstate, &size); 830ee5023f3SVitaliy Gusev if (data == NULL) 831ee5023f3SVitaliy Gusev errx(EX_DATAERR, "Cannot find kern struct %s", 832ee5023f3SVitaliy Gusev info->struct_name); 833483d953aSJohn Baldwin 834ee5023f3SVitaliy Gusev if (size == 0) 835ee5023f3SVitaliy Gusev errx(EX_DATAERR, "data with zero size for %s", 836ee5023f3SVitaliy Gusev info->struct_name); 837483d953aSJohn Baldwin 838483d953aSJohn Baldwin meta = &(struct vm_snapshot_meta) { 839483d953aSJohn Baldwin .dev_name = info->struct_name, 840483d953aSJohn Baldwin .dev_req = info->req, 841483d953aSJohn Baldwin 842ee5023f3SVitaliy Gusev .buffer.buf_start = data, 843ee5023f3SVitaliy Gusev .buffer.buf_size = size, 844483d953aSJohn Baldwin 845ee5023f3SVitaliy Gusev .buffer.buf = data, 846ee5023f3SVitaliy Gusev .buffer.buf_rem = size, 847483d953aSJohn Baldwin 848483d953aSJohn Baldwin .op = VM_SNAPSHOT_RESTORE, 849483d953aSJohn Baldwin }; 850483d953aSJohn Baldwin 851ee5023f3SVitaliy Gusev if (vm_snapshot_req(ctx, meta)) 852ee5023f3SVitaliy Gusev err(EX_DATAERR, "Failed to restore %s", 853ee5023f3SVitaliy Gusev info->struct_name); 854483d953aSJohn Baldwin } 855483d953aSJohn Baldwin return (0); 856483d953aSJohn Baldwin } 857483d953aSJohn Baldwin 858c9faf698SMark Johnston static int 859*b10d65a4SVitaliy Gusev vm_restore_device(struct restore_state *rstate, 860483d953aSJohn Baldwin const struct vm_snapshot_dev_info *info) 861483d953aSJohn Baldwin { 862483d953aSJohn Baldwin void *dev_ptr; 863483d953aSJohn Baldwin size_t dev_size; 864483d953aSJohn Baldwin int ret; 865483d953aSJohn Baldwin struct vm_snapshot_meta *meta; 866483d953aSJohn Baldwin 867ee5023f3SVitaliy Gusev dev_ptr = lookup_dev(info->dev_name, JSON_DEV_ARR_KEY, rstate, 868ee5023f3SVitaliy Gusev &dev_size); 869483d953aSJohn Baldwin if (dev_ptr == NULL) { 870483d953aSJohn Baldwin fprintf(stderr, "Failed to lookup dev: %s\r\n", info->dev_name); 871483d953aSJohn Baldwin fprintf(stderr, "Continuing the restore/migration process\r\n"); 872483d953aSJohn Baldwin return (0); 873483d953aSJohn Baldwin } 874483d953aSJohn Baldwin 875483d953aSJohn Baldwin if (dev_size == 0) { 876483d953aSJohn Baldwin fprintf(stderr, "%s: Device size is 0. " 877483d953aSJohn Baldwin "Assuming %s is not used\r\n", 878483d953aSJohn Baldwin __func__, info->dev_name); 879483d953aSJohn Baldwin return (0); 880483d953aSJohn Baldwin } 881483d953aSJohn Baldwin 882483d953aSJohn Baldwin meta = &(struct vm_snapshot_meta) { 883483d953aSJohn Baldwin .dev_name = info->dev_name, 884483d953aSJohn Baldwin 885483d953aSJohn Baldwin .buffer.buf_start = dev_ptr, 886483d953aSJohn Baldwin .buffer.buf_size = dev_size, 887483d953aSJohn Baldwin 888483d953aSJohn Baldwin .buffer.buf = dev_ptr, 889483d953aSJohn Baldwin .buffer.buf_rem = dev_size, 890483d953aSJohn Baldwin 891483d953aSJohn Baldwin .op = VM_SNAPSHOT_RESTORE, 892483d953aSJohn Baldwin }; 893483d953aSJohn Baldwin 894483d953aSJohn Baldwin ret = (*info->snapshot_cb)(meta); 895483d953aSJohn Baldwin if (ret != 0) { 896483d953aSJohn Baldwin fprintf(stderr, "Failed to restore dev: %s\r\n", 897483d953aSJohn Baldwin info->dev_name); 898483d953aSJohn Baldwin return (-1); 899483d953aSJohn Baldwin } 900483d953aSJohn Baldwin 901483d953aSJohn Baldwin return (0); 902483d953aSJohn Baldwin } 903483d953aSJohn Baldwin 904483d953aSJohn Baldwin int 905*b10d65a4SVitaliy Gusev vm_restore_devices(struct restore_state *rstate) 906483d953aSJohn Baldwin { 907c9faf698SMark Johnston size_t i; 908483d953aSJohn Baldwin int ret; 909483d953aSJohn Baldwin 910483d953aSJohn Baldwin for (i = 0; i < nitems(snapshot_devs); i++) { 911*b10d65a4SVitaliy Gusev ret = vm_restore_device(rstate, &snapshot_devs[i]); 912483d953aSJohn Baldwin if (ret != 0) 913483d953aSJohn Baldwin return (ret); 914483d953aSJohn Baldwin } 915483d953aSJohn Baldwin 916483d953aSJohn Baldwin return 0; 917483d953aSJohn Baldwin } 918483d953aSJohn Baldwin 919483d953aSJohn Baldwin int 920*b10d65a4SVitaliy Gusev vm_pause_devices(void) 921483d953aSJohn Baldwin { 922483d953aSJohn Baldwin const struct vm_snapshot_dev_info *info; 923c9faf698SMark Johnston size_t i; 924483d953aSJohn Baldwin int ret; 925483d953aSJohn Baldwin 926483d953aSJohn Baldwin for (i = 0; i < nitems(snapshot_devs); i++) { 927483d953aSJohn Baldwin info = &snapshot_devs[i]; 928483d953aSJohn Baldwin if (info->pause_cb == NULL) 929483d953aSJohn Baldwin continue; 930483d953aSJohn Baldwin 9316a284cacSJohn Baldwin ret = info->pause_cb(info->dev_name); 932483d953aSJohn Baldwin if (ret != 0) 933483d953aSJohn Baldwin return (ret); 934483d953aSJohn Baldwin } 935483d953aSJohn Baldwin 936483d953aSJohn Baldwin return (0); 937483d953aSJohn Baldwin } 938483d953aSJohn Baldwin 939483d953aSJohn Baldwin int 940*b10d65a4SVitaliy Gusev vm_resume_devices(void) 941483d953aSJohn Baldwin { 942483d953aSJohn Baldwin const struct vm_snapshot_dev_info *info; 943c9faf698SMark Johnston size_t i; 944483d953aSJohn Baldwin int ret; 945483d953aSJohn Baldwin 946483d953aSJohn Baldwin for (i = 0; i < nitems(snapshot_devs); i++) { 947483d953aSJohn Baldwin info = &snapshot_devs[i]; 948483d953aSJohn Baldwin if (info->resume_cb == NULL) 949483d953aSJohn Baldwin continue; 950483d953aSJohn Baldwin 9516a284cacSJohn Baldwin ret = info->resume_cb(info->dev_name); 952483d953aSJohn Baldwin if (ret != 0) 953483d953aSJohn Baldwin return (ret); 954483d953aSJohn Baldwin } 955483d953aSJohn Baldwin 956483d953aSJohn Baldwin return (0); 957483d953aSJohn Baldwin } 958483d953aSJohn Baldwin 959483d953aSJohn Baldwin static int 960ee5023f3SVitaliy Gusev vm_save_kern_struct(struct vmctx *ctx, int data_fd, xo_handle_t *xop, 9610f735657SJohn Baldwin const char *array_key, struct vm_snapshot_meta *meta, off_t *offset) 962483d953aSJohn Baldwin { 963483d953aSJohn Baldwin int ret; 964483d953aSJohn Baldwin size_t data_size; 965483d953aSJohn Baldwin ssize_t write_cnt; 966483d953aSJohn Baldwin 9670f735657SJohn Baldwin ret = vm_snapshot_req(ctx, meta); 968483d953aSJohn Baldwin if (ret != 0) { 969483d953aSJohn Baldwin fprintf(stderr, "%s: Failed to snapshot struct %s\r\n", 970483d953aSJohn Baldwin __func__, meta->dev_name); 971483d953aSJohn Baldwin ret = -1; 972483d953aSJohn Baldwin goto done; 973483d953aSJohn Baldwin } 974483d953aSJohn Baldwin 975483d953aSJohn Baldwin data_size = vm_get_snapshot_size(meta); 976483d953aSJohn Baldwin 977ed721684SMark Johnston /* XXX-MJ no handling for short writes. */ 978483d953aSJohn Baldwin write_cnt = write(data_fd, meta->buffer.buf_start, data_size); 979ed721684SMark Johnston if (write_cnt < 0 || (size_t)write_cnt != data_size) { 980483d953aSJohn Baldwin perror("Failed to write all snapshotted data."); 981483d953aSJohn Baldwin ret = -1; 982483d953aSJohn Baldwin goto done; 983483d953aSJohn Baldwin } 984483d953aSJohn Baldwin 985483d953aSJohn Baldwin /* Write metadata. */ 986483d953aSJohn Baldwin xo_open_instance_h(xop, array_key); 987ee5023f3SVitaliy Gusev xo_emit_h(xop, "{:" JSON_SNAPSHOT_REQ_KEY "/%s}\n", 988ee5023f3SVitaliy Gusev meta->dev_name); 989483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_SIZE_KEY "/%lu}\n", data_size); 990483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_FILE_OFFSET_KEY "/%lu}\n", *offset); 99150aef9f6SVitaliy Gusev xo_close_instance_h(xop, JSON_KERNEL_ARR_KEY); 992483d953aSJohn Baldwin 993483d953aSJohn Baldwin *offset += data_size; 994483d953aSJohn Baldwin 995483d953aSJohn Baldwin done: 996483d953aSJohn Baldwin return (ret); 997483d953aSJohn Baldwin } 998483d953aSJohn Baldwin 999483d953aSJohn Baldwin static int 1000ee5023f3SVitaliy Gusev vm_save_kern_structs(struct vmctx *ctx, int data_fd, xo_handle_t *xop) 1001483d953aSJohn Baldwin { 1002c9faf698SMark Johnston int ret, error; 1003c9faf698SMark Johnston size_t buf_size, i, offset; 1004483d953aSJohn Baldwin char *buffer; 1005483d953aSJohn Baldwin struct vm_snapshot_meta *meta; 1006483d953aSJohn Baldwin 1007483d953aSJohn Baldwin error = 0; 1008483d953aSJohn Baldwin offset = 0; 1009483d953aSJohn Baldwin buf_size = SNAPSHOT_BUFFER_SIZE; 1010483d953aSJohn Baldwin 1011483d953aSJohn Baldwin buffer = malloc(SNAPSHOT_BUFFER_SIZE * sizeof(char)); 1012483d953aSJohn Baldwin if (buffer == NULL) { 1013483d953aSJohn Baldwin error = ENOMEM; 1014483d953aSJohn Baldwin perror("Failed to allocate memory for snapshot buffer"); 1015483d953aSJohn Baldwin goto err_vm_snapshot_kern_data; 1016483d953aSJohn Baldwin } 1017483d953aSJohn Baldwin 1018483d953aSJohn Baldwin meta = &(struct vm_snapshot_meta) { 1019483d953aSJohn Baldwin .buffer.buf_start = buffer, 1020483d953aSJohn Baldwin .buffer.buf_size = buf_size, 1021483d953aSJohn Baldwin 1022483d953aSJohn Baldwin .op = VM_SNAPSHOT_SAVE, 1023483d953aSJohn Baldwin }; 1024483d953aSJohn Baldwin 102550aef9f6SVitaliy Gusev xo_open_list_h(xop, JSON_KERNEL_ARR_KEY); 1026483d953aSJohn Baldwin for (i = 0; i < nitems(snapshot_kern_structs); i++) { 1027483d953aSJohn Baldwin meta->dev_name = snapshot_kern_structs[i].struct_name; 1028483d953aSJohn Baldwin meta->dev_req = snapshot_kern_structs[i].req; 1029483d953aSJohn Baldwin 1030483d953aSJohn Baldwin memset(meta->buffer.buf_start, 0, meta->buffer.buf_size); 1031483d953aSJohn Baldwin meta->buffer.buf = meta->buffer.buf_start; 1032483d953aSJohn Baldwin meta->buffer.buf_rem = meta->buffer.buf_size; 1033483d953aSJohn Baldwin 1034ee5023f3SVitaliy Gusev ret = vm_save_kern_struct(ctx, data_fd, xop, 10350f735657SJohn Baldwin JSON_DEV_ARR_KEY, meta, &offset); 1036483d953aSJohn Baldwin if (ret != 0) { 1037483d953aSJohn Baldwin error = -1; 1038483d953aSJohn Baldwin goto err_vm_snapshot_kern_data; 1039483d953aSJohn Baldwin } 1040483d953aSJohn Baldwin } 104150aef9f6SVitaliy Gusev xo_close_list_h(xop, JSON_KERNEL_ARR_KEY); 1042483d953aSJohn Baldwin 1043483d953aSJohn Baldwin err_vm_snapshot_kern_data: 1044483d953aSJohn Baldwin if (buffer != NULL) 1045483d953aSJohn Baldwin free(buffer); 1046483d953aSJohn Baldwin return (error); 1047483d953aSJohn Baldwin } 1048483d953aSJohn Baldwin 1049483d953aSJohn Baldwin static int 1050483d953aSJohn Baldwin vm_snapshot_basic_metadata(struct vmctx *ctx, xo_handle_t *xop, size_t memsz) 1051483d953aSJohn Baldwin { 1052483d953aSJohn Baldwin 1053483d953aSJohn Baldwin xo_open_container_h(xop, JSON_BASIC_METADATA_KEY); 1054483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_NCPUS_KEY "/%ld}\n", guest_ncpus); 10553efc45f3SRobert Wing xo_emit_h(xop, "{:" JSON_VMNAME_KEY "/%s}\n", vm_get_name(ctx)); 1056483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_MEMSIZE_KEY "/%lu}\n", memsz); 10573efc45f3SRobert Wing xo_emit_h(xop, "{:" JSON_MEMFLAGS_KEY "/%d}\n", vm_get_memflags(ctx)); 1058483d953aSJohn Baldwin xo_close_container_h(xop, JSON_BASIC_METADATA_KEY); 1059483d953aSJohn Baldwin 10603efc45f3SRobert Wing return (0); 1061483d953aSJohn Baldwin } 1062483d953aSJohn Baldwin 1063483d953aSJohn Baldwin static int 1064483d953aSJohn Baldwin vm_snapshot_dev_write_data(int data_fd, xo_handle_t *xop, const char *array_key, 1065483d953aSJohn Baldwin struct vm_snapshot_meta *meta, off_t *offset) 1066483d953aSJohn Baldwin { 1067ed721684SMark Johnston ssize_t ret; 1068483d953aSJohn Baldwin size_t data_size; 1069483d953aSJohn Baldwin 1070483d953aSJohn Baldwin data_size = vm_get_snapshot_size(meta); 1071483d953aSJohn Baldwin 1072ed721684SMark Johnston /* XXX-MJ no handling for short writes. */ 1073483d953aSJohn Baldwin ret = write(data_fd, meta->buffer.buf_start, data_size); 1074ed721684SMark Johnston if (ret < 0 || (size_t)ret != data_size) { 1075483d953aSJohn Baldwin perror("Failed to write all snapshotted data."); 1076483d953aSJohn Baldwin return (-1); 1077483d953aSJohn Baldwin } 1078483d953aSJohn Baldwin 1079483d953aSJohn Baldwin /* Write metadata. */ 1080483d953aSJohn Baldwin xo_open_instance_h(xop, array_key); 1081483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_SNAPSHOT_REQ_KEY "/%s}\n", meta->dev_name); 1082483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_SIZE_KEY "/%lu}\n", data_size); 1083483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_FILE_OFFSET_KEY "/%lu}\n", *offset); 1084483d953aSJohn Baldwin xo_close_instance_h(xop, array_key); 1085483d953aSJohn Baldwin 1086483d953aSJohn Baldwin *offset += data_size; 1087483d953aSJohn Baldwin 1088483d953aSJohn Baldwin return (0); 1089483d953aSJohn Baldwin } 1090483d953aSJohn Baldwin 1091483d953aSJohn Baldwin static int 1092*b10d65a4SVitaliy Gusev vm_snapshot_device(const struct vm_snapshot_dev_info *info, 1093483d953aSJohn Baldwin int data_fd, xo_handle_t *xop, 1094483d953aSJohn Baldwin struct vm_snapshot_meta *meta, off_t *offset) 1095483d953aSJohn Baldwin { 1096483d953aSJohn Baldwin int ret; 1097483d953aSJohn Baldwin 1098483d953aSJohn Baldwin ret = (*info->snapshot_cb)(meta); 1099483d953aSJohn Baldwin if (ret != 0) { 1100483d953aSJohn Baldwin fprintf(stderr, "Failed to snapshot %s; ret=%d\r\n", 1101483d953aSJohn Baldwin meta->dev_name, ret); 1102483d953aSJohn Baldwin return (ret); 1103483d953aSJohn Baldwin } 1104483d953aSJohn Baldwin 1105483d953aSJohn Baldwin ret = vm_snapshot_dev_write_data(data_fd, xop, JSON_DEV_ARR_KEY, meta, 1106483d953aSJohn Baldwin offset); 1107483d953aSJohn Baldwin if (ret != 0) 1108483d953aSJohn Baldwin return (ret); 1109483d953aSJohn Baldwin 1110483d953aSJohn Baldwin return (0); 1111483d953aSJohn Baldwin } 1112483d953aSJohn Baldwin 1113483d953aSJohn Baldwin static int 1114*b10d65a4SVitaliy Gusev vm_snapshot_devices(int data_fd, xo_handle_t *xop) 1115483d953aSJohn Baldwin { 1116c9faf698SMark Johnston int ret; 1117483d953aSJohn Baldwin off_t offset; 1118483d953aSJohn Baldwin void *buffer; 1119c9faf698SMark Johnston size_t buf_size, i; 1120483d953aSJohn Baldwin struct vm_snapshot_meta *meta; 1121483d953aSJohn Baldwin 1122483d953aSJohn Baldwin buf_size = SNAPSHOT_BUFFER_SIZE; 1123483d953aSJohn Baldwin 1124483d953aSJohn Baldwin offset = lseek(data_fd, 0, SEEK_CUR); 1125483d953aSJohn Baldwin if (offset < 0) { 1126483d953aSJohn Baldwin perror("Failed to get data file current offset."); 1127483d953aSJohn Baldwin return (-1); 1128483d953aSJohn Baldwin } 1129483d953aSJohn Baldwin 1130483d953aSJohn Baldwin buffer = malloc(buf_size); 1131483d953aSJohn Baldwin if (buffer == NULL) { 1132483d953aSJohn Baldwin perror("Failed to allocate memory for snapshot buffer"); 1133483d953aSJohn Baldwin ret = ENOSPC; 1134483d953aSJohn Baldwin goto snapshot_err; 1135483d953aSJohn Baldwin } 1136483d953aSJohn Baldwin 1137483d953aSJohn Baldwin meta = &(struct vm_snapshot_meta) { 1138483d953aSJohn Baldwin .buffer.buf_start = buffer, 1139483d953aSJohn Baldwin .buffer.buf_size = buf_size, 1140483d953aSJohn Baldwin 1141483d953aSJohn Baldwin .op = VM_SNAPSHOT_SAVE, 1142483d953aSJohn Baldwin }; 1143483d953aSJohn Baldwin 1144483d953aSJohn Baldwin xo_open_list_h(xop, JSON_DEV_ARR_KEY); 1145483d953aSJohn Baldwin 1146483d953aSJohn Baldwin /* Restore other devices that support this feature */ 1147483d953aSJohn Baldwin for (i = 0; i < nitems(snapshot_devs); i++) { 1148483d953aSJohn Baldwin meta->dev_name = snapshot_devs[i].dev_name; 1149483d953aSJohn Baldwin 1150483d953aSJohn Baldwin memset(meta->buffer.buf_start, 0, meta->buffer.buf_size); 1151483d953aSJohn Baldwin meta->buffer.buf = meta->buffer.buf_start; 1152483d953aSJohn Baldwin meta->buffer.buf_rem = meta->buffer.buf_size; 1153483d953aSJohn Baldwin 1154*b10d65a4SVitaliy Gusev ret = vm_snapshot_device(&snapshot_devs[i], data_fd, xop, 1155483d953aSJohn Baldwin meta, &offset); 1156483d953aSJohn Baldwin if (ret != 0) 1157483d953aSJohn Baldwin goto snapshot_err; 1158483d953aSJohn Baldwin } 1159483d953aSJohn Baldwin 1160483d953aSJohn Baldwin xo_close_list_h(xop, JSON_DEV_ARR_KEY); 1161483d953aSJohn Baldwin 1162483d953aSJohn Baldwin snapshot_err: 1163483d953aSJohn Baldwin if (buffer != NULL) 1164483d953aSJohn Baldwin free(buffer); 1165483d953aSJohn Baldwin return (ret); 1166483d953aSJohn Baldwin } 1167483d953aSJohn Baldwin 1168483d953aSJohn Baldwin void 1169483d953aSJohn Baldwin checkpoint_cpu_add(int vcpu) 1170483d953aSJohn Baldwin { 1171483d953aSJohn Baldwin 1172483d953aSJohn Baldwin pthread_mutex_lock(&vcpu_lock); 1173483d953aSJohn Baldwin CPU_SET(vcpu, &vcpus_active); 1174483d953aSJohn Baldwin 1175483d953aSJohn Baldwin if (checkpoint_active) { 1176483d953aSJohn Baldwin CPU_SET(vcpu, &vcpus_suspended); 1177483d953aSJohn Baldwin while (checkpoint_active) 1178483d953aSJohn Baldwin pthread_cond_wait(&vcpus_can_run, &vcpu_lock); 1179483d953aSJohn Baldwin CPU_CLR(vcpu, &vcpus_suspended); 1180483d953aSJohn Baldwin } 1181483d953aSJohn Baldwin pthread_mutex_unlock(&vcpu_lock); 1182483d953aSJohn Baldwin } 1183483d953aSJohn Baldwin 1184483d953aSJohn Baldwin /* 1185483d953aSJohn Baldwin * When a vCPU is suspended for any reason, it calls 1186483d953aSJohn Baldwin * checkpoint_cpu_suspend(). This records that the vCPU is idle. 1187483d953aSJohn Baldwin * Before returning from suspension, checkpoint_cpu_resume() is 1188483d953aSJohn Baldwin * called. In suspend we note that the vCPU is idle. In resume we 1189483d953aSJohn Baldwin * pause the vCPU thread until the checkpoint is complete. The reason 1190483d953aSJohn Baldwin * for the two-step process is that vCPUs might already be stopped in 1191483d953aSJohn Baldwin * the debug server when a checkpoint is requested. This approach 1192483d953aSJohn Baldwin * allows us to account for and handle those vCPUs. 1193483d953aSJohn Baldwin */ 1194483d953aSJohn Baldwin void 1195483d953aSJohn Baldwin checkpoint_cpu_suspend(int vcpu) 1196483d953aSJohn Baldwin { 1197483d953aSJohn Baldwin 1198483d953aSJohn Baldwin pthread_mutex_lock(&vcpu_lock); 1199483d953aSJohn Baldwin CPU_SET(vcpu, &vcpus_suspended); 1200483d953aSJohn Baldwin if (checkpoint_active && CPU_CMP(&vcpus_active, &vcpus_suspended) == 0) 1201483d953aSJohn Baldwin pthread_cond_signal(&vcpus_idle); 1202483d953aSJohn Baldwin pthread_mutex_unlock(&vcpu_lock); 1203483d953aSJohn Baldwin } 1204483d953aSJohn Baldwin 1205483d953aSJohn Baldwin void 1206483d953aSJohn Baldwin checkpoint_cpu_resume(int vcpu) 1207483d953aSJohn Baldwin { 1208483d953aSJohn Baldwin 1209483d953aSJohn Baldwin pthread_mutex_lock(&vcpu_lock); 1210483d953aSJohn Baldwin while (checkpoint_active) 1211483d953aSJohn Baldwin pthread_cond_wait(&vcpus_can_run, &vcpu_lock); 1212483d953aSJohn Baldwin CPU_CLR(vcpu, &vcpus_suspended); 1213483d953aSJohn Baldwin pthread_mutex_unlock(&vcpu_lock); 1214483d953aSJohn Baldwin } 1215483d953aSJohn Baldwin 1216483d953aSJohn Baldwin static void 1217483d953aSJohn Baldwin vm_vcpu_pause(struct vmctx *ctx) 1218483d953aSJohn Baldwin { 1219483d953aSJohn Baldwin 1220483d953aSJohn Baldwin pthread_mutex_lock(&vcpu_lock); 1221483d953aSJohn Baldwin checkpoint_active = true; 12227d9ef309SJohn Baldwin vm_suspend_all_cpus(ctx); 1223483d953aSJohn Baldwin while (CPU_CMP(&vcpus_active, &vcpus_suspended) != 0) 1224483d953aSJohn Baldwin pthread_cond_wait(&vcpus_idle, &vcpu_lock); 1225483d953aSJohn Baldwin pthread_mutex_unlock(&vcpu_lock); 1226483d953aSJohn Baldwin } 1227483d953aSJohn Baldwin 1228483d953aSJohn Baldwin static void 1229483d953aSJohn Baldwin vm_vcpu_resume(struct vmctx *ctx) 1230483d953aSJohn Baldwin { 1231483d953aSJohn Baldwin 1232483d953aSJohn Baldwin pthread_mutex_lock(&vcpu_lock); 1233483d953aSJohn Baldwin checkpoint_active = false; 1234483d953aSJohn Baldwin pthread_mutex_unlock(&vcpu_lock); 12357d9ef309SJohn Baldwin vm_resume_all_cpus(ctx); 1236483d953aSJohn Baldwin pthread_cond_broadcast(&vcpus_can_run); 1237483d953aSJohn Baldwin } 1238483d953aSJohn Baldwin 1239483d953aSJohn Baldwin static int 124018126b64SVitaliy Gusev vm_checkpoint(struct vmctx *ctx, int fddir, const char *checkpoint_file, 124118126b64SVitaliy Gusev bool stop_vm) 1242483d953aSJohn Baldwin { 124318126b64SVitaliy Gusev int fd_checkpoint = 0, kdata_fd = 0, fd_meta; 1244483d953aSJohn Baldwin int ret = 0; 1245483d953aSJohn Baldwin int error = 0; 1246483d953aSJohn Baldwin size_t memsz; 1247483d953aSJohn Baldwin xo_handle_t *xop = NULL; 1248483d953aSJohn Baldwin char *meta_filename = NULL; 1249483d953aSJohn Baldwin char *kdata_filename = NULL; 1250483d953aSJohn Baldwin FILE *meta_file = NULL; 1251483d953aSJohn Baldwin 1252483d953aSJohn Baldwin kdata_filename = strcat_extension(checkpoint_file, ".kern"); 1253483d953aSJohn Baldwin if (kdata_filename == NULL) { 1254483d953aSJohn Baldwin fprintf(stderr, "Failed to construct kernel data filename.\n"); 1255483d953aSJohn Baldwin return (-1); 1256483d953aSJohn Baldwin } 1257483d953aSJohn Baldwin 125818126b64SVitaliy Gusev kdata_fd = openat(fddir, kdata_filename, O_WRONLY | O_CREAT | O_TRUNC, 0700); 1259483d953aSJohn Baldwin if (kdata_fd < 0) { 1260483d953aSJohn Baldwin perror("Failed to open kernel data snapshot file."); 1261483d953aSJohn Baldwin error = -1; 1262483d953aSJohn Baldwin goto done; 1263483d953aSJohn Baldwin } 1264483d953aSJohn Baldwin 126518126b64SVitaliy Gusev fd_checkpoint = openat(fddir, checkpoint_file, O_RDWR | O_CREAT | O_TRUNC, 0700); 1266483d953aSJohn Baldwin 1267483d953aSJohn Baldwin if (fd_checkpoint < 0) { 1268483d953aSJohn Baldwin perror("Failed to create checkpoint file"); 1269483d953aSJohn Baldwin error = -1; 1270483d953aSJohn Baldwin goto done; 1271483d953aSJohn Baldwin } 1272483d953aSJohn Baldwin 1273483d953aSJohn Baldwin meta_filename = strcat_extension(checkpoint_file, ".meta"); 1274483d953aSJohn Baldwin if (meta_filename == NULL) { 1275483d953aSJohn Baldwin fprintf(stderr, "Failed to construct vm metadata filename.\n"); 1276483d953aSJohn Baldwin goto done; 1277483d953aSJohn Baldwin } 1278483d953aSJohn Baldwin 127918126b64SVitaliy Gusev fd_meta = openat(fddir, meta_filename, O_WRONLY | O_CREAT | O_TRUNC, 0700); 128018126b64SVitaliy Gusev if (fd_meta != -1) 128118126b64SVitaliy Gusev meta_file = fdopen(fd_meta, "w"); 1282483d953aSJohn Baldwin if (meta_file == NULL) { 1283483d953aSJohn Baldwin perror("Failed to open vm metadata snapshot file."); 128418126b64SVitaliy Gusev close(fd_meta); 1285483d953aSJohn Baldwin goto done; 1286483d953aSJohn Baldwin } 1287483d953aSJohn Baldwin 1288483d953aSJohn Baldwin xop = xo_create_to_file(meta_file, XO_STYLE_JSON, XOF_PRETTY); 1289483d953aSJohn Baldwin if (xop == NULL) { 1290483d953aSJohn Baldwin perror("Failed to get libxo handle on metadata file."); 1291483d953aSJohn Baldwin goto done; 1292483d953aSJohn Baldwin } 1293483d953aSJohn Baldwin 1294483d953aSJohn Baldwin vm_vcpu_pause(ctx); 1295483d953aSJohn Baldwin 1296*b10d65a4SVitaliy Gusev ret = vm_pause_devices(); 1297483d953aSJohn Baldwin if (ret != 0) { 1298483d953aSJohn Baldwin fprintf(stderr, "Could not pause devices\r\n"); 1299483d953aSJohn Baldwin error = ret; 1300483d953aSJohn Baldwin goto done; 1301483d953aSJohn Baldwin } 1302483d953aSJohn Baldwin 1303483d953aSJohn Baldwin memsz = vm_snapshot_mem(ctx, fd_checkpoint, 0, true); 1304483d953aSJohn Baldwin if (memsz == 0) { 1305483d953aSJohn Baldwin perror("Could not write guest memory to file"); 1306483d953aSJohn Baldwin error = -1; 1307483d953aSJohn Baldwin goto done; 1308483d953aSJohn Baldwin } 1309483d953aSJohn Baldwin 1310483d953aSJohn Baldwin ret = vm_snapshot_basic_metadata(ctx, xop, memsz); 1311483d953aSJohn Baldwin if (ret != 0) { 1312483d953aSJohn Baldwin fprintf(stderr, "Failed to snapshot vm basic metadata.\n"); 1313483d953aSJohn Baldwin error = -1; 1314483d953aSJohn Baldwin goto done; 1315483d953aSJohn Baldwin } 1316483d953aSJohn Baldwin 1317ee5023f3SVitaliy Gusev ret = vm_save_kern_structs(ctx, kdata_fd, xop); 1318483d953aSJohn Baldwin if (ret != 0) { 1319483d953aSJohn Baldwin fprintf(stderr, "Failed to snapshot vm kernel data.\n"); 1320483d953aSJohn Baldwin error = -1; 1321483d953aSJohn Baldwin goto done; 1322483d953aSJohn Baldwin } 1323483d953aSJohn Baldwin 1324*b10d65a4SVitaliy Gusev ret = vm_snapshot_devices(kdata_fd, xop); 1325483d953aSJohn Baldwin if (ret != 0) { 1326483d953aSJohn Baldwin fprintf(stderr, "Failed to snapshot device state.\n"); 1327483d953aSJohn Baldwin error = -1; 1328483d953aSJohn Baldwin goto done; 1329483d953aSJohn Baldwin } 1330483d953aSJohn Baldwin 1331483d953aSJohn Baldwin xo_finish_h(xop); 1332483d953aSJohn Baldwin 1333483d953aSJohn Baldwin if (stop_vm) { 1334483d953aSJohn Baldwin vm_destroy(ctx); 1335483d953aSJohn Baldwin exit(0); 1336483d953aSJohn Baldwin } 1337483d953aSJohn Baldwin 1338483d953aSJohn Baldwin done: 1339*b10d65a4SVitaliy Gusev ret = vm_resume_devices(); 1340483d953aSJohn Baldwin if (ret != 0) 1341483d953aSJohn Baldwin fprintf(stderr, "Could not resume devices\r\n"); 1342483d953aSJohn Baldwin vm_vcpu_resume(ctx); 1343483d953aSJohn Baldwin if (fd_checkpoint > 0) 1344483d953aSJohn Baldwin close(fd_checkpoint); 1345483d953aSJohn Baldwin if (meta_filename != NULL) 1346483d953aSJohn Baldwin free(meta_filename); 1347483d953aSJohn Baldwin if (kdata_filename != NULL) 1348483d953aSJohn Baldwin free(kdata_filename); 1349483d953aSJohn Baldwin if (xop != NULL) 1350483d953aSJohn Baldwin xo_destroy(xop); 1351483d953aSJohn Baldwin if (meta_file != NULL) 1352483d953aSJohn Baldwin fclose(meta_file); 1353483d953aSJohn Baldwin if (kdata_fd > 0) 1354483d953aSJohn Baldwin close(kdata_fd); 1355483d953aSJohn Baldwin return (error); 1356483d953aSJohn Baldwin } 1357483d953aSJohn Baldwin 1358edfb339dSRobert Wing static int 1359edfb339dSRobert Wing handle_message(struct vmctx *ctx, nvlist_t *nvl) 1360483d953aSJohn Baldwin { 13614379c1daSRobert Wing const char *cmd; 1362c79331a4SRobert Wing struct ipc_command **ipc_cmd; 1363483d953aSJohn Baldwin 13644379c1daSRobert Wing if (!nvlist_exists_string(nvl, "cmd")) 1365c79331a4SRobert Wing return (EINVAL); 1366edfb339dSRobert Wing 13674379c1daSRobert Wing cmd = nvlist_get_string(nvl, "cmd"); 1368c79331a4SRobert Wing IPC_COMMAND_FOREACH(ipc_cmd, ipc_cmd_set) { 1369c79331a4SRobert Wing if (strcmp(cmd, (*ipc_cmd)->name) == 0) 1370c79331a4SRobert Wing return ((*ipc_cmd)->handler(ctx, nvl)); 1371483d953aSJohn Baldwin } 1372483d953aSJohn Baldwin 1373c79331a4SRobert Wing return (EOPNOTSUPP); 1374483d953aSJohn Baldwin } 1375483d953aSJohn Baldwin 1376483d953aSJohn Baldwin /* 1377483d953aSJohn Baldwin * Listen for commands from bhyvectl 1378483d953aSJohn Baldwin */ 1379483d953aSJohn Baldwin void * 1380483d953aSJohn Baldwin checkpoint_thread(void *param) 1381483d953aSJohn Baldwin { 1382690b7ea0SRobert Wing int fd; 1383483d953aSJohn Baldwin struct checkpoint_thread_info *thread_info; 1384edfb339dSRobert Wing nvlist_t *nvl; 1385483d953aSJohn Baldwin 1386483d953aSJohn Baldwin pthread_set_name_np(pthread_self(), "checkpoint thread"); 1387483d953aSJohn Baldwin thread_info = (struct checkpoint_thread_info *)param; 1388483d953aSJohn Baldwin 1389690b7ea0SRobert Wing while ((fd = accept(thread_info->socket_fd, NULL, NULL)) != -1) { 1390690b7ea0SRobert Wing nvl = nvlist_recv(fd, 0); 1391edfb339dSRobert Wing if (nvl != NULL) 1392edfb339dSRobert Wing handle_message(thread_info->ctx, nvl); 139338dfb062SRobert Wing else 1394edfb339dSRobert Wing EPRINTLN("nvlist_recv() failed: %s", strerror(errno)); 1395c79331a4SRobert Wing 1396690b7ea0SRobert Wing close(fd); 1397c79331a4SRobert Wing nvlist_destroy(nvl); 1398483d953aSJohn Baldwin } 1399483d953aSJohn Baldwin 1400483d953aSJohn Baldwin return (NULL); 1401483d953aSJohn Baldwin } 1402483d953aSJohn Baldwin 1403c79331a4SRobert Wing static int 1404c79331a4SRobert Wing vm_do_checkpoint(struct vmctx *ctx, const nvlist_t *nvl) 1405c79331a4SRobert Wing { 1406c79331a4SRobert Wing int error; 1407c79331a4SRobert Wing 1408c79331a4SRobert Wing if (!nvlist_exists_string(nvl, "filename") || 140918126b64SVitaliy Gusev !nvlist_exists_bool(nvl, "suspend") || 141018126b64SVitaliy Gusev !nvlist_exists_descriptor(nvl, "fddir")) 1411c79331a4SRobert Wing error = EINVAL; 1412c79331a4SRobert Wing else 141318126b64SVitaliy Gusev error = vm_checkpoint(ctx, 141418126b64SVitaliy Gusev nvlist_get_descriptor(nvl, "fddir"), 141518126b64SVitaliy Gusev nvlist_get_string(nvl, "filename"), 1416c79331a4SRobert Wing nvlist_get_bool(nvl, "suspend")); 1417c79331a4SRobert Wing 1418c79331a4SRobert Wing return (error); 1419c79331a4SRobert Wing } 1420c79331a4SRobert Wing IPC_COMMAND(ipc_cmd_set, checkpoint, vm_do_checkpoint); 1421c79331a4SRobert Wing 1422fdbc86cfSRobert Wing void 1423fdbc86cfSRobert Wing init_snapshot(void) 1424fdbc86cfSRobert Wing { 1425fdbc86cfSRobert Wing int err; 1426fdbc86cfSRobert Wing 1427fdbc86cfSRobert Wing err = pthread_mutex_init(&vcpu_lock, NULL); 1428fdbc86cfSRobert Wing if (err != 0) 1429fdbc86cfSRobert Wing errc(1, err, "checkpoint mutex init"); 1430fdbc86cfSRobert Wing err = pthread_cond_init(&vcpus_idle, NULL); 1431fdbc86cfSRobert Wing if (err != 0) 1432fdbc86cfSRobert Wing errc(1, err, "checkpoint cv init (vcpus_idle)"); 1433fdbc86cfSRobert Wing err = pthread_cond_init(&vcpus_can_run, NULL); 1434fdbc86cfSRobert Wing if (err != 0) 1435fdbc86cfSRobert Wing errc(1, err, "checkpoint cv init (vcpus_can_run)"); 1436fdbc86cfSRobert Wing } 1437fdbc86cfSRobert Wing 1438483d953aSJohn Baldwin /* 1439483d953aSJohn Baldwin * Create the listening socket for IPC with bhyvectl 1440483d953aSJohn Baldwin */ 1441483d953aSJohn Baldwin int 1442483d953aSJohn Baldwin init_checkpoint_thread(struct vmctx *ctx) 1443483d953aSJohn Baldwin { 1444483d953aSJohn Baldwin struct checkpoint_thread_info *checkpoint_info = NULL; 1445483d953aSJohn Baldwin struct sockaddr_un addr; 1446483d953aSJohn Baldwin int socket_fd; 1447483d953aSJohn Baldwin pthread_t checkpoint_pthread; 144851fbd894SRobert Wing int err; 1449577ddca9SVitaliy Gusev #ifndef WITHOUT_CAPSICUM 1450577ddca9SVitaliy Gusev cap_rights_t rights; 1451577ddca9SVitaliy Gusev #endif 1452483d953aSJohn Baldwin 1453483d953aSJohn Baldwin memset(&addr, 0, sizeof(addr)); 1454483d953aSJohn Baldwin 1455690b7ea0SRobert Wing socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); 1456483d953aSJohn Baldwin if (socket_fd < 0) { 145738dfb062SRobert Wing EPRINTLN("Socket creation failed: %s", strerror(errno)); 1458483d953aSJohn Baldwin err = -1; 1459483d953aSJohn Baldwin goto fail; 1460483d953aSJohn Baldwin } 1461483d953aSJohn Baldwin 1462483d953aSJohn Baldwin addr.sun_family = AF_UNIX; 1463483d953aSJohn Baldwin 14645ce2d4a1SRobert Wing snprintf(addr.sun_path, sizeof(addr.sun_path), "%s%s", 14653efc45f3SRobert Wing BHYVE_RUN_DIR, vm_get_name(ctx)); 1466483d953aSJohn Baldwin addr.sun_len = SUN_LEN(&addr); 1467483d953aSJohn Baldwin unlink(addr.sun_path); 1468483d953aSJohn Baldwin 1469483d953aSJohn Baldwin if (bind(socket_fd, (struct sockaddr *)&addr, addr.sun_len) != 0) { 147038dfb062SRobert Wing EPRINTLN("Failed to bind socket \"%s\": %s\n", 147138dfb062SRobert Wing addr.sun_path, strerror(errno)); 1472483d953aSJohn Baldwin err = -1; 1473483d953aSJohn Baldwin goto fail; 1474483d953aSJohn Baldwin } 1475483d953aSJohn Baldwin 1476690b7ea0SRobert Wing if (listen(socket_fd, 10) < 0) { 1477690b7ea0SRobert Wing EPRINTLN("ipc socket listen: %s\n", strerror(errno)); 1478690b7ea0SRobert Wing err = errno; 1479690b7ea0SRobert Wing goto fail; 1480690b7ea0SRobert Wing } 1481690b7ea0SRobert Wing 1482577ddca9SVitaliy Gusev #ifndef WITHOUT_CAPSICUM 1483577ddca9SVitaliy Gusev cap_rights_init(&rights, CAP_ACCEPT, CAP_READ, CAP_RECV, CAP_WRITE, 1484577ddca9SVitaliy Gusev CAP_SEND, CAP_GETSOCKOPT); 1485577ddca9SVitaliy Gusev 1486577ddca9SVitaliy Gusev if (caph_rights_limit(socket_fd, &rights) == -1) 1487577ddca9SVitaliy Gusev errx(EX_OSERR, "Unable to apply rights for sandbox"); 1488577ddca9SVitaliy Gusev #endif 1489483d953aSJohn Baldwin checkpoint_info = calloc(1, sizeof(*checkpoint_info)); 1490483d953aSJohn Baldwin checkpoint_info->ctx = ctx; 1491483d953aSJohn Baldwin checkpoint_info->socket_fd = socket_fd; 1492483d953aSJohn Baldwin 149351fbd894SRobert Wing err = pthread_create(&checkpoint_pthread, NULL, checkpoint_thread, 1494483d953aSJohn Baldwin checkpoint_info); 149551fbd894SRobert Wing if (err != 0) 1496483d953aSJohn Baldwin goto fail; 1497483d953aSJohn Baldwin 1498483d953aSJohn Baldwin return (0); 1499483d953aSJohn Baldwin fail: 1500483d953aSJohn Baldwin free(checkpoint_info); 1501483d953aSJohn Baldwin if (socket_fd > 0) 1502483d953aSJohn Baldwin close(socket_fd); 1503483d953aSJohn Baldwin unlink(addr.sun_path); 1504483d953aSJohn Baldwin 1505483d953aSJohn Baldwin return (err); 1506483d953aSJohn Baldwin } 1507483d953aSJohn Baldwin 1508483d953aSJohn Baldwin void 1509483d953aSJohn Baldwin vm_snapshot_buf_err(const char *bufname, const enum vm_snapshot_op op) 1510483d953aSJohn Baldwin { 1511483d953aSJohn Baldwin const char *__op; 1512483d953aSJohn Baldwin 1513483d953aSJohn Baldwin if (op == VM_SNAPSHOT_SAVE) 1514483d953aSJohn Baldwin __op = "save"; 1515483d953aSJohn Baldwin else if (op == VM_SNAPSHOT_RESTORE) 1516483d953aSJohn Baldwin __op = "restore"; 1517483d953aSJohn Baldwin else 1518483d953aSJohn Baldwin __op = "unknown"; 1519483d953aSJohn Baldwin 1520483d953aSJohn Baldwin fprintf(stderr, "%s: snapshot-%s failed for %s\r\n", 1521483d953aSJohn Baldwin __func__, __op, bufname); 1522483d953aSJohn Baldwin } 1523483d953aSJohn Baldwin 1524483d953aSJohn Baldwin int 15258b1adff8SMark Johnston vm_snapshot_buf(void *data, size_t data_size, struct vm_snapshot_meta *meta) 1526483d953aSJohn Baldwin { 1527483d953aSJohn Baldwin struct vm_snapshot_buffer *buffer; 1528483d953aSJohn Baldwin int op; 1529483d953aSJohn Baldwin 1530483d953aSJohn Baldwin buffer = &meta->buffer; 1531483d953aSJohn Baldwin op = meta->op; 1532483d953aSJohn Baldwin 1533483d953aSJohn Baldwin if (buffer->buf_rem < data_size) { 1534483d953aSJohn Baldwin fprintf(stderr, "%s: buffer too small\r\n", __func__); 1535483d953aSJohn Baldwin return (E2BIG); 1536483d953aSJohn Baldwin } 1537483d953aSJohn Baldwin 1538483d953aSJohn Baldwin if (op == VM_SNAPSHOT_SAVE) 15398b1adff8SMark Johnston memcpy(buffer->buf, data, data_size); 1540483d953aSJohn Baldwin else if (op == VM_SNAPSHOT_RESTORE) 15418b1adff8SMark Johnston memcpy(data, buffer->buf, data_size); 1542483d953aSJohn Baldwin else 1543483d953aSJohn Baldwin return (EINVAL); 1544483d953aSJohn Baldwin 1545483d953aSJohn Baldwin buffer->buf += data_size; 1546483d953aSJohn Baldwin buffer->buf_rem -= data_size; 1547483d953aSJohn Baldwin 1548483d953aSJohn Baldwin return (0); 1549483d953aSJohn Baldwin } 1550483d953aSJohn Baldwin 1551483d953aSJohn Baldwin size_t 1552483d953aSJohn Baldwin vm_get_snapshot_size(struct vm_snapshot_meta *meta) 1553483d953aSJohn Baldwin { 1554483d953aSJohn Baldwin size_t length; 1555483d953aSJohn Baldwin struct vm_snapshot_buffer *buffer; 1556483d953aSJohn Baldwin 1557483d953aSJohn Baldwin buffer = &meta->buffer; 1558483d953aSJohn Baldwin 1559483d953aSJohn Baldwin if (buffer->buf_size < buffer->buf_rem) { 1560483d953aSJohn Baldwin fprintf(stderr, "%s: Invalid buffer: size = %zu, rem = %zu\r\n", 1561483d953aSJohn Baldwin __func__, buffer->buf_size, buffer->buf_rem); 1562483d953aSJohn Baldwin length = 0; 1563483d953aSJohn Baldwin } else { 1564483d953aSJohn Baldwin length = buffer->buf_size - buffer->buf_rem; 1565483d953aSJohn Baldwin } 1566483d953aSJohn Baldwin 1567483d953aSJohn Baldwin return (length); 1568483d953aSJohn Baldwin } 1569483d953aSJohn Baldwin 1570483d953aSJohn Baldwin int 15710f735657SJohn Baldwin vm_snapshot_guest2host_addr(struct vmctx *ctx, void **addrp, size_t len, 15720f735657SJohn Baldwin bool restore_null, struct vm_snapshot_meta *meta) 1573483d953aSJohn Baldwin { 1574483d953aSJohn Baldwin int ret; 1575483d953aSJohn Baldwin vm_paddr_t gaddr; 1576483d953aSJohn Baldwin 1577483d953aSJohn Baldwin if (meta->op == VM_SNAPSHOT_SAVE) { 15780f735657SJohn Baldwin gaddr = paddr_host2guest(ctx, *addrp); 1579483d953aSJohn Baldwin if (gaddr == (vm_paddr_t) -1) { 1580483d953aSJohn Baldwin if (!restore_null || 1581483d953aSJohn Baldwin (restore_null && (*addrp != NULL))) { 1582483d953aSJohn Baldwin ret = EFAULT; 1583483d953aSJohn Baldwin goto done; 1584483d953aSJohn Baldwin } 1585483d953aSJohn Baldwin } 1586483d953aSJohn Baldwin 1587483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(gaddr, meta, ret, done); 1588483d953aSJohn Baldwin } else if (meta->op == VM_SNAPSHOT_RESTORE) { 1589483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(gaddr, meta, ret, done); 1590483d953aSJohn Baldwin if (gaddr == (vm_paddr_t) -1) { 1591483d953aSJohn Baldwin if (!restore_null) { 1592483d953aSJohn Baldwin ret = EFAULT; 1593483d953aSJohn Baldwin goto done; 1594483d953aSJohn Baldwin } 1595483d953aSJohn Baldwin } 1596483d953aSJohn Baldwin 15970f735657SJohn Baldwin *addrp = paddr_guest2host(ctx, gaddr, len); 1598483d953aSJohn Baldwin } else { 1599483d953aSJohn Baldwin ret = EINVAL; 1600483d953aSJohn Baldwin } 1601483d953aSJohn Baldwin 1602483d953aSJohn Baldwin done: 1603483d953aSJohn Baldwin return (ret); 1604483d953aSJohn Baldwin } 1605483d953aSJohn Baldwin 1606483d953aSJohn Baldwin int 16078b1adff8SMark Johnston vm_snapshot_buf_cmp(void *data, size_t data_size, struct vm_snapshot_meta *meta) 1608483d953aSJohn Baldwin { 1609483d953aSJohn Baldwin struct vm_snapshot_buffer *buffer; 1610483d953aSJohn Baldwin int op; 1611483d953aSJohn Baldwin int ret; 1612483d953aSJohn Baldwin 1613483d953aSJohn Baldwin buffer = &meta->buffer; 1614483d953aSJohn Baldwin op = meta->op; 1615483d953aSJohn Baldwin 1616483d953aSJohn Baldwin if (buffer->buf_rem < data_size) { 1617483d953aSJohn Baldwin fprintf(stderr, "%s: buffer too small\r\n", __func__); 1618483d953aSJohn Baldwin ret = E2BIG; 1619483d953aSJohn Baldwin goto done; 1620483d953aSJohn Baldwin } 1621483d953aSJohn Baldwin 1622483d953aSJohn Baldwin if (op == VM_SNAPSHOT_SAVE) { 1623483d953aSJohn Baldwin ret = 0; 16248b1adff8SMark Johnston memcpy(buffer->buf, data, data_size); 1625483d953aSJohn Baldwin } else if (op == VM_SNAPSHOT_RESTORE) { 16268b1adff8SMark Johnston ret = memcmp(data, buffer->buf, data_size); 1627483d953aSJohn Baldwin } else { 1628483d953aSJohn Baldwin ret = EINVAL; 1629483d953aSJohn Baldwin goto done; 1630483d953aSJohn Baldwin } 1631483d953aSJohn Baldwin 1632483d953aSJohn Baldwin buffer->buf += data_size; 1633483d953aSJohn Baldwin buffer->buf_rem -= data_size; 1634483d953aSJohn Baldwin 1635483d953aSJohn Baldwin done: 1636483d953aSJohn Baldwin return (ret); 1637483d953aSJohn Baldwin } 1638