1483d953aSJohn Baldwin /*- 2483d953aSJohn Baldwin * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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" 84483d953aSJohn Baldwin #include "fwctl.h" 85483d953aSJohn Baldwin #include "ioapic.h" 86483d953aSJohn Baldwin #include "mem.h" 87483d953aSJohn Baldwin #include "mevent.h" 88483d953aSJohn Baldwin #include "mptbl.h" 89483d953aSJohn Baldwin #include "pci_emul.h" 90483d953aSJohn Baldwin #include "pci_irq.h" 91483d953aSJohn Baldwin #include "pci_lpc.h" 92483d953aSJohn Baldwin #include "smbiostbl.h" 93483d953aSJohn Baldwin #include "snapshot.h" 94483d953aSJohn Baldwin #include "xmsr.h" 95483d953aSJohn Baldwin #include "spinup_ap.h" 96483d953aSJohn Baldwin #include "rtc.h" 97483d953aSJohn Baldwin 98483d953aSJohn Baldwin #include <libxo/xo.h> 99483d953aSJohn Baldwin #include <ucl.h> 100483d953aSJohn Baldwin 101483d953aSJohn Baldwin struct spinner_info { 102483d953aSJohn Baldwin const size_t *crtval; 103483d953aSJohn Baldwin const size_t maxval; 104483d953aSJohn Baldwin const size_t total; 105483d953aSJohn Baldwin }; 106483d953aSJohn Baldwin 107483d953aSJohn Baldwin extern int guest_ncpus; 108483d953aSJohn Baldwin 109483d953aSJohn Baldwin static struct winsize winsize; 110483d953aSJohn Baldwin static sig_t old_winch_handler; 111483d953aSJohn Baldwin 112483d953aSJohn Baldwin #define KB (1024UL) 113483d953aSJohn Baldwin #define MB (1024UL * KB) 114483d953aSJohn Baldwin #define GB (1024UL * MB) 115483d953aSJohn Baldwin 116483d953aSJohn Baldwin #define SNAPSHOT_CHUNK (4 * MB) 117483d953aSJohn Baldwin #define PROG_BUF_SZ (8192) 118483d953aSJohn Baldwin 119483d953aSJohn Baldwin #define MAX_VMNAME 100 120483d953aSJohn Baldwin 121483d953aSJohn Baldwin #define SNAPSHOT_BUFFER_SIZE (20 * MB) 122483d953aSJohn Baldwin 123483d953aSJohn Baldwin #define JSON_STRUCT_ARR_KEY "structs" 124483d953aSJohn Baldwin #define JSON_DEV_ARR_KEY "devices" 125483d953aSJohn Baldwin #define JSON_BASIC_METADATA_KEY "basic metadata" 126483d953aSJohn Baldwin #define JSON_SNAPSHOT_REQ_KEY "snapshot_req" 127483d953aSJohn Baldwin #define JSON_SIZE_KEY "size" 128483d953aSJohn Baldwin #define JSON_FILE_OFFSET_KEY "file_offset" 129483d953aSJohn Baldwin 130483d953aSJohn Baldwin #define JSON_NCPUS_KEY "ncpus" 131483d953aSJohn Baldwin #define JSON_VMNAME_KEY "vmname" 132483d953aSJohn Baldwin #define JSON_MEMSIZE_KEY "memsize" 133483d953aSJohn Baldwin #define JSON_MEMFLAGS_KEY "memflags" 134483d953aSJohn Baldwin 135483d953aSJohn Baldwin #define min(a,b) \ 136483d953aSJohn Baldwin ({ \ 137483d953aSJohn Baldwin __typeof__ (a) _a = (a); \ 138483d953aSJohn Baldwin __typeof__ (b) _b = (b); \ 139483d953aSJohn Baldwin _a < _b ? _a : _b; \ 140483d953aSJohn Baldwin }) 141483d953aSJohn Baldwin 142483d953aSJohn Baldwin const struct vm_snapshot_dev_info snapshot_devs[] = { 143483d953aSJohn Baldwin { "atkbdc", atkbdc_snapshot, NULL, NULL }, 144483d953aSJohn Baldwin { "virtio-net", pci_snapshot, pci_pause, pci_resume }, 145483d953aSJohn Baldwin { "virtio-blk", pci_snapshot, pci_pause, pci_resume }, 146bb481f67SJohn Baldwin { "virtio-rnd", pci_snapshot, NULL, NULL }, 147483d953aSJohn Baldwin { "lpc", pci_snapshot, NULL, NULL }, 148483d953aSJohn Baldwin { "fbuf", pci_snapshot, NULL, NULL }, 149483d953aSJohn Baldwin { "xhci", pci_snapshot, NULL, NULL }, 150483d953aSJohn Baldwin { "e1000", pci_snapshot, NULL, NULL }, 151483d953aSJohn Baldwin { "ahci", pci_snapshot, pci_pause, pci_resume }, 152483d953aSJohn Baldwin { "ahci-hd", pci_snapshot, pci_pause, pci_resume }, 1531b9c7861SJohn Baldwin { "ahci-cd", pci_snapshot, pci_pause, pci_resume }, 154483d953aSJohn Baldwin }; 155483d953aSJohn Baldwin 156483d953aSJohn Baldwin const struct vm_snapshot_kern_info snapshot_kern_structs[] = { 157483d953aSJohn Baldwin { "vhpet", STRUCT_VHPET }, 158483d953aSJohn Baldwin { "vm", STRUCT_VM }, 159483d953aSJohn Baldwin { "vmx", STRUCT_VMX }, 160483d953aSJohn Baldwin { "vioapic", STRUCT_VIOAPIC }, 161483d953aSJohn Baldwin { "vlapic", STRUCT_VLAPIC }, 162483d953aSJohn Baldwin { "vmcx", STRUCT_VMCX }, 163483d953aSJohn Baldwin { "vatpit", STRUCT_VATPIT }, 164483d953aSJohn Baldwin { "vatpic", STRUCT_VATPIC }, 165483d953aSJohn Baldwin { "vpmtmr", STRUCT_VPMTMR }, 166483d953aSJohn Baldwin { "vrtc", STRUCT_VRTC }, 167483d953aSJohn Baldwin }; 168483d953aSJohn Baldwin 169483d953aSJohn Baldwin static cpuset_t vcpus_active, vcpus_suspended; 170483d953aSJohn Baldwin static pthread_mutex_t vcpu_lock; 171483d953aSJohn Baldwin static pthread_cond_t vcpus_idle, vcpus_can_run; 172483d953aSJohn Baldwin static bool checkpoint_active; 173483d953aSJohn Baldwin 174483d953aSJohn Baldwin /* 175483d953aSJohn Baldwin * TODO: Harden this function and all of its callers since 'base_str' is a user 176483d953aSJohn Baldwin * provided string. 177483d953aSJohn Baldwin */ 178483d953aSJohn Baldwin static char * 179483d953aSJohn Baldwin strcat_extension(const char *base_str, const char *ext) 180483d953aSJohn Baldwin { 181483d953aSJohn Baldwin char *res; 182483d953aSJohn Baldwin size_t base_len, ext_len; 183483d953aSJohn Baldwin 184483d953aSJohn Baldwin base_len = strnlen(base_str, MAX_VMNAME); 185483d953aSJohn Baldwin ext_len = strnlen(ext, MAX_VMNAME); 186483d953aSJohn Baldwin 187483d953aSJohn Baldwin if (base_len + ext_len > MAX_VMNAME) { 188483d953aSJohn Baldwin fprintf(stderr, "Filename exceeds maximum length.\n"); 189483d953aSJohn Baldwin return (NULL); 190483d953aSJohn Baldwin } 191483d953aSJohn Baldwin 192483d953aSJohn Baldwin res = malloc(base_len + ext_len + 1); 193483d953aSJohn Baldwin if (res == NULL) { 194483d953aSJohn Baldwin perror("Failed to allocate memory."); 195483d953aSJohn Baldwin return (NULL); 196483d953aSJohn Baldwin } 197483d953aSJohn Baldwin 198483d953aSJohn Baldwin memcpy(res, base_str, base_len); 199483d953aSJohn Baldwin memcpy(res + base_len, ext, ext_len); 200483d953aSJohn Baldwin res[base_len + ext_len] = 0; 201483d953aSJohn Baldwin 202483d953aSJohn Baldwin return (res); 203483d953aSJohn Baldwin } 204483d953aSJohn Baldwin 205483d953aSJohn Baldwin void 206483d953aSJohn Baldwin destroy_restore_state(struct restore_state *rstate) 207483d953aSJohn Baldwin { 208483d953aSJohn Baldwin if (rstate == NULL) { 209483d953aSJohn Baldwin fprintf(stderr, "Attempting to destroy NULL restore struct.\n"); 210483d953aSJohn Baldwin return; 211483d953aSJohn Baldwin } 212483d953aSJohn Baldwin 213483d953aSJohn Baldwin if (rstate->kdata_map != MAP_FAILED) 214483d953aSJohn Baldwin munmap(rstate->kdata_map, rstate->kdata_len); 215483d953aSJohn Baldwin 216483d953aSJohn Baldwin if (rstate->kdata_fd > 0) 217483d953aSJohn Baldwin close(rstate->kdata_fd); 218483d953aSJohn Baldwin if (rstate->vmmem_fd > 0) 219483d953aSJohn Baldwin close(rstate->vmmem_fd); 220483d953aSJohn Baldwin 221483d953aSJohn Baldwin if (rstate->meta_root_obj != NULL) 222483d953aSJohn Baldwin ucl_object_unref(rstate->meta_root_obj); 223483d953aSJohn Baldwin if (rstate->meta_parser != NULL) 224483d953aSJohn Baldwin ucl_parser_free(rstate->meta_parser); 225483d953aSJohn Baldwin } 226483d953aSJohn Baldwin 227483d953aSJohn Baldwin static int 228483d953aSJohn Baldwin load_vmmem_file(const char *filename, struct restore_state *rstate) 229483d953aSJohn Baldwin { 230483d953aSJohn Baldwin struct stat sb; 231483d953aSJohn Baldwin int err; 232483d953aSJohn Baldwin 233483d953aSJohn Baldwin rstate->vmmem_fd = open(filename, O_RDONLY); 234483d953aSJohn Baldwin if (rstate->vmmem_fd < 0) { 235483d953aSJohn Baldwin perror("Failed to open restore file"); 236483d953aSJohn Baldwin return (-1); 237483d953aSJohn Baldwin } 238483d953aSJohn Baldwin 239483d953aSJohn Baldwin err = fstat(rstate->vmmem_fd, &sb); 240483d953aSJohn Baldwin if (err < 0) { 241483d953aSJohn Baldwin perror("Failed to stat restore file"); 242483d953aSJohn Baldwin goto err_load_vmmem; 243483d953aSJohn Baldwin } 244483d953aSJohn Baldwin 245483d953aSJohn Baldwin if (sb.st_size == 0) { 246483d953aSJohn Baldwin fprintf(stderr, "Restore file is empty.\n"); 247483d953aSJohn Baldwin goto err_load_vmmem; 248483d953aSJohn Baldwin } 249483d953aSJohn Baldwin 250483d953aSJohn Baldwin rstate->vmmem_len = sb.st_size; 251483d953aSJohn Baldwin 252483d953aSJohn Baldwin return (0); 253483d953aSJohn Baldwin 254483d953aSJohn Baldwin err_load_vmmem: 255483d953aSJohn Baldwin if (rstate->vmmem_fd > 0) 256483d953aSJohn Baldwin close(rstate->vmmem_fd); 257483d953aSJohn Baldwin return (-1); 258483d953aSJohn Baldwin } 259483d953aSJohn Baldwin 260483d953aSJohn Baldwin static int 261483d953aSJohn Baldwin load_kdata_file(const char *filename, struct restore_state *rstate) 262483d953aSJohn Baldwin { 263483d953aSJohn Baldwin struct stat sb; 264483d953aSJohn Baldwin int err; 265483d953aSJohn Baldwin 266483d953aSJohn Baldwin rstate->kdata_fd = open(filename, O_RDONLY); 267483d953aSJohn Baldwin if (rstate->kdata_fd < 0) { 268483d953aSJohn Baldwin perror("Failed to open kernel data file"); 269483d953aSJohn Baldwin return (-1); 270483d953aSJohn Baldwin } 271483d953aSJohn Baldwin 272483d953aSJohn Baldwin err = fstat(rstate->kdata_fd, &sb); 273483d953aSJohn Baldwin if (err < 0) { 274483d953aSJohn Baldwin perror("Failed to stat kernel data file"); 275483d953aSJohn Baldwin goto err_load_kdata; 276483d953aSJohn Baldwin } 277483d953aSJohn Baldwin 278483d953aSJohn Baldwin if (sb.st_size == 0) { 279483d953aSJohn Baldwin fprintf(stderr, "Kernel data file is empty.\n"); 280483d953aSJohn Baldwin goto err_load_kdata; 281483d953aSJohn Baldwin } 282483d953aSJohn Baldwin 283483d953aSJohn Baldwin rstate->kdata_len = sb.st_size; 284483d953aSJohn Baldwin rstate->kdata_map = mmap(NULL, rstate->kdata_len, PROT_READ, 285483d953aSJohn Baldwin MAP_SHARED, rstate->kdata_fd, 0); 286483d953aSJohn Baldwin if (rstate->kdata_map == MAP_FAILED) { 287483d953aSJohn Baldwin perror("Failed to map restore file"); 288483d953aSJohn Baldwin goto err_load_kdata; 289483d953aSJohn Baldwin } 290483d953aSJohn Baldwin 291483d953aSJohn Baldwin return (0); 292483d953aSJohn Baldwin 293483d953aSJohn Baldwin err_load_kdata: 294483d953aSJohn Baldwin if (rstate->kdata_fd > 0) 295483d953aSJohn Baldwin close(rstate->kdata_fd); 296483d953aSJohn Baldwin return (-1); 297483d953aSJohn Baldwin } 298483d953aSJohn Baldwin 299483d953aSJohn Baldwin static int 300483d953aSJohn Baldwin load_metadata_file(const char *filename, struct restore_state *rstate) 301483d953aSJohn Baldwin { 302483d953aSJohn Baldwin const ucl_object_t *obj; 303483d953aSJohn Baldwin struct ucl_parser *parser; 304483d953aSJohn Baldwin int err; 305483d953aSJohn Baldwin 306483d953aSJohn Baldwin parser = ucl_parser_new(UCL_PARSER_DEFAULT); 307483d953aSJohn Baldwin if (parser == NULL) { 308483d953aSJohn Baldwin fprintf(stderr, "Failed to initialize UCL parser.\n"); 309483d953aSJohn Baldwin goto err_load_metadata; 310483d953aSJohn Baldwin } 311483d953aSJohn Baldwin 312483d953aSJohn Baldwin err = ucl_parser_add_file(parser, filename); 313483d953aSJohn Baldwin if (err == 0) { 314483d953aSJohn Baldwin fprintf(stderr, "Failed to parse metadata file: '%s'\n", 315483d953aSJohn Baldwin filename); 316483d953aSJohn Baldwin err = -1; 317483d953aSJohn Baldwin goto err_load_metadata; 318483d953aSJohn Baldwin } 319483d953aSJohn Baldwin 320483d953aSJohn Baldwin obj = ucl_parser_get_object(parser); 321483d953aSJohn Baldwin if (obj == NULL) { 322483d953aSJohn Baldwin fprintf(stderr, "Failed to parse object.\n"); 323483d953aSJohn Baldwin err = -1; 324483d953aSJohn Baldwin goto err_load_metadata; 325483d953aSJohn Baldwin } 326483d953aSJohn Baldwin 327483d953aSJohn Baldwin rstate->meta_parser = parser; 328483d953aSJohn Baldwin rstate->meta_root_obj = (ucl_object_t *)obj; 329483d953aSJohn Baldwin 330483d953aSJohn Baldwin return (0); 331483d953aSJohn Baldwin 332483d953aSJohn Baldwin err_load_metadata: 333483d953aSJohn Baldwin if (parser != NULL) 334483d953aSJohn Baldwin ucl_parser_free(parser); 335483d953aSJohn Baldwin return (err); 336483d953aSJohn Baldwin } 337483d953aSJohn Baldwin 338483d953aSJohn Baldwin int 339483d953aSJohn Baldwin load_restore_file(const char *filename, struct restore_state *rstate) 340483d953aSJohn Baldwin { 341483d953aSJohn Baldwin int err = 0; 342483d953aSJohn Baldwin char *kdata_filename = NULL, *meta_filename = NULL; 343483d953aSJohn Baldwin 344483d953aSJohn Baldwin assert(filename != NULL); 345483d953aSJohn Baldwin assert(rstate != NULL); 346483d953aSJohn Baldwin 347483d953aSJohn Baldwin memset(rstate, 0, sizeof(*rstate)); 348483d953aSJohn Baldwin rstate->kdata_map = MAP_FAILED; 349483d953aSJohn Baldwin 350483d953aSJohn Baldwin err = load_vmmem_file(filename, rstate); 351483d953aSJohn Baldwin if (err != 0) { 352483d953aSJohn Baldwin fprintf(stderr, "Failed to load guest RAM file.\n"); 353483d953aSJohn Baldwin goto err_restore; 354483d953aSJohn Baldwin } 355483d953aSJohn Baldwin 356483d953aSJohn Baldwin kdata_filename = strcat_extension(filename, ".kern"); 357483d953aSJohn Baldwin if (kdata_filename == NULL) { 358483d953aSJohn Baldwin fprintf(stderr, "Failed to construct kernel data filename.\n"); 359483d953aSJohn Baldwin goto err_restore; 360483d953aSJohn Baldwin } 361483d953aSJohn Baldwin 362483d953aSJohn Baldwin err = load_kdata_file(kdata_filename, rstate); 363483d953aSJohn Baldwin if (err != 0) { 364483d953aSJohn Baldwin fprintf(stderr, "Failed to load guest kernel data file.\n"); 365483d953aSJohn Baldwin goto err_restore; 366483d953aSJohn Baldwin } 367483d953aSJohn Baldwin 368483d953aSJohn Baldwin meta_filename = strcat_extension(filename, ".meta"); 369483d953aSJohn Baldwin if (meta_filename == NULL) { 370483d953aSJohn Baldwin fprintf(stderr, "Failed to construct kernel metadata filename.\n"); 371483d953aSJohn Baldwin goto err_restore; 372483d953aSJohn Baldwin } 373483d953aSJohn Baldwin 374483d953aSJohn Baldwin err = load_metadata_file(meta_filename, rstate); 375483d953aSJohn Baldwin if (err != 0) { 376483d953aSJohn Baldwin fprintf(stderr, "Failed to load guest metadata file.\n"); 377483d953aSJohn Baldwin goto err_restore; 378483d953aSJohn Baldwin } 379483d953aSJohn Baldwin 380483d953aSJohn Baldwin return (0); 381483d953aSJohn Baldwin 382483d953aSJohn Baldwin err_restore: 383483d953aSJohn Baldwin destroy_restore_state(rstate); 384483d953aSJohn Baldwin if (kdata_filename != NULL) 385483d953aSJohn Baldwin free(kdata_filename); 386483d953aSJohn Baldwin if (meta_filename != NULL) 387483d953aSJohn Baldwin free(meta_filename); 388483d953aSJohn Baldwin return (-1); 389483d953aSJohn Baldwin } 390483d953aSJohn Baldwin 391483d953aSJohn Baldwin #define JSON_GET_INT_OR_RETURN(key, obj, result_ptr, ret) \ 392483d953aSJohn Baldwin do { \ 393483d953aSJohn Baldwin const ucl_object_t *obj__; \ 394483d953aSJohn Baldwin obj__ = ucl_object_lookup(obj, key); \ 395483d953aSJohn Baldwin if (obj__ == NULL) { \ 396483d953aSJohn Baldwin fprintf(stderr, "Missing key: '%s'", key); \ 397483d953aSJohn Baldwin return (ret); \ 398483d953aSJohn Baldwin } \ 399483d953aSJohn Baldwin if (!ucl_object_toint_safe(obj__, result_ptr)) { \ 400483d953aSJohn Baldwin fprintf(stderr, "Cannot convert '%s' value to int.", key); \ 401483d953aSJohn Baldwin return (ret); \ 402483d953aSJohn Baldwin } \ 403483d953aSJohn Baldwin } while(0) 404483d953aSJohn Baldwin 405483d953aSJohn Baldwin #define JSON_GET_STRING_OR_RETURN(key, obj, result_ptr, ret) \ 406483d953aSJohn Baldwin do { \ 407483d953aSJohn Baldwin const ucl_object_t *obj__; \ 408483d953aSJohn Baldwin obj__ = ucl_object_lookup(obj, key); \ 409483d953aSJohn Baldwin if (obj__ == NULL) { \ 410483d953aSJohn Baldwin fprintf(stderr, "Missing key: '%s'", key); \ 411483d953aSJohn Baldwin return (ret); \ 412483d953aSJohn Baldwin } \ 413483d953aSJohn Baldwin if (!ucl_object_tostring_safe(obj__, result_ptr)) { \ 414483d953aSJohn Baldwin fprintf(stderr, "Cannot convert '%s' value to string.", key); \ 415483d953aSJohn Baldwin return (ret); \ 416483d953aSJohn Baldwin } \ 417483d953aSJohn Baldwin } while(0) 418483d953aSJohn Baldwin 419483d953aSJohn Baldwin static void * 420483d953aSJohn Baldwin lookup_struct(enum snapshot_req struct_id, struct restore_state *rstate, 421483d953aSJohn Baldwin size_t *struct_size) 422483d953aSJohn Baldwin { 423483d953aSJohn Baldwin const ucl_object_t *structs = NULL, *obj = NULL; 424483d953aSJohn Baldwin ucl_object_iter_t it = NULL; 425483d953aSJohn Baldwin int64_t snapshot_req, size, file_offset; 426483d953aSJohn Baldwin 427483d953aSJohn Baldwin structs = ucl_object_lookup(rstate->meta_root_obj, JSON_STRUCT_ARR_KEY); 428483d953aSJohn Baldwin if (structs == NULL) { 429483d953aSJohn Baldwin fprintf(stderr, "Failed to find '%s' object.\n", 430483d953aSJohn Baldwin JSON_STRUCT_ARR_KEY); 431483d953aSJohn Baldwin return (NULL); 432483d953aSJohn Baldwin } 433483d953aSJohn Baldwin 434483d953aSJohn Baldwin if (ucl_object_type((ucl_object_t *)structs) != UCL_ARRAY) { 435483d953aSJohn Baldwin fprintf(stderr, "Object '%s' is not an array.\n", 436483d953aSJohn Baldwin JSON_STRUCT_ARR_KEY); 437483d953aSJohn Baldwin return (NULL); 438483d953aSJohn Baldwin } 439483d953aSJohn Baldwin 440483d953aSJohn Baldwin while ((obj = ucl_object_iterate(structs, &it, true)) != NULL) { 441483d953aSJohn Baldwin snapshot_req = -1; 442483d953aSJohn Baldwin JSON_GET_INT_OR_RETURN(JSON_SNAPSHOT_REQ_KEY, obj, 443483d953aSJohn Baldwin &snapshot_req, NULL); 444483d953aSJohn Baldwin assert(snapshot_req >= 0); 445483d953aSJohn Baldwin if ((enum snapshot_req) snapshot_req == struct_id) { 446483d953aSJohn Baldwin JSON_GET_INT_OR_RETURN(JSON_SIZE_KEY, obj, 447483d953aSJohn Baldwin &size, NULL); 448483d953aSJohn Baldwin assert(size >= 0); 449483d953aSJohn Baldwin 450483d953aSJohn Baldwin JSON_GET_INT_OR_RETURN(JSON_FILE_OFFSET_KEY, obj, 451483d953aSJohn Baldwin &file_offset, NULL); 452483d953aSJohn Baldwin assert(file_offset >= 0); 453483d953aSJohn Baldwin assert(file_offset + size <= rstate->kdata_len); 454483d953aSJohn Baldwin 455483d953aSJohn Baldwin *struct_size = (size_t)size; 456483d953aSJohn Baldwin return (rstate->kdata_map + file_offset); 457483d953aSJohn Baldwin } 458483d953aSJohn Baldwin } 459483d953aSJohn Baldwin 460483d953aSJohn Baldwin return (NULL); 461483d953aSJohn Baldwin } 462483d953aSJohn Baldwin 463483d953aSJohn Baldwin static void * 464483d953aSJohn Baldwin lookup_check_dev(const char *dev_name, struct restore_state *rstate, 465483d953aSJohn Baldwin const ucl_object_t *obj, size_t *data_size) 466483d953aSJohn Baldwin { 467483d953aSJohn Baldwin const char *snapshot_req; 468483d953aSJohn Baldwin int64_t size, file_offset; 469483d953aSJohn Baldwin 470483d953aSJohn Baldwin snapshot_req = NULL; 471483d953aSJohn Baldwin JSON_GET_STRING_OR_RETURN(JSON_SNAPSHOT_REQ_KEY, obj, 472483d953aSJohn Baldwin &snapshot_req, NULL); 473483d953aSJohn Baldwin assert(snapshot_req != NULL); 474483d953aSJohn Baldwin if (!strcmp(snapshot_req, dev_name)) { 475483d953aSJohn Baldwin JSON_GET_INT_OR_RETURN(JSON_SIZE_KEY, obj, 476483d953aSJohn Baldwin &size, NULL); 477483d953aSJohn Baldwin assert(size >= 0); 478483d953aSJohn Baldwin 479483d953aSJohn Baldwin JSON_GET_INT_OR_RETURN(JSON_FILE_OFFSET_KEY, obj, 480483d953aSJohn Baldwin &file_offset, NULL); 481483d953aSJohn Baldwin assert(file_offset >= 0); 482483d953aSJohn Baldwin assert(file_offset + size <= rstate->kdata_len); 483483d953aSJohn Baldwin 484483d953aSJohn Baldwin *data_size = (size_t)size; 485483d953aSJohn Baldwin return (rstate->kdata_map + file_offset); 486483d953aSJohn Baldwin } 487483d953aSJohn Baldwin 488483d953aSJohn Baldwin return (NULL); 489483d953aSJohn Baldwin } 490483d953aSJohn Baldwin 491483d953aSJohn Baldwin static void* 492483d953aSJohn Baldwin lookup_dev(const char *dev_name, struct restore_state *rstate, 493483d953aSJohn Baldwin size_t *data_size) 494483d953aSJohn Baldwin { 495483d953aSJohn Baldwin const ucl_object_t *devs = NULL, *obj = NULL; 496483d953aSJohn Baldwin ucl_object_iter_t it = NULL; 497483d953aSJohn Baldwin void *ret; 498483d953aSJohn Baldwin 499483d953aSJohn Baldwin devs = ucl_object_lookup(rstate->meta_root_obj, JSON_DEV_ARR_KEY); 500483d953aSJohn Baldwin if (devs == NULL) { 501483d953aSJohn Baldwin fprintf(stderr, "Failed to find '%s' object.\n", 502483d953aSJohn Baldwin JSON_DEV_ARR_KEY); 503483d953aSJohn Baldwin return (NULL); 504483d953aSJohn Baldwin } 505483d953aSJohn Baldwin 506483d953aSJohn Baldwin if (ucl_object_type((ucl_object_t *)devs) != UCL_ARRAY) { 507483d953aSJohn Baldwin fprintf(stderr, "Object '%s' is not an array.\n", 508483d953aSJohn Baldwin JSON_DEV_ARR_KEY); 509483d953aSJohn Baldwin return (NULL); 510483d953aSJohn Baldwin } 511483d953aSJohn Baldwin 512483d953aSJohn Baldwin while ((obj = ucl_object_iterate(devs, &it, true)) != NULL) { 513483d953aSJohn Baldwin ret = lookup_check_dev(dev_name, rstate, obj, data_size); 514483d953aSJohn Baldwin if (ret != NULL) 515483d953aSJohn Baldwin return (ret); 516483d953aSJohn Baldwin } 517483d953aSJohn Baldwin 518483d953aSJohn Baldwin return (NULL); 519483d953aSJohn Baldwin } 520483d953aSJohn Baldwin 521483d953aSJohn Baldwin static const ucl_object_t * 522483d953aSJohn Baldwin lookup_basic_metadata_object(struct restore_state *rstate) 523483d953aSJohn Baldwin { 524483d953aSJohn Baldwin const ucl_object_t *basic_meta_obj = NULL; 525483d953aSJohn Baldwin 526483d953aSJohn Baldwin basic_meta_obj = ucl_object_lookup(rstate->meta_root_obj, 527483d953aSJohn Baldwin JSON_BASIC_METADATA_KEY); 528483d953aSJohn Baldwin if (basic_meta_obj == NULL) { 529483d953aSJohn Baldwin fprintf(stderr, "Failed to find '%s' object.\n", 530483d953aSJohn Baldwin JSON_BASIC_METADATA_KEY); 531483d953aSJohn Baldwin return (NULL); 532483d953aSJohn Baldwin } 533483d953aSJohn Baldwin 534483d953aSJohn Baldwin if (ucl_object_type((ucl_object_t *)basic_meta_obj) != UCL_OBJECT) { 535483d953aSJohn Baldwin fprintf(stderr, "Object '%s' is not a JSON object.\n", 536483d953aSJohn Baldwin JSON_BASIC_METADATA_KEY); 537483d953aSJohn Baldwin return (NULL); 538483d953aSJohn Baldwin } 539483d953aSJohn Baldwin 540483d953aSJohn Baldwin return (basic_meta_obj); 541483d953aSJohn Baldwin } 542483d953aSJohn Baldwin 543483d953aSJohn Baldwin const char * 544483d953aSJohn Baldwin lookup_vmname(struct restore_state *rstate) 545483d953aSJohn Baldwin { 546483d953aSJohn Baldwin const char *vmname; 547483d953aSJohn Baldwin const ucl_object_t *obj; 548483d953aSJohn Baldwin 549483d953aSJohn Baldwin obj = lookup_basic_metadata_object(rstate); 550483d953aSJohn Baldwin if (obj == NULL) 551483d953aSJohn Baldwin return (NULL); 552483d953aSJohn Baldwin 553483d953aSJohn Baldwin JSON_GET_STRING_OR_RETURN(JSON_VMNAME_KEY, obj, &vmname, NULL); 554483d953aSJohn Baldwin return (vmname); 555483d953aSJohn Baldwin } 556483d953aSJohn Baldwin 557483d953aSJohn Baldwin int 558483d953aSJohn Baldwin lookup_memflags(struct restore_state *rstate) 559483d953aSJohn Baldwin { 560483d953aSJohn Baldwin int64_t memflags; 561483d953aSJohn Baldwin const ucl_object_t *obj; 562483d953aSJohn Baldwin 563483d953aSJohn Baldwin obj = lookup_basic_metadata_object(rstate); 564483d953aSJohn Baldwin if (obj == NULL) 565483d953aSJohn Baldwin return (0); 566483d953aSJohn Baldwin 567483d953aSJohn Baldwin JSON_GET_INT_OR_RETURN(JSON_MEMFLAGS_KEY, obj, &memflags, 0); 568483d953aSJohn Baldwin 569483d953aSJohn Baldwin return ((int)memflags); 570483d953aSJohn Baldwin } 571483d953aSJohn Baldwin 572483d953aSJohn Baldwin size_t 573483d953aSJohn Baldwin lookup_memsize(struct restore_state *rstate) 574483d953aSJohn Baldwin { 575483d953aSJohn Baldwin int64_t memsize; 576483d953aSJohn Baldwin const ucl_object_t *obj; 577483d953aSJohn Baldwin 578483d953aSJohn Baldwin obj = lookup_basic_metadata_object(rstate); 579483d953aSJohn Baldwin if (obj == NULL) 580483d953aSJohn Baldwin return (0); 581483d953aSJohn Baldwin 582483d953aSJohn Baldwin JSON_GET_INT_OR_RETURN(JSON_MEMSIZE_KEY, obj, &memsize, 0); 583483d953aSJohn Baldwin if (memsize < 0) 584483d953aSJohn Baldwin memsize = 0; 585483d953aSJohn Baldwin 586483d953aSJohn Baldwin return ((size_t)memsize); 587483d953aSJohn Baldwin } 588483d953aSJohn Baldwin 589483d953aSJohn Baldwin 590483d953aSJohn Baldwin int 591483d953aSJohn Baldwin lookup_guest_ncpus(struct restore_state *rstate) 592483d953aSJohn Baldwin { 593483d953aSJohn Baldwin int64_t ncpus; 594483d953aSJohn Baldwin const ucl_object_t *obj; 595483d953aSJohn Baldwin 596483d953aSJohn Baldwin obj = lookup_basic_metadata_object(rstate); 597483d953aSJohn Baldwin if (obj == NULL) 598483d953aSJohn Baldwin return (0); 599483d953aSJohn Baldwin 600483d953aSJohn Baldwin JSON_GET_INT_OR_RETURN(JSON_NCPUS_KEY, obj, &ncpus, 0); 601483d953aSJohn Baldwin return ((int)ncpus); 602483d953aSJohn Baldwin } 603483d953aSJohn Baldwin 604483d953aSJohn Baldwin static void 605483d953aSJohn Baldwin winch_handler(int signal) 606483d953aSJohn Baldwin { 607483d953aSJohn Baldwin #ifdef TIOCGWINSZ 608483d953aSJohn Baldwin ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize); 609483d953aSJohn Baldwin #endif /* TIOCGWINSZ */ 610483d953aSJohn Baldwin } 611483d953aSJohn Baldwin 612483d953aSJohn Baldwin static int 613483d953aSJohn Baldwin print_progress(size_t crtval, const size_t maxval) 614483d953aSJohn Baldwin { 615483d953aSJohn Baldwin size_t rc; 616483d953aSJohn Baldwin double crtval_gb, maxval_gb; 617483d953aSJohn Baldwin size_t i, win_width, prog_start, prog_done, prog_end; 618483d953aSJohn Baldwin int mval_len; 619483d953aSJohn Baldwin 620483d953aSJohn Baldwin static char prog_buf[PROG_BUF_SZ]; 621483d953aSJohn Baldwin static const size_t len = sizeof(prog_buf); 622483d953aSJohn Baldwin 623483d953aSJohn Baldwin static size_t div; 624483d953aSJohn Baldwin static char *div_str; 625483d953aSJohn Baldwin 626483d953aSJohn Baldwin static char wip_bar[] = { '/', '-', '\\', '|' }; 627483d953aSJohn Baldwin static int wip_idx = 0; 628483d953aSJohn Baldwin 629483d953aSJohn Baldwin if (maxval == 0) { 630483d953aSJohn Baldwin printf("[0B / 0B]\r\n"); 631483d953aSJohn Baldwin return (0); 632483d953aSJohn Baldwin } 633483d953aSJohn Baldwin 634483d953aSJohn Baldwin if (crtval > maxval) 635483d953aSJohn Baldwin crtval = maxval; 636483d953aSJohn Baldwin 637483d953aSJohn Baldwin if (maxval > 10 * GB) { 638483d953aSJohn Baldwin div = GB; 639483d953aSJohn Baldwin div_str = "GiB"; 640483d953aSJohn Baldwin } else if (maxval > 10 * MB) { 641483d953aSJohn Baldwin div = MB; 642483d953aSJohn Baldwin div_str = "MiB"; 643483d953aSJohn Baldwin } else { 644483d953aSJohn Baldwin div = KB; 645483d953aSJohn Baldwin div_str = "KiB"; 646483d953aSJohn Baldwin } 647483d953aSJohn Baldwin 648483d953aSJohn Baldwin crtval_gb = (double) crtval / div; 649483d953aSJohn Baldwin maxval_gb = (double) maxval / div; 650483d953aSJohn Baldwin 651483d953aSJohn Baldwin rc = snprintf(prog_buf, len, "%.03lf", maxval_gb); 652483d953aSJohn Baldwin if (rc == len) { 653483d953aSJohn Baldwin fprintf(stderr, "Maxval too big\n"); 654483d953aSJohn Baldwin return (-1); 655483d953aSJohn Baldwin } 656483d953aSJohn Baldwin mval_len = rc; 657483d953aSJohn Baldwin 658483d953aSJohn Baldwin rc = snprintf(prog_buf, len, "\r[%*.03lf%s / %.03lf%s] |", 659483d953aSJohn Baldwin mval_len, crtval_gb, div_str, maxval_gb, div_str); 660483d953aSJohn Baldwin 661483d953aSJohn Baldwin if (rc == len) { 662483d953aSJohn Baldwin fprintf(stderr, "Buffer too small to print progress\n"); 663483d953aSJohn Baldwin return (-1); 664483d953aSJohn Baldwin } 665483d953aSJohn Baldwin 666483d953aSJohn Baldwin win_width = min(winsize.ws_col, len); 667483d953aSJohn Baldwin prog_start = rc; 668483d953aSJohn Baldwin 669483d953aSJohn Baldwin if (prog_start < (win_width - 2)) { 670483d953aSJohn Baldwin prog_end = win_width - prog_start - 2; 671483d953aSJohn Baldwin prog_done = prog_end * (crtval_gb / maxval_gb); 672483d953aSJohn Baldwin 673483d953aSJohn Baldwin for (i = prog_start; i < prog_start + prog_done; i++) 674483d953aSJohn Baldwin prog_buf[i] = '#'; 675483d953aSJohn Baldwin 676483d953aSJohn Baldwin if (crtval != maxval) { 677483d953aSJohn Baldwin prog_buf[i] = wip_bar[wip_idx]; 678483d953aSJohn Baldwin wip_idx = (wip_idx + 1) % sizeof(wip_bar); 679483d953aSJohn Baldwin i++; 680483d953aSJohn Baldwin } else { 681483d953aSJohn Baldwin prog_buf[i++] = '#'; 682483d953aSJohn Baldwin } 683483d953aSJohn Baldwin 684483d953aSJohn Baldwin for (; i < win_width - 2; i++) 685483d953aSJohn Baldwin prog_buf[i] = '_'; 686483d953aSJohn Baldwin 687483d953aSJohn Baldwin prog_buf[win_width - 2] = '|'; 688483d953aSJohn Baldwin } 689483d953aSJohn Baldwin 690483d953aSJohn Baldwin prog_buf[win_width - 1] = '\0'; 691483d953aSJohn Baldwin write(STDOUT_FILENO, prog_buf, win_width); 692483d953aSJohn Baldwin 693483d953aSJohn Baldwin return (0); 694483d953aSJohn Baldwin } 695483d953aSJohn Baldwin 696483d953aSJohn Baldwin static void * 697483d953aSJohn Baldwin snapshot_spinner_cb(void *arg) 698483d953aSJohn Baldwin { 699483d953aSJohn Baldwin int rc; 700483d953aSJohn Baldwin size_t crtval, maxval, total; 701483d953aSJohn Baldwin struct spinner_info *si; 702483d953aSJohn Baldwin struct timespec ts; 703483d953aSJohn Baldwin 704483d953aSJohn Baldwin si = arg; 705483d953aSJohn Baldwin if (si == NULL) 706483d953aSJohn Baldwin pthread_exit(NULL); 707483d953aSJohn Baldwin 708483d953aSJohn Baldwin ts.tv_sec = 0; 709483d953aSJohn Baldwin ts.tv_nsec = 50 * 1000 * 1000; /* 50 ms sleep time */ 710483d953aSJohn Baldwin 711483d953aSJohn Baldwin do { 712483d953aSJohn Baldwin crtval = *si->crtval; 713483d953aSJohn Baldwin maxval = si->maxval; 714483d953aSJohn Baldwin total = si->total; 715483d953aSJohn Baldwin 716483d953aSJohn Baldwin rc = print_progress(crtval, total); 717483d953aSJohn Baldwin if (rc < 0) { 718483d953aSJohn Baldwin fprintf(stderr, "Failed to parse progress\n"); 719483d953aSJohn Baldwin break; 720483d953aSJohn Baldwin } 721483d953aSJohn Baldwin 722483d953aSJohn Baldwin nanosleep(&ts, NULL); 723483d953aSJohn Baldwin } while (crtval < maxval); 724483d953aSJohn Baldwin 725483d953aSJohn Baldwin pthread_exit(NULL); 726483d953aSJohn Baldwin return NULL; 727483d953aSJohn Baldwin } 728483d953aSJohn Baldwin 729483d953aSJohn Baldwin static int 730483d953aSJohn Baldwin vm_snapshot_mem_part(const int snapfd, const size_t foff, void *src, 731483d953aSJohn Baldwin const size_t len, const size_t totalmem, const bool op_wr) 732483d953aSJohn Baldwin { 733483d953aSJohn Baldwin int rc; 734483d953aSJohn Baldwin size_t part_done, todo, rem; 735483d953aSJohn Baldwin ssize_t done; 736483d953aSJohn Baldwin bool show_progress; 737483d953aSJohn Baldwin pthread_t spinner_th; 738483d953aSJohn Baldwin struct spinner_info *si; 739483d953aSJohn Baldwin 740483d953aSJohn Baldwin if (lseek(snapfd, foff, SEEK_SET) < 0) { 741483d953aSJohn Baldwin perror("Failed to change file offset"); 742483d953aSJohn Baldwin return (-1); 743483d953aSJohn Baldwin } 744483d953aSJohn Baldwin 745483d953aSJohn Baldwin show_progress = false; 746483d953aSJohn Baldwin if (isatty(STDIN_FILENO) && (winsize.ws_col != 0)) 747483d953aSJohn Baldwin show_progress = true; 748483d953aSJohn Baldwin 749483d953aSJohn Baldwin part_done = foff; 750483d953aSJohn Baldwin rem = len; 751483d953aSJohn Baldwin 752483d953aSJohn Baldwin if (show_progress) { 753483d953aSJohn Baldwin si = &(struct spinner_info) { 754483d953aSJohn Baldwin .crtval = &part_done, 755483d953aSJohn Baldwin .maxval = foff + len, 756483d953aSJohn Baldwin .total = totalmem 757483d953aSJohn Baldwin }; 758483d953aSJohn Baldwin 759483d953aSJohn Baldwin rc = pthread_create(&spinner_th, 0, snapshot_spinner_cb, si); 760483d953aSJohn Baldwin if (rc) { 761483d953aSJohn Baldwin perror("Unable to create spinner thread"); 762483d953aSJohn Baldwin show_progress = false; 763483d953aSJohn Baldwin } 764483d953aSJohn Baldwin } 765483d953aSJohn Baldwin 766483d953aSJohn Baldwin while (rem > 0) { 767483d953aSJohn Baldwin if (show_progress) 768483d953aSJohn Baldwin todo = min(SNAPSHOT_CHUNK, rem); 769483d953aSJohn Baldwin else 770483d953aSJohn Baldwin todo = rem; 771483d953aSJohn Baldwin 772483d953aSJohn Baldwin if (op_wr) 773483d953aSJohn Baldwin done = write(snapfd, src, todo); 774483d953aSJohn Baldwin else 775483d953aSJohn Baldwin done = read(snapfd, src, todo); 776483d953aSJohn Baldwin if (done < 0) { 777483d953aSJohn Baldwin perror("Failed to write in file"); 778483d953aSJohn Baldwin return (-1); 779483d953aSJohn Baldwin } 780483d953aSJohn Baldwin 781483d953aSJohn Baldwin src += done; 782483d953aSJohn Baldwin part_done += done; 783483d953aSJohn Baldwin rem -= done; 784483d953aSJohn Baldwin } 785483d953aSJohn Baldwin 786483d953aSJohn Baldwin if (show_progress) { 787483d953aSJohn Baldwin rc = pthread_join(spinner_th, NULL); 788483d953aSJohn Baldwin if (rc) 789483d953aSJohn Baldwin perror("Unable to end spinner thread"); 790483d953aSJohn Baldwin } 791483d953aSJohn Baldwin 792483d953aSJohn Baldwin return (0); 793483d953aSJohn Baldwin } 794483d953aSJohn Baldwin 795483d953aSJohn Baldwin static size_t 796483d953aSJohn Baldwin vm_snapshot_mem(struct vmctx *ctx, int snapfd, size_t memsz, const bool op_wr) 797483d953aSJohn Baldwin { 798483d953aSJohn Baldwin int ret; 799483d953aSJohn Baldwin size_t lowmem, highmem, totalmem; 800483d953aSJohn Baldwin char *baseaddr; 801483d953aSJohn Baldwin 802483d953aSJohn Baldwin ret = vm_get_guestmem_from_ctx(ctx, &baseaddr, &lowmem, &highmem); 803483d953aSJohn Baldwin if (ret) { 804483d953aSJohn Baldwin fprintf(stderr, "%s: unable to retrieve guest memory size\r\n", 805483d953aSJohn Baldwin __func__); 806483d953aSJohn Baldwin return (0); 807483d953aSJohn Baldwin } 808483d953aSJohn Baldwin totalmem = lowmem + highmem; 809483d953aSJohn Baldwin 810483d953aSJohn Baldwin if ((op_wr == false) && (totalmem != memsz)) { 811483d953aSJohn Baldwin fprintf(stderr, "%s: mem size mismatch: %ld vs %ld\r\n", 812483d953aSJohn Baldwin __func__, totalmem, memsz); 813483d953aSJohn Baldwin return (0); 814483d953aSJohn Baldwin } 815483d953aSJohn Baldwin 816483d953aSJohn Baldwin winsize.ws_col = 80; 817483d953aSJohn Baldwin #ifdef TIOCGWINSZ 818483d953aSJohn Baldwin ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize); 819483d953aSJohn Baldwin #endif /* TIOCGWINSZ */ 820483d953aSJohn Baldwin old_winch_handler = signal(SIGWINCH, winch_handler); 821483d953aSJohn Baldwin 822483d953aSJohn Baldwin ret = vm_snapshot_mem_part(snapfd, 0, baseaddr, lowmem, 823483d953aSJohn Baldwin totalmem, op_wr); 824483d953aSJohn Baldwin if (ret) { 825483d953aSJohn Baldwin fprintf(stderr, "%s: Could not %s lowmem\r\n", 826483d953aSJohn Baldwin __func__, op_wr ? "write" : "read"); 827483d953aSJohn Baldwin totalmem = 0; 828483d953aSJohn Baldwin goto done; 829483d953aSJohn Baldwin } 830483d953aSJohn Baldwin 831483d953aSJohn Baldwin if (highmem == 0) 832483d953aSJohn Baldwin goto done; 833483d953aSJohn Baldwin 834483d953aSJohn Baldwin ret = vm_snapshot_mem_part(snapfd, lowmem, baseaddr + 4*GB, 835483d953aSJohn Baldwin highmem, totalmem, op_wr); 836483d953aSJohn Baldwin if (ret) { 837483d953aSJohn Baldwin fprintf(stderr, "%s: Could not %s highmem\r\n", 838483d953aSJohn Baldwin __func__, op_wr ? "write" : "read"); 839483d953aSJohn Baldwin totalmem = 0; 840483d953aSJohn Baldwin goto done; 841483d953aSJohn Baldwin } 842483d953aSJohn Baldwin 843483d953aSJohn Baldwin done: 844483d953aSJohn Baldwin printf("\r\n"); 845483d953aSJohn Baldwin signal(SIGWINCH, old_winch_handler); 846483d953aSJohn Baldwin 847483d953aSJohn Baldwin return (totalmem); 848483d953aSJohn Baldwin } 849483d953aSJohn Baldwin 850483d953aSJohn Baldwin int 851483d953aSJohn Baldwin restore_vm_mem(struct vmctx *ctx, struct restore_state *rstate) 852483d953aSJohn Baldwin { 853483d953aSJohn Baldwin size_t restored; 854483d953aSJohn Baldwin 855483d953aSJohn Baldwin restored = vm_snapshot_mem(ctx, rstate->vmmem_fd, rstate->vmmem_len, 856483d953aSJohn Baldwin false); 857483d953aSJohn Baldwin 858483d953aSJohn Baldwin if (restored != rstate->vmmem_len) 859483d953aSJohn Baldwin return (-1); 860483d953aSJohn Baldwin 861483d953aSJohn Baldwin return (0); 862483d953aSJohn Baldwin } 863483d953aSJohn Baldwin 864483d953aSJohn Baldwin static int 865483d953aSJohn Baldwin vm_restore_kern_struct(struct vmctx *ctx, struct restore_state *rstate, 866483d953aSJohn Baldwin const struct vm_snapshot_kern_info *info) 867483d953aSJohn Baldwin { 868483d953aSJohn Baldwin void *struct_ptr; 869483d953aSJohn Baldwin size_t struct_size; 870483d953aSJohn Baldwin int ret; 871483d953aSJohn Baldwin struct vm_snapshot_meta *meta; 872483d953aSJohn Baldwin 873483d953aSJohn Baldwin struct_ptr = lookup_struct(info->req, rstate, &struct_size); 874483d953aSJohn Baldwin if (struct_ptr == NULL) { 875483d953aSJohn Baldwin fprintf(stderr, "%s: Failed to lookup struct %s\r\n", 876483d953aSJohn Baldwin __func__, info->struct_name); 877483d953aSJohn Baldwin ret = -1; 878483d953aSJohn Baldwin goto done; 879483d953aSJohn Baldwin } 880483d953aSJohn Baldwin 881483d953aSJohn Baldwin if (struct_size == 0) { 882483d953aSJohn Baldwin fprintf(stderr, "%s: Kernel struct size was 0 for: %s\r\n", 883483d953aSJohn Baldwin __func__, info->struct_name); 884483d953aSJohn Baldwin ret = -1; 885483d953aSJohn Baldwin goto done; 886483d953aSJohn Baldwin } 887483d953aSJohn Baldwin 888483d953aSJohn Baldwin meta = &(struct vm_snapshot_meta) { 889483d953aSJohn Baldwin .ctx = ctx, 890483d953aSJohn Baldwin .dev_name = info->struct_name, 891483d953aSJohn Baldwin .dev_req = info->req, 892483d953aSJohn Baldwin 893483d953aSJohn Baldwin .buffer.buf_start = struct_ptr, 894483d953aSJohn Baldwin .buffer.buf_size = struct_size, 895483d953aSJohn Baldwin 896483d953aSJohn Baldwin .buffer.buf = struct_ptr, 897483d953aSJohn Baldwin .buffer.buf_rem = struct_size, 898483d953aSJohn Baldwin 899483d953aSJohn Baldwin .op = VM_SNAPSHOT_RESTORE, 900483d953aSJohn Baldwin }; 901483d953aSJohn Baldwin 902483d953aSJohn Baldwin ret = vm_snapshot_req(meta); 903483d953aSJohn Baldwin if (ret != 0) { 904483d953aSJohn Baldwin fprintf(stderr, "%s: Failed to restore struct: %s\r\n", 905483d953aSJohn Baldwin __func__, info->struct_name); 906483d953aSJohn Baldwin goto done; 907483d953aSJohn Baldwin } 908483d953aSJohn Baldwin 909483d953aSJohn Baldwin done: 910483d953aSJohn Baldwin return (ret); 911483d953aSJohn Baldwin } 912483d953aSJohn Baldwin 913483d953aSJohn Baldwin int 914483d953aSJohn Baldwin vm_restore_kern_structs(struct vmctx *ctx, struct restore_state *rstate) 915483d953aSJohn Baldwin { 916483d953aSJohn Baldwin int ret; 917483d953aSJohn Baldwin int i; 918483d953aSJohn Baldwin 919483d953aSJohn Baldwin for (i = 0; i < nitems(snapshot_kern_structs); i++) { 920483d953aSJohn Baldwin ret = vm_restore_kern_struct(ctx, rstate, 921483d953aSJohn Baldwin &snapshot_kern_structs[i]); 922483d953aSJohn Baldwin if (ret != 0) 923483d953aSJohn Baldwin return (ret); 924483d953aSJohn Baldwin } 925483d953aSJohn Baldwin 926483d953aSJohn Baldwin return (0); 927483d953aSJohn Baldwin } 928483d953aSJohn Baldwin 929483d953aSJohn Baldwin int 930483d953aSJohn Baldwin vm_restore_user_dev(struct vmctx *ctx, struct restore_state *rstate, 931483d953aSJohn Baldwin const struct vm_snapshot_dev_info *info) 932483d953aSJohn Baldwin { 933483d953aSJohn Baldwin void *dev_ptr; 934483d953aSJohn Baldwin size_t dev_size; 935483d953aSJohn Baldwin int ret; 936483d953aSJohn Baldwin struct vm_snapshot_meta *meta; 937483d953aSJohn Baldwin 938483d953aSJohn Baldwin dev_ptr = lookup_dev(info->dev_name, rstate, &dev_size); 939483d953aSJohn Baldwin if (dev_ptr == NULL) { 940483d953aSJohn Baldwin fprintf(stderr, "Failed to lookup dev: %s\r\n", info->dev_name); 941483d953aSJohn Baldwin fprintf(stderr, "Continuing the restore/migration process\r\n"); 942483d953aSJohn Baldwin return (0); 943483d953aSJohn Baldwin } 944483d953aSJohn Baldwin 945483d953aSJohn Baldwin if (dev_size == 0) { 946483d953aSJohn Baldwin fprintf(stderr, "%s: Device size is 0. " 947483d953aSJohn Baldwin "Assuming %s is not used\r\n", 948483d953aSJohn Baldwin __func__, info->dev_name); 949483d953aSJohn Baldwin return (0); 950483d953aSJohn Baldwin } 951483d953aSJohn Baldwin 952483d953aSJohn Baldwin meta = &(struct vm_snapshot_meta) { 953483d953aSJohn Baldwin .ctx = ctx, 954483d953aSJohn Baldwin .dev_name = info->dev_name, 955483d953aSJohn Baldwin 956483d953aSJohn Baldwin .buffer.buf_start = dev_ptr, 957483d953aSJohn Baldwin .buffer.buf_size = dev_size, 958483d953aSJohn Baldwin 959483d953aSJohn Baldwin .buffer.buf = dev_ptr, 960483d953aSJohn Baldwin .buffer.buf_rem = dev_size, 961483d953aSJohn Baldwin 962483d953aSJohn Baldwin .op = VM_SNAPSHOT_RESTORE, 963483d953aSJohn Baldwin }; 964483d953aSJohn Baldwin 965483d953aSJohn Baldwin ret = (*info->snapshot_cb)(meta); 966483d953aSJohn Baldwin if (ret != 0) { 967483d953aSJohn Baldwin fprintf(stderr, "Failed to restore dev: %s\r\n", 968483d953aSJohn Baldwin info->dev_name); 969483d953aSJohn Baldwin return (-1); 970483d953aSJohn Baldwin } 971483d953aSJohn Baldwin 972483d953aSJohn Baldwin return (0); 973483d953aSJohn Baldwin } 974483d953aSJohn Baldwin 975483d953aSJohn Baldwin 976483d953aSJohn Baldwin int 977483d953aSJohn Baldwin vm_restore_user_devs(struct vmctx *ctx, struct restore_state *rstate) 978483d953aSJohn Baldwin { 979483d953aSJohn Baldwin int ret; 980483d953aSJohn Baldwin int i; 981483d953aSJohn Baldwin 982483d953aSJohn Baldwin for (i = 0; i < nitems(snapshot_devs); i++) { 983483d953aSJohn Baldwin ret = vm_restore_user_dev(ctx, rstate, &snapshot_devs[i]); 984483d953aSJohn Baldwin if (ret != 0) 985483d953aSJohn Baldwin return (ret); 986483d953aSJohn Baldwin } 987483d953aSJohn Baldwin 988483d953aSJohn Baldwin return 0; 989483d953aSJohn Baldwin } 990483d953aSJohn Baldwin 991483d953aSJohn Baldwin int 992483d953aSJohn Baldwin vm_pause_user_devs(struct vmctx *ctx) 993483d953aSJohn Baldwin { 994483d953aSJohn Baldwin const struct vm_snapshot_dev_info *info; 995483d953aSJohn Baldwin int ret; 996483d953aSJohn Baldwin int i; 997483d953aSJohn Baldwin 998483d953aSJohn Baldwin for (i = 0; i < nitems(snapshot_devs); i++) { 999483d953aSJohn Baldwin info = &snapshot_devs[i]; 1000483d953aSJohn Baldwin if (info->pause_cb == NULL) 1001483d953aSJohn Baldwin continue; 1002483d953aSJohn Baldwin 1003483d953aSJohn Baldwin ret = info->pause_cb(ctx, info->dev_name); 1004483d953aSJohn Baldwin if (ret != 0) 1005483d953aSJohn Baldwin return (ret); 1006483d953aSJohn Baldwin } 1007483d953aSJohn Baldwin 1008483d953aSJohn Baldwin return (0); 1009483d953aSJohn Baldwin } 1010483d953aSJohn Baldwin 1011483d953aSJohn Baldwin int 1012483d953aSJohn Baldwin vm_resume_user_devs(struct vmctx *ctx) 1013483d953aSJohn Baldwin { 1014483d953aSJohn Baldwin const struct vm_snapshot_dev_info *info; 1015483d953aSJohn Baldwin int ret; 1016483d953aSJohn Baldwin int i; 1017483d953aSJohn Baldwin 1018483d953aSJohn Baldwin for (i = 0; i < nitems(snapshot_devs); i++) { 1019483d953aSJohn Baldwin info = &snapshot_devs[i]; 1020483d953aSJohn Baldwin if (info->resume_cb == NULL) 1021483d953aSJohn Baldwin continue; 1022483d953aSJohn Baldwin 1023483d953aSJohn Baldwin ret = info->resume_cb(ctx, info->dev_name); 1024483d953aSJohn Baldwin if (ret != 0) 1025483d953aSJohn Baldwin return (ret); 1026483d953aSJohn Baldwin } 1027483d953aSJohn Baldwin 1028483d953aSJohn Baldwin return (0); 1029483d953aSJohn Baldwin } 1030483d953aSJohn Baldwin 1031483d953aSJohn Baldwin static int 1032483d953aSJohn Baldwin vm_snapshot_kern_struct(int data_fd, xo_handle_t *xop, const char *array_key, 1033483d953aSJohn Baldwin struct vm_snapshot_meta *meta, off_t *offset) 1034483d953aSJohn Baldwin { 1035483d953aSJohn Baldwin int ret; 1036483d953aSJohn Baldwin size_t data_size; 1037483d953aSJohn Baldwin ssize_t write_cnt; 1038483d953aSJohn Baldwin 1039483d953aSJohn Baldwin ret = vm_snapshot_req(meta); 1040483d953aSJohn Baldwin if (ret != 0) { 1041483d953aSJohn Baldwin fprintf(stderr, "%s: Failed to snapshot struct %s\r\n", 1042483d953aSJohn Baldwin __func__, meta->dev_name); 1043483d953aSJohn Baldwin ret = -1; 1044483d953aSJohn Baldwin goto done; 1045483d953aSJohn Baldwin } 1046483d953aSJohn Baldwin 1047483d953aSJohn Baldwin data_size = vm_get_snapshot_size(meta); 1048483d953aSJohn Baldwin 1049483d953aSJohn Baldwin write_cnt = write(data_fd, meta->buffer.buf_start, data_size); 1050483d953aSJohn Baldwin if (write_cnt != data_size) { 1051483d953aSJohn Baldwin perror("Failed to write all snapshotted data."); 1052483d953aSJohn Baldwin ret = -1; 1053483d953aSJohn Baldwin goto done; 1054483d953aSJohn Baldwin } 1055483d953aSJohn Baldwin 1056483d953aSJohn Baldwin /* Write metadata. */ 1057483d953aSJohn Baldwin xo_open_instance_h(xop, array_key); 1058483d953aSJohn Baldwin xo_emit_h(xop, "{:debug_name/%s}\n", meta->dev_name); 1059483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_SNAPSHOT_REQ_KEY "/%d}\n", 1060483d953aSJohn Baldwin meta->dev_req); 1061483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_SIZE_KEY "/%lu}\n", data_size); 1062483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_FILE_OFFSET_KEY "/%lu}\n", *offset); 1063483d953aSJohn Baldwin xo_close_instance_h(xop, JSON_STRUCT_ARR_KEY); 1064483d953aSJohn Baldwin 1065483d953aSJohn Baldwin *offset += data_size; 1066483d953aSJohn Baldwin 1067483d953aSJohn Baldwin done: 1068483d953aSJohn Baldwin return (ret); 1069483d953aSJohn Baldwin } 1070483d953aSJohn Baldwin 1071483d953aSJohn Baldwin static int 1072483d953aSJohn Baldwin vm_snapshot_kern_structs(struct vmctx *ctx, int data_fd, xo_handle_t *xop) 1073483d953aSJohn Baldwin { 1074483d953aSJohn Baldwin int ret, i, error; 1075483d953aSJohn Baldwin size_t offset, buf_size; 1076483d953aSJohn Baldwin char *buffer; 1077483d953aSJohn Baldwin struct vm_snapshot_meta *meta; 1078483d953aSJohn Baldwin 1079483d953aSJohn Baldwin error = 0; 1080483d953aSJohn Baldwin offset = 0; 1081483d953aSJohn Baldwin buf_size = SNAPSHOT_BUFFER_SIZE; 1082483d953aSJohn Baldwin 1083483d953aSJohn Baldwin buffer = malloc(SNAPSHOT_BUFFER_SIZE * sizeof(char)); 1084483d953aSJohn Baldwin if (buffer == NULL) { 1085483d953aSJohn Baldwin error = ENOMEM; 1086483d953aSJohn Baldwin perror("Failed to allocate memory for snapshot buffer"); 1087483d953aSJohn Baldwin goto err_vm_snapshot_kern_data; 1088483d953aSJohn Baldwin } 1089483d953aSJohn Baldwin 1090483d953aSJohn Baldwin meta = &(struct vm_snapshot_meta) { 1091483d953aSJohn Baldwin .ctx = ctx, 1092483d953aSJohn Baldwin 1093483d953aSJohn Baldwin .buffer.buf_start = buffer, 1094483d953aSJohn Baldwin .buffer.buf_size = buf_size, 1095483d953aSJohn Baldwin 1096483d953aSJohn Baldwin .op = VM_SNAPSHOT_SAVE, 1097483d953aSJohn Baldwin }; 1098483d953aSJohn Baldwin 1099483d953aSJohn Baldwin xo_open_list_h(xop, JSON_STRUCT_ARR_KEY); 1100483d953aSJohn Baldwin for (i = 0; i < nitems(snapshot_kern_structs); i++) { 1101483d953aSJohn Baldwin meta->dev_name = snapshot_kern_structs[i].struct_name; 1102483d953aSJohn Baldwin meta->dev_req = snapshot_kern_structs[i].req; 1103483d953aSJohn Baldwin 1104483d953aSJohn Baldwin memset(meta->buffer.buf_start, 0, meta->buffer.buf_size); 1105483d953aSJohn Baldwin meta->buffer.buf = meta->buffer.buf_start; 1106483d953aSJohn Baldwin meta->buffer.buf_rem = meta->buffer.buf_size; 1107483d953aSJohn Baldwin 1108483d953aSJohn Baldwin ret = vm_snapshot_kern_struct(data_fd, xop, JSON_DEV_ARR_KEY, 1109483d953aSJohn Baldwin meta, &offset); 1110483d953aSJohn Baldwin if (ret != 0) { 1111483d953aSJohn Baldwin error = -1; 1112483d953aSJohn Baldwin goto err_vm_snapshot_kern_data; 1113483d953aSJohn Baldwin } 1114483d953aSJohn Baldwin } 1115483d953aSJohn Baldwin xo_close_list_h(xop, JSON_STRUCT_ARR_KEY); 1116483d953aSJohn Baldwin 1117483d953aSJohn Baldwin err_vm_snapshot_kern_data: 1118483d953aSJohn Baldwin if (buffer != NULL) 1119483d953aSJohn Baldwin free(buffer); 1120483d953aSJohn Baldwin return (error); 1121483d953aSJohn Baldwin } 1122483d953aSJohn Baldwin 1123483d953aSJohn Baldwin static int 1124483d953aSJohn Baldwin vm_snapshot_basic_metadata(struct vmctx *ctx, xo_handle_t *xop, size_t memsz) 1125483d953aSJohn Baldwin { 1126483d953aSJohn Baldwin int error; 1127483d953aSJohn Baldwin int memflags; 1128483d953aSJohn Baldwin char vmname_buf[MAX_VMNAME]; 1129483d953aSJohn Baldwin 1130483d953aSJohn Baldwin memset(vmname_buf, 0, MAX_VMNAME); 1131483d953aSJohn Baldwin error = vm_get_name(ctx, vmname_buf, MAX_VMNAME - 1); 1132483d953aSJohn Baldwin if (error != 0) { 1133483d953aSJohn Baldwin perror("Failed to get VM name"); 1134483d953aSJohn Baldwin goto err; 1135483d953aSJohn Baldwin } 1136483d953aSJohn Baldwin 1137483d953aSJohn Baldwin memflags = vm_get_memflags(ctx); 1138483d953aSJohn Baldwin 1139483d953aSJohn Baldwin xo_open_container_h(xop, JSON_BASIC_METADATA_KEY); 1140483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_NCPUS_KEY "/%ld}\n", guest_ncpus); 1141483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_VMNAME_KEY "/%s}\n", vmname_buf); 1142483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_MEMSIZE_KEY "/%lu}\n", memsz); 1143483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_MEMFLAGS_KEY "/%d}\n", memflags); 1144483d953aSJohn Baldwin xo_close_container_h(xop, JSON_BASIC_METADATA_KEY); 1145483d953aSJohn Baldwin 1146483d953aSJohn Baldwin err: 1147483d953aSJohn Baldwin return (error); 1148483d953aSJohn Baldwin } 1149483d953aSJohn Baldwin 1150483d953aSJohn Baldwin static int 1151483d953aSJohn Baldwin vm_snapshot_dev_write_data(int data_fd, xo_handle_t *xop, const char *array_key, 1152483d953aSJohn Baldwin struct vm_snapshot_meta *meta, off_t *offset) 1153483d953aSJohn Baldwin { 1154483d953aSJohn Baldwin int ret; 1155483d953aSJohn Baldwin size_t data_size; 1156483d953aSJohn Baldwin 1157483d953aSJohn Baldwin data_size = vm_get_snapshot_size(meta); 1158483d953aSJohn Baldwin 1159483d953aSJohn Baldwin ret = write(data_fd, meta->buffer.buf_start, data_size); 1160483d953aSJohn Baldwin if (ret != data_size) { 1161483d953aSJohn Baldwin perror("Failed to write all snapshotted data."); 1162483d953aSJohn Baldwin return (-1); 1163483d953aSJohn Baldwin } 1164483d953aSJohn Baldwin 1165483d953aSJohn Baldwin /* Write metadata. */ 1166483d953aSJohn Baldwin xo_open_instance_h(xop, array_key); 1167483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_SNAPSHOT_REQ_KEY "/%s}\n", meta->dev_name); 1168483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_SIZE_KEY "/%lu}\n", data_size); 1169483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_FILE_OFFSET_KEY "/%lu}\n", *offset); 1170483d953aSJohn Baldwin xo_close_instance_h(xop, array_key); 1171483d953aSJohn Baldwin 1172483d953aSJohn Baldwin *offset += data_size; 1173483d953aSJohn Baldwin 1174483d953aSJohn Baldwin return (0); 1175483d953aSJohn Baldwin } 1176483d953aSJohn Baldwin 1177483d953aSJohn Baldwin static int 1178483d953aSJohn Baldwin vm_snapshot_user_dev(const struct vm_snapshot_dev_info *info, 1179483d953aSJohn Baldwin int data_fd, xo_handle_t *xop, 1180483d953aSJohn Baldwin struct vm_snapshot_meta *meta, off_t *offset) 1181483d953aSJohn Baldwin { 1182483d953aSJohn Baldwin int ret; 1183483d953aSJohn Baldwin 1184483d953aSJohn Baldwin ret = (*info->snapshot_cb)(meta); 1185483d953aSJohn Baldwin if (ret != 0) { 1186483d953aSJohn Baldwin fprintf(stderr, "Failed to snapshot %s; ret=%d\r\n", 1187483d953aSJohn Baldwin meta->dev_name, ret); 1188483d953aSJohn Baldwin return (ret); 1189483d953aSJohn Baldwin } 1190483d953aSJohn Baldwin 1191483d953aSJohn Baldwin ret = vm_snapshot_dev_write_data(data_fd, xop, JSON_DEV_ARR_KEY, meta, 1192483d953aSJohn Baldwin offset); 1193483d953aSJohn Baldwin if (ret != 0) 1194483d953aSJohn Baldwin return (ret); 1195483d953aSJohn Baldwin 1196483d953aSJohn Baldwin return (0); 1197483d953aSJohn Baldwin } 1198483d953aSJohn Baldwin 1199483d953aSJohn Baldwin static int 1200483d953aSJohn Baldwin vm_snapshot_user_devs(struct vmctx *ctx, int data_fd, xo_handle_t *xop) 1201483d953aSJohn Baldwin { 1202483d953aSJohn Baldwin int ret, i; 1203483d953aSJohn Baldwin off_t offset; 1204483d953aSJohn Baldwin void *buffer; 1205483d953aSJohn Baldwin size_t buf_size; 1206483d953aSJohn Baldwin struct vm_snapshot_meta *meta; 1207483d953aSJohn Baldwin 1208483d953aSJohn Baldwin buf_size = SNAPSHOT_BUFFER_SIZE; 1209483d953aSJohn Baldwin 1210483d953aSJohn Baldwin offset = lseek(data_fd, 0, SEEK_CUR); 1211483d953aSJohn Baldwin if (offset < 0) { 1212483d953aSJohn Baldwin perror("Failed to get data file current offset."); 1213483d953aSJohn Baldwin return (-1); 1214483d953aSJohn Baldwin } 1215483d953aSJohn Baldwin 1216483d953aSJohn Baldwin buffer = malloc(buf_size); 1217483d953aSJohn Baldwin if (buffer == NULL) { 1218483d953aSJohn Baldwin perror("Failed to allocate memory for snapshot buffer"); 1219483d953aSJohn Baldwin ret = ENOSPC; 1220483d953aSJohn Baldwin goto snapshot_err; 1221483d953aSJohn Baldwin } 1222483d953aSJohn Baldwin 1223483d953aSJohn Baldwin meta = &(struct vm_snapshot_meta) { 1224483d953aSJohn Baldwin .ctx = ctx, 1225483d953aSJohn Baldwin 1226483d953aSJohn Baldwin .buffer.buf_start = buffer, 1227483d953aSJohn Baldwin .buffer.buf_size = buf_size, 1228483d953aSJohn Baldwin 1229483d953aSJohn Baldwin .op = VM_SNAPSHOT_SAVE, 1230483d953aSJohn Baldwin }; 1231483d953aSJohn Baldwin 1232483d953aSJohn Baldwin xo_open_list_h(xop, JSON_DEV_ARR_KEY); 1233483d953aSJohn Baldwin 1234483d953aSJohn Baldwin /* Restore other devices that support this feature */ 1235483d953aSJohn Baldwin for (i = 0; i < nitems(snapshot_devs); i++) { 1236483d953aSJohn Baldwin meta->dev_name = snapshot_devs[i].dev_name; 1237483d953aSJohn Baldwin 1238483d953aSJohn Baldwin memset(meta->buffer.buf_start, 0, meta->buffer.buf_size); 1239483d953aSJohn Baldwin meta->buffer.buf = meta->buffer.buf_start; 1240483d953aSJohn Baldwin meta->buffer.buf_rem = meta->buffer.buf_size; 1241483d953aSJohn Baldwin 1242483d953aSJohn Baldwin ret = vm_snapshot_user_dev(&snapshot_devs[i], data_fd, xop, 1243483d953aSJohn Baldwin meta, &offset); 1244483d953aSJohn Baldwin if (ret != 0) 1245483d953aSJohn Baldwin goto snapshot_err; 1246483d953aSJohn Baldwin } 1247483d953aSJohn Baldwin 1248483d953aSJohn Baldwin xo_close_list_h(xop, JSON_DEV_ARR_KEY); 1249483d953aSJohn Baldwin 1250483d953aSJohn Baldwin snapshot_err: 1251483d953aSJohn Baldwin if (buffer != NULL) 1252483d953aSJohn Baldwin free(buffer); 1253483d953aSJohn Baldwin return (ret); 1254483d953aSJohn Baldwin } 1255483d953aSJohn Baldwin 1256483d953aSJohn Baldwin void 1257483d953aSJohn Baldwin checkpoint_cpu_add(int vcpu) 1258483d953aSJohn Baldwin { 1259483d953aSJohn Baldwin 1260483d953aSJohn Baldwin pthread_mutex_lock(&vcpu_lock); 1261483d953aSJohn Baldwin CPU_SET(vcpu, &vcpus_active); 1262483d953aSJohn Baldwin 1263483d953aSJohn Baldwin if (checkpoint_active) { 1264483d953aSJohn Baldwin CPU_SET(vcpu, &vcpus_suspended); 1265483d953aSJohn Baldwin while (checkpoint_active) 1266483d953aSJohn Baldwin pthread_cond_wait(&vcpus_can_run, &vcpu_lock); 1267483d953aSJohn Baldwin CPU_CLR(vcpu, &vcpus_suspended); 1268483d953aSJohn Baldwin } 1269483d953aSJohn Baldwin pthread_mutex_unlock(&vcpu_lock); 1270483d953aSJohn Baldwin } 1271483d953aSJohn Baldwin 1272483d953aSJohn Baldwin /* 1273483d953aSJohn Baldwin * When a vCPU is suspended for any reason, it calls 1274483d953aSJohn Baldwin * checkpoint_cpu_suspend(). This records that the vCPU is idle. 1275483d953aSJohn Baldwin * Before returning from suspension, checkpoint_cpu_resume() is 1276483d953aSJohn Baldwin * called. In suspend we note that the vCPU is idle. In resume we 1277483d953aSJohn Baldwin * pause the vCPU thread until the checkpoint is complete. The reason 1278483d953aSJohn Baldwin * for the two-step process is that vCPUs might already be stopped in 1279483d953aSJohn Baldwin * the debug server when a checkpoint is requested. This approach 1280483d953aSJohn Baldwin * allows us to account for and handle those vCPUs. 1281483d953aSJohn Baldwin */ 1282483d953aSJohn Baldwin void 1283483d953aSJohn Baldwin checkpoint_cpu_suspend(int vcpu) 1284483d953aSJohn Baldwin { 1285483d953aSJohn Baldwin 1286483d953aSJohn Baldwin pthread_mutex_lock(&vcpu_lock); 1287483d953aSJohn Baldwin CPU_SET(vcpu, &vcpus_suspended); 1288483d953aSJohn Baldwin if (checkpoint_active && CPU_CMP(&vcpus_active, &vcpus_suspended) == 0) 1289483d953aSJohn Baldwin pthread_cond_signal(&vcpus_idle); 1290483d953aSJohn Baldwin pthread_mutex_unlock(&vcpu_lock); 1291483d953aSJohn Baldwin } 1292483d953aSJohn Baldwin 1293483d953aSJohn Baldwin void 1294483d953aSJohn Baldwin checkpoint_cpu_resume(int vcpu) 1295483d953aSJohn Baldwin { 1296483d953aSJohn Baldwin 1297483d953aSJohn Baldwin pthread_mutex_lock(&vcpu_lock); 1298483d953aSJohn Baldwin while (checkpoint_active) 1299483d953aSJohn Baldwin pthread_cond_wait(&vcpus_can_run, &vcpu_lock); 1300483d953aSJohn Baldwin CPU_CLR(vcpu, &vcpus_suspended); 1301483d953aSJohn Baldwin pthread_mutex_unlock(&vcpu_lock); 1302483d953aSJohn Baldwin } 1303483d953aSJohn Baldwin 1304483d953aSJohn Baldwin static void 1305483d953aSJohn Baldwin vm_vcpu_pause(struct vmctx *ctx) 1306483d953aSJohn Baldwin { 1307483d953aSJohn Baldwin 1308483d953aSJohn Baldwin pthread_mutex_lock(&vcpu_lock); 1309483d953aSJohn Baldwin checkpoint_active = true; 1310483d953aSJohn Baldwin vm_suspend_cpu(ctx, -1); 1311483d953aSJohn Baldwin while (CPU_CMP(&vcpus_active, &vcpus_suspended) != 0) 1312483d953aSJohn Baldwin pthread_cond_wait(&vcpus_idle, &vcpu_lock); 1313483d953aSJohn Baldwin pthread_mutex_unlock(&vcpu_lock); 1314483d953aSJohn Baldwin } 1315483d953aSJohn Baldwin 1316483d953aSJohn Baldwin static void 1317483d953aSJohn Baldwin vm_vcpu_resume(struct vmctx *ctx) 1318483d953aSJohn Baldwin { 1319483d953aSJohn Baldwin 1320483d953aSJohn Baldwin pthread_mutex_lock(&vcpu_lock); 1321483d953aSJohn Baldwin checkpoint_active = false; 1322483d953aSJohn Baldwin pthread_mutex_unlock(&vcpu_lock); 1323483d953aSJohn Baldwin vm_resume_cpu(ctx, -1); 1324483d953aSJohn Baldwin pthread_cond_broadcast(&vcpus_can_run); 1325483d953aSJohn Baldwin } 1326483d953aSJohn Baldwin 1327483d953aSJohn Baldwin static int 1328edfb339dSRobert Wing vm_checkpoint(struct vmctx *ctx, const char *checkpoint_file, bool stop_vm) 1329483d953aSJohn Baldwin { 1330483d953aSJohn Baldwin int fd_checkpoint = 0, kdata_fd = 0; 1331483d953aSJohn Baldwin int ret = 0; 1332483d953aSJohn Baldwin int error = 0; 1333483d953aSJohn Baldwin size_t memsz; 1334483d953aSJohn Baldwin xo_handle_t *xop = NULL; 1335483d953aSJohn Baldwin char *meta_filename = NULL; 1336483d953aSJohn Baldwin char *kdata_filename = NULL; 1337483d953aSJohn Baldwin FILE *meta_file = NULL; 1338483d953aSJohn Baldwin 1339483d953aSJohn Baldwin kdata_filename = strcat_extension(checkpoint_file, ".kern"); 1340483d953aSJohn Baldwin if (kdata_filename == NULL) { 1341483d953aSJohn Baldwin fprintf(stderr, "Failed to construct kernel data filename.\n"); 1342483d953aSJohn Baldwin return (-1); 1343483d953aSJohn Baldwin } 1344483d953aSJohn Baldwin 1345483d953aSJohn Baldwin kdata_fd = open(kdata_filename, O_WRONLY | O_CREAT | O_TRUNC, 0700); 1346483d953aSJohn Baldwin if (kdata_fd < 0) { 1347483d953aSJohn Baldwin perror("Failed to open kernel data snapshot file."); 1348483d953aSJohn Baldwin error = -1; 1349483d953aSJohn Baldwin goto done; 1350483d953aSJohn Baldwin } 1351483d953aSJohn Baldwin 1352483d953aSJohn Baldwin fd_checkpoint = open(checkpoint_file, O_RDWR | O_CREAT | O_TRUNC, 0700); 1353483d953aSJohn Baldwin 1354483d953aSJohn Baldwin if (fd_checkpoint < 0) { 1355483d953aSJohn Baldwin perror("Failed to create checkpoint file"); 1356483d953aSJohn Baldwin error = -1; 1357483d953aSJohn Baldwin goto done; 1358483d953aSJohn Baldwin } 1359483d953aSJohn Baldwin 1360483d953aSJohn Baldwin meta_filename = strcat_extension(checkpoint_file, ".meta"); 1361483d953aSJohn Baldwin if (meta_filename == NULL) { 1362483d953aSJohn Baldwin fprintf(stderr, "Failed to construct vm metadata filename.\n"); 1363483d953aSJohn Baldwin goto done; 1364483d953aSJohn Baldwin } 1365483d953aSJohn Baldwin 1366483d953aSJohn Baldwin meta_file = fopen(meta_filename, "w"); 1367483d953aSJohn Baldwin if (meta_file == NULL) { 1368483d953aSJohn Baldwin perror("Failed to open vm metadata snapshot file."); 1369483d953aSJohn Baldwin goto done; 1370483d953aSJohn Baldwin } 1371483d953aSJohn Baldwin 1372483d953aSJohn Baldwin xop = xo_create_to_file(meta_file, XO_STYLE_JSON, XOF_PRETTY); 1373483d953aSJohn Baldwin if (xop == NULL) { 1374483d953aSJohn Baldwin perror("Failed to get libxo handle on metadata file."); 1375483d953aSJohn Baldwin goto done; 1376483d953aSJohn Baldwin } 1377483d953aSJohn Baldwin 1378483d953aSJohn Baldwin vm_vcpu_pause(ctx); 1379483d953aSJohn Baldwin 1380483d953aSJohn Baldwin ret = vm_pause_user_devs(ctx); 1381483d953aSJohn Baldwin if (ret != 0) { 1382483d953aSJohn Baldwin fprintf(stderr, "Could not pause devices\r\n"); 1383483d953aSJohn Baldwin error = ret; 1384483d953aSJohn Baldwin goto done; 1385483d953aSJohn Baldwin } 1386483d953aSJohn Baldwin 1387483d953aSJohn Baldwin memsz = vm_snapshot_mem(ctx, fd_checkpoint, 0, true); 1388483d953aSJohn Baldwin if (memsz == 0) { 1389483d953aSJohn Baldwin perror("Could not write guest memory to file"); 1390483d953aSJohn Baldwin error = -1; 1391483d953aSJohn Baldwin goto done; 1392483d953aSJohn Baldwin } 1393483d953aSJohn Baldwin 1394483d953aSJohn Baldwin ret = vm_snapshot_basic_metadata(ctx, xop, memsz); 1395483d953aSJohn Baldwin if (ret != 0) { 1396483d953aSJohn Baldwin fprintf(stderr, "Failed to snapshot vm basic metadata.\n"); 1397483d953aSJohn Baldwin error = -1; 1398483d953aSJohn Baldwin goto done; 1399483d953aSJohn Baldwin } 1400483d953aSJohn Baldwin 1401483d953aSJohn Baldwin 1402483d953aSJohn Baldwin ret = vm_snapshot_kern_structs(ctx, kdata_fd, xop); 1403483d953aSJohn Baldwin if (ret != 0) { 1404483d953aSJohn Baldwin fprintf(stderr, "Failed to snapshot vm kernel data.\n"); 1405483d953aSJohn Baldwin error = -1; 1406483d953aSJohn Baldwin goto done; 1407483d953aSJohn Baldwin } 1408483d953aSJohn Baldwin 1409483d953aSJohn Baldwin ret = vm_snapshot_user_devs(ctx, kdata_fd, xop); 1410483d953aSJohn Baldwin if (ret != 0) { 1411483d953aSJohn Baldwin fprintf(stderr, "Failed to snapshot device state.\n"); 1412483d953aSJohn Baldwin error = -1; 1413483d953aSJohn Baldwin goto done; 1414483d953aSJohn Baldwin } 1415483d953aSJohn Baldwin 1416483d953aSJohn Baldwin xo_finish_h(xop); 1417483d953aSJohn Baldwin 1418483d953aSJohn Baldwin if (stop_vm) { 1419483d953aSJohn Baldwin vm_destroy(ctx); 1420483d953aSJohn Baldwin exit(0); 1421483d953aSJohn Baldwin } 1422483d953aSJohn Baldwin 1423483d953aSJohn Baldwin done: 1424483d953aSJohn Baldwin ret = vm_resume_user_devs(ctx); 1425483d953aSJohn Baldwin if (ret != 0) 1426483d953aSJohn Baldwin fprintf(stderr, "Could not resume devices\r\n"); 1427483d953aSJohn Baldwin vm_vcpu_resume(ctx); 1428483d953aSJohn Baldwin if (fd_checkpoint > 0) 1429483d953aSJohn Baldwin close(fd_checkpoint); 1430483d953aSJohn Baldwin if (meta_filename != NULL) 1431483d953aSJohn Baldwin free(meta_filename); 1432483d953aSJohn Baldwin if (kdata_filename != NULL) 1433483d953aSJohn Baldwin free(kdata_filename); 1434483d953aSJohn Baldwin if (xop != NULL) 1435483d953aSJohn Baldwin xo_destroy(xop); 1436483d953aSJohn Baldwin if (meta_file != NULL) 1437483d953aSJohn Baldwin fclose(meta_file); 1438483d953aSJohn Baldwin if (kdata_fd > 0) 1439483d953aSJohn Baldwin close(kdata_fd); 1440483d953aSJohn Baldwin return (error); 1441483d953aSJohn Baldwin } 1442483d953aSJohn Baldwin 1443edfb339dSRobert Wing static int 1444edfb339dSRobert Wing handle_message(struct vmctx *ctx, nvlist_t *nvl) 1445483d953aSJohn Baldwin { 1446*4379c1daSRobert Wing int err; 1447*4379c1daSRobert Wing const char *cmd; 1448483d953aSJohn Baldwin 1449*4379c1daSRobert Wing if (!nvlist_exists_string(nvl, "cmd")) 1450edfb339dSRobert Wing return (-1); 1451edfb339dSRobert Wing 1452*4379c1daSRobert Wing cmd = nvlist_get_string(nvl, "cmd"); 1453*4379c1daSRobert Wing if (strcmp(cmd, "checkpoint") == 0) { 1454*4379c1daSRobert Wing if (!nvlist_exists_string(nvl, "filename") || 1455*4379c1daSRobert Wing !nvlist_exists_bool(nvl, "suspend")) 1456edfb339dSRobert Wing err = -1; 1457edfb339dSRobert Wing else 1458edfb339dSRobert Wing err = vm_checkpoint(ctx, nvlist_get_string(nvl, "filename"), 1459*4379c1daSRobert Wing nvlist_get_bool(nvl, "suspend")); 1460*4379c1daSRobert Wing } else { 146138dfb062SRobert Wing EPRINTLN("Unrecognized checkpoint operation\n"); 1462483d953aSJohn Baldwin err = -1; 1463483d953aSJohn Baldwin } 1464483d953aSJohn Baldwin 146538dfb062SRobert Wing if (err != 0) 146638dfb062SRobert Wing EPRINTLN("Unable to perform the requested operation\n"); 146738dfb062SRobert Wing 1468edfb339dSRobert Wing nvlist_destroy(nvl); 1469483d953aSJohn Baldwin return (err); 1470483d953aSJohn Baldwin } 1471483d953aSJohn Baldwin 1472483d953aSJohn Baldwin /* 1473483d953aSJohn Baldwin * Listen for commands from bhyvectl 1474483d953aSJohn Baldwin */ 1475483d953aSJohn Baldwin void * 1476483d953aSJohn Baldwin checkpoint_thread(void *param) 1477483d953aSJohn Baldwin { 1478483d953aSJohn Baldwin struct checkpoint_thread_info *thread_info; 1479edfb339dSRobert Wing nvlist_t *nvl; 1480483d953aSJohn Baldwin 1481483d953aSJohn Baldwin pthread_set_name_np(pthread_self(), "checkpoint thread"); 1482483d953aSJohn Baldwin thread_info = (struct checkpoint_thread_info *)param; 1483483d953aSJohn Baldwin 148438dfb062SRobert Wing for (;;) { 1485edfb339dSRobert Wing nvl = nvlist_recv(thread_info->socket_fd, 0); 1486edfb339dSRobert Wing if (nvl != NULL) 1487edfb339dSRobert Wing handle_message(thread_info->ctx, nvl); 148838dfb062SRobert Wing else 1489edfb339dSRobert Wing EPRINTLN("nvlist_recv() failed: %s", strerror(errno)); 1490483d953aSJohn Baldwin } 1491483d953aSJohn Baldwin 1492483d953aSJohn Baldwin return (NULL); 1493483d953aSJohn Baldwin } 1494483d953aSJohn Baldwin 1495fdbc86cfSRobert Wing void 1496fdbc86cfSRobert Wing init_snapshot(void) 1497fdbc86cfSRobert Wing { 1498fdbc86cfSRobert Wing int err; 1499fdbc86cfSRobert Wing 1500fdbc86cfSRobert Wing err = pthread_mutex_init(&vcpu_lock, NULL); 1501fdbc86cfSRobert Wing if (err != 0) 1502fdbc86cfSRobert Wing errc(1, err, "checkpoint mutex init"); 1503fdbc86cfSRobert Wing err = pthread_cond_init(&vcpus_idle, NULL); 1504fdbc86cfSRobert Wing if (err != 0) 1505fdbc86cfSRobert Wing errc(1, err, "checkpoint cv init (vcpus_idle)"); 1506fdbc86cfSRobert Wing err = pthread_cond_init(&vcpus_can_run, NULL); 1507fdbc86cfSRobert Wing if (err != 0) 1508fdbc86cfSRobert Wing errc(1, err, "checkpoint cv init (vcpus_can_run)"); 1509fdbc86cfSRobert Wing } 1510fdbc86cfSRobert Wing 1511483d953aSJohn Baldwin /* 1512483d953aSJohn Baldwin * Create the listening socket for IPC with bhyvectl 1513483d953aSJohn Baldwin */ 1514483d953aSJohn Baldwin int 1515483d953aSJohn Baldwin init_checkpoint_thread(struct vmctx *ctx) 1516483d953aSJohn Baldwin { 1517483d953aSJohn Baldwin struct checkpoint_thread_info *checkpoint_info = NULL; 1518483d953aSJohn Baldwin struct sockaddr_un addr; 1519483d953aSJohn Baldwin int socket_fd; 1520483d953aSJohn Baldwin pthread_t checkpoint_pthread; 1521483d953aSJohn Baldwin char vmname_buf[MAX_VMNAME]; 152251fbd894SRobert Wing int err; 1523483d953aSJohn Baldwin 1524483d953aSJohn Baldwin memset(&addr, 0, sizeof(addr)); 1525483d953aSJohn Baldwin 152638dfb062SRobert Wing socket_fd = socket(PF_UNIX, SOCK_DGRAM, 0); 1527483d953aSJohn Baldwin if (socket_fd < 0) { 152838dfb062SRobert Wing EPRINTLN("Socket creation failed: %s", strerror(errno)); 1529483d953aSJohn Baldwin err = -1; 1530483d953aSJohn Baldwin goto fail; 1531483d953aSJohn Baldwin } 1532483d953aSJohn Baldwin 1533483d953aSJohn Baldwin addr.sun_family = AF_UNIX; 1534483d953aSJohn Baldwin 1535483d953aSJohn Baldwin err = vm_get_name(ctx, vmname_buf, MAX_VMNAME - 1); 1536483d953aSJohn Baldwin if (err != 0) { 1537483d953aSJohn Baldwin perror("Failed to get VM name"); 1538483d953aSJohn Baldwin goto fail; 1539483d953aSJohn Baldwin } 1540483d953aSJohn Baldwin 15415ce2d4a1SRobert Wing snprintf(addr.sun_path, sizeof(addr.sun_path), "%s%s", 15425ce2d4a1SRobert Wing BHYVE_RUN_DIR, vmname_buf); 1543483d953aSJohn Baldwin addr.sun_len = SUN_LEN(&addr); 1544483d953aSJohn Baldwin unlink(addr.sun_path); 1545483d953aSJohn Baldwin 1546483d953aSJohn Baldwin if (bind(socket_fd, (struct sockaddr *)&addr, addr.sun_len) != 0) { 154738dfb062SRobert Wing EPRINTLN("Failed to bind socket \"%s\": %s\n", 154838dfb062SRobert Wing addr.sun_path, strerror(errno)); 1549483d953aSJohn Baldwin err = -1; 1550483d953aSJohn Baldwin goto fail; 1551483d953aSJohn Baldwin } 1552483d953aSJohn Baldwin 1553483d953aSJohn Baldwin checkpoint_info = calloc(1, sizeof(*checkpoint_info)); 1554483d953aSJohn Baldwin checkpoint_info->ctx = ctx; 1555483d953aSJohn Baldwin checkpoint_info->socket_fd = socket_fd; 1556483d953aSJohn Baldwin 155751fbd894SRobert Wing err = pthread_create(&checkpoint_pthread, NULL, checkpoint_thread, 1558483d953aSJohn Baldwin checkpoint_info); 155951fbd894SRobert Wing if (err != 0) 1560483d953aSJohn Baldwin goto fail; 1561483d953aSJohn Baldwin 1562483d953aSJohn Baldwin return (0); 1563483d953aSJohn Baldwin fail: 1564483d953aSJohn Baldwin free(checkpoint_info); 1565483d953aSJohn Baldwin if (socket_fd > 0) 1566483d953aSJohn Baldwin close(socket_fd); 1567483d953aSJohn Baldwin unlink(addr.sun_path); 1568483d953aSJohn Baldwin 1569483d953aSJohn Baldwin return (err); 1570483d953aSJohn Baldwin } 1571483d953aSJohn Baldwin 1572483d953aSJohn Baldwin void 1573483d953aSJohn Baldwin vm_snapshot_buf_err(const char *bufname, const enum vm_snapshot_op op) 1574483d953aSJohn Baldwin { 1575483d953aSJohn Baldwin const char *__op; 1576483d953aSJohn Baldwin 1577483d953aSJohn Baldwin if (op == VM_SNAPSHOT_SAVE) 1578483d953aSJohn Baldwin __op = "save"; 1579483d953aSJohn Baldwin else if (op == VM_SNAPSHOT_RESTORE) 1580483d953aSJohn Baldwin __op = "restore"; 1581483d953aSJohn Baldwin else 1582483d953aSJohn Baldwin __op = "unknown"; 1583483d953aSJohn Baldwin 1584483d953aSJohn Baldwin fprintf(stderr, "%s: snapshot-%s failed for %s\r\n", 1585483d953aSJohn Baldwin __func__, __op, bufname); 1586483d953aSJohn Baldwin } 1587483d953aSJohn Baldwin 1588483d953aSJohn Baldwin int 1589483d953aSJohn Baldwin vm_snapshot_buf(volatile void *data, size_t data_size, 1590483d953aSJohn Baldwin struct vm_snapshot_meta *meta) 1591483d953aSJohn Baldwin { 1592483d953aSJohn Baldwin struct vm_snapshot_buffer *buffer; 1593483d953aSJohn Baldwin int op; 1594483d953aSJohn Baldwin 1595483d953aSJohn Baldwin buffer = &meta->buffer; 1596483d953aSJohn Baldwin op = meta->op; 1597483d953aSJohn Baldwin 1598483d953aSJohn Baldwin if (buffer->buf_rem < data_size) { 1599483d953aSJohn Baldwin fprintf(stderr, "%s: buffer too small\r\n", __func__); 1600483d953aSJohn Baldwin return (E2BIG); 1601483d953aSJohn Baldwin } 1602483d953aSJohn Baldwin 1603483d953aSJohn Baldwin if (op == VM_SNAPSHOT_SAVE) 1604483d953aSJohn Baldwin memcpy(buffer->buf, (uint8_t *) data, data_size); 1605483d953aSJohn Baldwin else if (op == VM_SNAPSHOT_RESTORE) 1606483d953aSJohn Baldwin memcpy((uint8_t *) data, buffer->buf, data_size); 1607483d953aSJohn Baldwin else 1608483d953aSJohn Baldwin return (EINVAL); 1609483d953aSJohn Baldwin 1610483d953aSJohn Baldwin buffer->buf += data_size; 1611483d953aSJohn Baldwin buffer->buf_rem -= data_size; 1612483d953aSJohn Baldwin 1613483d953aSJohn Baldwin return (0); 1614483d953aSJohn Baldwin } 1615483d953aSJohn Baldwin 1616483d953aSJohn Baldwin size_t 1617483d953aSJohn Baldwin vm_get_snapshot_size(struct vm_snapshot_meta *meta) 1618483d953aSJohn Baldwin { 1619483d953aSJohn Baldwin size_t length; 1620483d953aSJohn Baldwin struct vm_snapshot_buffer *buffer; 1621483d953aSJohn Baldwin 1622483d953aSJohn Baldwin buffer = &meta->buffer; 1623483d953aSJohn Baldwin 1624483d953aSJohn Baldwin if (buffer->buf_size < buffer->buf_rem) { 1625483d953aSJohn Baldwin fprintf(stderr, "%s: Invalid buffer: size = %zu, rem = %zu\r\n", 1626483d953aSJohn Baldwin __func__, buffer->buf_size, buffer->buf_rem); 1627483d953aSJohn Baldwin length = 0; 1628483d953aSJohn Baldwin } else { 1629483d953aSJohn Baldwin length = buffer->buf_size - buffer->buf_rem; 1630483d953aSJohn Baldwin } 1631483d953aSJohn Baldwin 1632483d953aSJohn Baldwin return (length); 1633483d953aSJohn Baldwin } 1634483d953aSJohn Baldwin 1635483d953aSJohn Baldwin int 1636483d953aSJohn Baldwin vm_snapshot_guest2host_addr(void **addrp, size_t len, bool restore_null, 1637483d953aSJohn Baldwin struct vm_snapshot_meta *meta) 1638483d953aSJohn Baldwin { 1639483d953aSJohn Baldwin int ret; 1640483d953aSJohn Baldwin vm_paddr_t gaddr; 1641483d953aSJohn Baldwin 1642483d953aSJohn Baldwin if (meta->op == VM_SNAPSHOT_SAVE) { 1643483d953aSJohn Baldwin gaddr = paddr_host2guest(meta->ctx, *addrp); 1644483d953aSJohn Baldwin if (gaddr == (vm_paddr_t) -1) { 1645483d953aSJohn Baldwin if (!restore_null || 1646483d953aSJohn Baldwin (restore_null && (*addrp != NULL))) { 1647483d953aSJohn Baldwin ret = EFAULT; 1648483d953aSJohn Baldwin goto done; 1649483d953aSJohn Baldwin } 1650483d953aSJohn Baldwin } 1651483d953aSJohn Baldwin 1652483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(gaddr, meta, ret, done); 1653483d953aSJohn Baldwin } else if (meta->op == VM_SNAPSHOT_RESTORE) { 1654483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(gaddr, meta, ret, done); 1655483d953aSJohn Baldwin if (gaddr == (vm_paddr_t) -1) { 1656483d953aSJohn Baldwin if (!restore_null) { 1657483d953aSJohn Baldwin ret = EFAULT; 1658483d953aSJohn Baldwin goto done; 1659483d953aSJohn Baldwin } 1660483d953aSJohn Baldwin } 1661483d953aSJohn Baldwin 1662483d953aSJohn Baldwin *addrp = paddr_guest2host(meta->ctx, gaddr, len); 1663483d953aSJohn Baldwin } else { 1664483d953aSJohn Baldwin ret = EINVAL; 1665483d953aSJohn Baldwin } 1666483d953aSJohn Baldwin 1667483d953aSJohn Baldwin done: 1668483d953aSJohn Baldwin return (ret); 1669483d953aSJohn Baldwin } 1670483d953aSJohn Baldwin 1671483d953aSJohn Baldwin int 1672483d953aSJohn Baldwin vm_snapshot_buf_cmp(volatile void *data, size_t data_size, 1673483d953aSJohn Baldwin struct vm_snapshot_meta *meta) 1674483d953aSJohn Baldwin { 1675483d953aSJohn Baldwin struct vm_snapshot_buffer *buffer; 1676483d953aSJohn Baldwin int op; 1677483d953aSJohn Baldwin int ret; 1678483d953aSJohn Baldwin 1679483d953aSJohn Baldwin buffer = &meta->buffer; 1680483d953aSJohn Baldwin op = meta->op; 1681483d953aSJohn Baldwin 1682483d953aSJohn Baldwin if (buffer->buf_rem < data_size) { 1683483d953aSJohn Baldwin fprintf(stderr, "%s: buffer too small\r\n", __func__); 1684483d953aSJohn Baldwin ret = E2BIG; 1685483d953aSJohn Baldwin goto done; 1686483d953aSJohn Baldwin } 1687483d953aSJohn Baldwin 1688483d953aSJohn Baldwin if (op == VM_SNAPSHOT_SAVE) { 1689483d953aSJohn Baldwin ret = 0; 1690483d953aSJohn Baldwin memcpy(buffer->buf, (uint8_t *) data, data_size); 1691483d953aSJohn Baldwin } else if (op == VM_SNAPSHOT_RESTORE) { 1692483d953aSJohn Baldwin ret = memcmp((uint8_t *) data, buffer->buf, data_size); 1693483d953aSJohn Baldwin } else { 1694483d953aSJohn Baldwin ret = EINVAL; 1695483d953aSJohn Baldwin goto done; 1696483d953aSJohn Baldwin } 1697483d953aSJohn Baldwin 1698483d953aSJohn Baldwin buffer->buf += data_size; 1699483d953aSJohn Baldwin buffer->buf_rem -= data_size; 1700483d953aSJohn Baldwin 1701483d953aSJohn Baldwin done: 1702483d953aSJohn Baldwin return (ret); 1703483d953aSJohn Baldwin } 1704