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" 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 122483d953aSJohn Baldwin #define JSON_STRUCT_ARR_KEY "structs" 123483d953aSJohn Baldwin #define JSON_DEV_ARR_KEY "devices" 124483d953aSJohn Baldwin #define JSON_BASIC_METADATA_KEY "basic metadata" 125483d953aSJohn Baldwin #define JSON_SNAPSHOT_REQ_KEY "snapshot_req" 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 141*c9faf698SMark 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 155*c9faf698SMark Johnston static const struct vm_snapshot_kern_info snapshot_kern_structs[] = { 156483d953aSJohn Baldwin { "vhpet", STRUCT_VHPET }, 157483d953aSJohn Baldwin { "vm", STRUCT_VM }, 158483d953aSJohn Baldwin { "vmx", STRUCT_VMX }, 159483d953aSJohn Baldwin { "vioapic", STRUCT_VIOAPIC }, 160483d953aSJohn Baldwin { "vlapic", STRUCT_VLAPIC }, 161483d953aSJohn Baldwin { "vmcx", STRUCT_VMCX }, 162483d953aSJohn Baldwin { "vatpit", STRUCT_VATPIT }, 163483d953aSJohn Baldwin { "vatpic", STRUCT_VATPIC }, 164483d953aSJohn Baldwin { "vpmtmr", STRUCT_VPMTMR }, 165483d953aSJohn Baldwin { "vrtc", STRUCT_VRTC }, 166483d953aSJohn Baldwin }; 167483d953aSJohn Baldwin 168483d953aSJohn Baldwin static cpuset_t vcpus_active, vcpus_suspended; 169483d953aSJohn Baldwin static pthread_mutex_t vcpu_lock; 170483d953aSJohn Baldwin static pthread_cond_t vcpus_idle, vcpus_can_run; 171483d953aSJohn Baldwin static bool checkpoint_active; 172483d953aSJohn Baldwin 173483d953aSJohn Baldwin /* 174483d953aSJohn Baldwin * TODO: Harden this function and all of its callers since 'base_str' is a user 175483d953aSJohn Baldwin * provided string. 176483d953aSJohn Baldwin */ 177483d953aSJohn Baldwin static char * 178483d953aSJohn Baldwin strcat_extension(const char *base_str, const char *ext) 179483d953aSJohn Baldwin { 180483d953aSJohn Baldwin char *res; 181483d953aSJohn Baldwin size_t base_len, ext_len; 182483d953aSJohn Baldwin 183961e6a12SRobert Wing base_len = strnlen(base_str, NAME_MAX); 184961e6a12SRobert Wing ext_len = strnlen(ext, NAME_MAX); 185483d953aSJohn Baldwin 186961e6a12SRobert Wing if (base_len + ext_len > NAME_MAX) { 187483d953aSJohn Baldwin fprintf(stderr, "Filename exceeds maximum length.\n"); 188483d953aSJohn Baldwin return (NULL); 189483d953aSJohn Baldwin } 190483d953aSJohn Baldwin 191483d953aSJohn Baldwin res = malloc(base_len + ext_len + 1); 192483d953aSJohn Baldwin if (res == NULL) { 193483d953aSJohn Baldwin perror("Failed to allocate memory."); 194483d953aSJohn Baldwin return (NULL); 195483d953aSJohn Baldwin } 196483d953aSJohn Baldwin 197483d953aSJohn Baldwin memcpy(res, base_str, base_len); 198483d953aSJohn Baldwin memcpy(res + base_len, ext, ext_len); 199483d953aSJohn Baldwin res[base_len + ext_len] = 0; 200483d953aSJohn Baldwin 201483d953aSJohn Baldwin return (res); 202483d953aSJohn Baldwin } 203483d953aSJohn Baldwin 204483d953aSJohn Baldwin void 205483d953aSJohn Baldwin destroy_restore_state(struct restore_state *rstate) 206483d953aSJohn Baldwin { 207483d953aSJohn Baldwin if (rstate == NULL) { 208483d953aSJohn Baldwin fprintf(stderr, "Attempting to destroy NULL restore struct.\n"); 209483d953aSJohn Baldwin return; 210483d953aSJohn Baldwin } 211483d953aSJohn Baldwin 212483d953aSJohn Baldwin if (rstate->kdata_map != MAP_FAILED) 213483d953aSJohn Baldwin munmap(rstate->kdata_map, rstate->kdata_len); 214483d953aSJohn Baldwin 215483d953aSJohn Baldwin if (rstate->kdata_fd > 0) 216483d953aSJohn Baldwin close(rstate->kdata_fd); 217483d953aSJohn Baldwin if (rstate->vmmem_fd > 0) 218483d953aSJohn Baldwin close(rstate->vmmem_fd); 219483d953aSJohn Baldwin 220483d953aSJohn Baldwin if (rstate->meta_root_obj != NULL) 221483d953aSJohn Baldwin ucl_object_unref(rstate->meta_root_obj); 222483d953aSJohn Baldwin if (rstate->meta_parser != NULL) 223483d953aSJohn Baldwin ucl_parser_free(rstate->meta_parser); 224483d953aSJohn Baldwin } 225483d953aSJohn Baldwin 226483d953aSJohn Baldwin static int 227483d953aSJohn Baldwin load_vmmem_file(const char *filename, struct restore_state *rstate) 228483d953aSJohn Baldwin { 229483d953aSJohn Baldwin struct stat sb; 230483d953aSJohn Baldwin int err; 231483d953aSJohn Baldwin 232483d953aSJohn Baldwin rstate->vmmem_fd = open(filename, O_RDONLY); 233483d953aSJohn Baldwin if (rstate->vmmem_fd < 0) { 234483d953aSJohn Baldwin perror("Failed to open restore file"); 235483d953aSJohn Baldwin return (-1); 236483d953aSJohn Baldwin } 237483d953aSJohn Baldwin 238483d953aSJohn Baldwin err = fstat(rstate->vmmem_fd, &sb); 239483d953aSJohn Baldwin if (err < 0) { 240483d953aSJohn Baldwin perror("Failed to stat restore file"); 241483d953aSJohn Baldwin goto err_load_vmmem; 242483d953aSJohn Baldwin } 243483d953aSJohn Baldwin 244483d953aSJohn Baldwin if (sb.st_size == 0) { 245483d953aSJohn Baldwin fprintf(stderr, "Restore file is empty.\n"); 246483d953aSJohn Baldwin goto err_load_vmmem; 247483d953aSJohn Baldwin } 248483d953aSJohn Baldwin 249483d953aSJohn Baldwin rstate->vmmem_len = sb.st_size; 250483d953aSJohn Baldwin 251483d953aSJohn Baldwin return (0); 252483d953aSJohn Baldwin 253483d953aSJohn Baldwin err_load_vmmem: 254483d953aSJohn Baldwin if (rstate->vmmem_fd > 0) 255483d953aSJohn Baldwin close(rstate->vmmem_fd); 256483d953aSJohn Baldwin return (-1); 257483d953aSJohn Baldwin } 258483d953aSJohn Baldwin 259483d953aSJohn Baldwin static int 260483d953aSJohn Baldwin load_kdata_file(const char *filename, struct restore_state *rstate) 261483d953aSJohn Baldwin { 262483d953aSJohn Baldwin struct stat sb; 263483d953aSJohn Baldwin int err; 264483d953aSJohn Baldwin 265483d953aSJohn Baldwin rstate->kdata_fd = open(filename, O_RDONLY); 266483d953aSJohn Baldwin if (rstate->kdata_fd < 0) { 267483d953aSJohn Baldwin perror("Failed to open kernel data file"); 268483d953aSJohn Baldwin return (-1); 269483d953aSJohn Baldwin } 270483d953aSJohn Baldwin 271483d953aSJohn Baldwin err = fstat(rstate->kdata_fd, &sb); 272483d953aSJohn Baldwin if (err < 0) { 273483d953aSJohn Baldwin perror("Failed to stat kernel data file"); 274483d953aSJohn Baldwin goto err_load_kdata; 275483d953aSJohn Baldwin } 276483d953aSJohn Baldwin 277483d953aSJohn Baldwin if (sb.st_size == 0) { 278483d953aSJohn Baldwin fprintf(stderr, "Kernel data file is empty.\n"); 279483d953aSJohn Baldwin goto err_load_kdata; 280483d953aSJohn Baldwin } 281483d953aSJohn Baldwin 282483d953aSJohn Baldwin rstate->kdata_len = sb.st_size; 283483d953aSJohn Baldwin rstate->kdata_map = mmap(NULL, rstate->kdata_len, PROT_READ, 284483d953aSJohn Baldwin MAP_SHARED, rstate->kdata_fd, 0); 285483d953aSJohn Baldwin if (rstate->kdata_map == MAP_FAILED) { 286483d953aSJohn Baldwin perror("Failed to map restore file"); 287483d953aSJohn Baldwin goto err_load_kdata; 288483d953aSJohn Baldwin } 289483d953aSJohn Baldwin 290483d953aSJohn Baldwin return (0); 291483d953aSJohn Baldwin 292483d953aSJohn Baldwin err_load_kdata: 293483d953aSJohn Baldwin if (rstate->kdata_fd > 0) 294483d953aSJohn Baldwin close(rstate->kdata_fd); 295483d953aSJohn Baldwin return (-1); 296483d953aSJohn Baldwin } 297483d953aSJohn Baldwin 298483d953aSJohn Baldwin static int 299483d953aSJohn Baldwin load_metadata_file(const char *filename, struct restore_state *rstate) 300483d953aSJohn Baldwin { 301*c9faf698SMark Johnston ucl_object_t *obj; 302483d953aSJohn Baldwin struct ucl_parser *parser; 303483d953aSJohn Baldwin int err; 304483d953aSJohn Baldwin 305483d953aSJohn Baldwin parser = ucl_parser_new(UCL_PARSER_DEFAULT); 306483d953aSJohn Baldwin if (parser == NULL) { 307483d953aSJohn Baldwin fprintf(stderr, "Failed to initialize UCL parser.\n"); 308*c9faf698SMark Johnston err = -1; 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 434*c9faf698SMark Johnston if (ucl_object_type(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; 45663898728SMark Johnston return ((uint8_t *)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; 48563898728SMark Johnston return ((uint8_t *)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 506*c9faf698SMark Johnston if (ucl_object_type(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 534*c9faf698SMark Johnston if (ucl_object_type(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 60598d920d9SMark Johnston winch_handler(int signal __unused) 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; 624*c9faf698SMark Johnston static const 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 781*c9faf698SMark Johnston src = (uint8_t *)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 { 916*c9faf698SMark Johnston size_t i; 917483d953aSJohn Baldwin int ret; 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 929*c9faf698SMark Johnston static 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 { 979*c9faf698SMark Johnston size_t i; 980483d953aSJohn Baldwin int ret; 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; 995*c9faf698SMark Johnston size_t i; 996483d953aSJohn Baldwin int ret; 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; 1015*c9faf698SMark Johnston size_t i; 1016483d953aSJohn Baldwin int ret; 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 { 1074*c9faf698SMark Johnston int ret, error; 1075*c9faf698SMark Johnston size_t buf_size, i, offset; 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 1127483d953aSJohn Baldwin xo_open_container_h(xop, JSON_BASIC_METADATA_KEY); 1128483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_NCPUS_KEY "/%ld}\n", guest_ncpus); 11293efc45f3SRobert Wing xo_emit_h(xop, "{:" JSON_VMNAME_KEY "/%s}\n", vm_get_name(ctx)); 1130483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_MEMSIZE_KEY "/%lu}\n", memsz); 11313efc45f3SRobert Wing xo_emit_h(xop, "{:" JSON_MEMFLAGS_KEY "/%d}\n", vm_get_memflags(ctx)); 1132483d953aSJohn Baldwin xo_close_container_h(xop, JSON_BASIC_METADATA_KEY); 1133483d953aSJohn Baldwin 11343efc45f3SRobert Wing return (0); 1135483d953aSJohn Baldwin } 1136483d953aSJohn Baldwin 1137483d953aSJohn Baldwin static int 1138483d953aSJohn Baldwin vm_snapshot_dev_write_data(int data_fd, xo_handle_t *xop, const char *array_key, 1139483d953aSJohn Baldwin struct vm_snapshot_meta *meta, off_t *offset) 1140483d953aSJohn Baldwin { 1141483d953aSJohn Baldwin int ret; 1142483d953aSJohn Baldwin size_t data_size; 1143483d953aSJohn Baldwin 1144483d953aSJohn Baldwin data_size = vm_get_snapshot_size(meta); 1145483d953aSJohn Baldwin 1146483d953aSJohn Baldwin ret = write(data_fd, meta->buffer.buf_start, data_size); 1147483d953aSJohn Baldwin if (ret != data_size) { 1148483d953aSJohn Baldwin perror("Failed to write all snapshotted data."); 1149483d953aSJohn Baldwin return (-1); 1150483d953aSJohn Baldwin } 1151483d953aSJohn Baldwin 1152483d953aSJohn Baldwin /* Write metadata. */ 1153483d953aSJohn Baldwin xo_open_instance_h(xop, array_key); 1154483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_SNAPSHOT_REQ_KEY "/%s}\n", meta->dev_name); 1155483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_SIZE_KEY "/%lu}\n", data_size); 1156483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_FILE_OFFSET_KEY "/%lu}\n", *offset); 1157483d953aSJohn Baldwin xo_close_instance_h(xop, array_key); 1158483d953aSJohn Baldwin 1159483d953aSJohn Baldwin *offset += data_size; 1160483d953aSJohn Baldwin 1161483d953aSJohn Baldwin return (0); 1162483d953aSJohn Baldwin } 1163483d953aSJohn Baldwin 1164483d953aSJohn Baldwin static int 1165483d953aSJohn Baldwin vm_snapshot_user_dev(const struct vm_snapshot_dev_info *info, 1166483d953aSJohn Baldwin int data_fd, xo_handle_t *xop, 1167483d953aSJohn Baldwin struct vm_snapshot_meta *meta, off_t *offset) 1168483d953aSJohn Baldwin { 1169483d953aSJohn Baldwin int ret; 1170483d953aSJohn Baldwin 1171483d953aSJohn Baldwin ret = (*info->snapshot_cb)(meta); 1172483d953aSJohn Baldwin if (ret != 0) { 1173483d953aSJohn Baldwin fprintf(stderr, "Failed to snapshot %s; ret=%d\r\n", 1174483d953aSJohn Baldwin meta->dev_name, ret); 1175483d953aSJohn Baldwin return (ret); 1176483d953aSJohn Baldwin } 1177483d953aSJohn Baldwin 1178483d953aSJohn Baldwin ret = vm_snapshot_dev_write_data(data_fd, xop, JSON_DEV_ARR_KEY, meta, 1179483d953aSJohn Baldwin offset); 1180483d953aSJohn Baldwin if (ret != 0) 1181483d953aSJohn Baldwin return (ret); 1182483d953aSJohn Baldwin 1183483d953aSJohn Baldwin return (0); 1184483d953aSJohn Baldwin } 1185483d953aSJohn Baldwin 1186483d953aSJohn Baldwin static int 1187483d953aSJohn Baldwin vm_snapshot_user_devs(struct vmctx *ctx, int data_fd, xo_handle_t *xop) 1188483d953aSJohn Baldwin { 1189*c9faf698SMark Johnston int ret; 1190483d953aSJohn Baldwin off_t offset; 1191483d953aSJohn Baldwin void *buffer; 1192*c9faf698SMark Johnston size_t buf_size, i; 1193483d953aSJohn Baldwin struct vm_snapshot_meta *meta; 1194483d953aSJohn Baldwin 1195483d953aSJohn Baldwin buf_size = SNAPSHOT_BUFFER_SIZE; 1196483d953aSJohn Baldwin 1197483d953aSJohn Baldwin offset = lseek(data_fd, 0, SEEK_CUR); 1198483d953aSJohn Baldwin if (offset < 0) { 1199483d953aSJohn Baldwin perror("Failed to get data file current offset."); 1200483d953aSJohn Baldwin return (-1); 1201483d953aSJohn Baldwin } 1202483d953aSJohn Baldwin 1203483d953aSJohn Baldwin buffer = malloc(buf_size); 1204483d953aSJohn Baldwin if (buffer == NULL) { 1205483d953aSJohn Baldwin perror("Failed to allocate memory for snapshot buffer"); 1206483d953aSJohn Baldwin ret = ENOSPC; 1207483d953aSJohn Baldwin goto snapshot_err; 1208483d953aSJohn Baldwin } 1209483d953aSJohn Baldwin 1210483d953aSJohn Baldwin meta = &(struct vm_snapshot_meta) { 1211483d953aSJohn Baldwin .ctx = ctx, 1212483d953aSJohn Baldwin 1213483d953aSJohn Baldwin .buffer.buf_start = buffer, 1214483d953aSJohn Baldwin .buffer.buf_size = buf_size, 1215483d953aSJohn Baldwin 1216483d953aSJohn Baldwin .op = VM_SNAPSHOT_SAVE, 1217483d953aSJohn Baldwin }; 1218483d953aSJohn Baldwin 1219483d953aSJohn Baldwin xo_open_list_h(xop, JSON_DEV_ARR_KEY); 1220483d953aSJohn Baldwin 1221483d953aSJohn Baldwin /* Restore other devices that support this feature */ 1222483d953aSJohn Baldwin for (i = 0; i < nitems(snapshot_devs); i++) { 1223483d953aSJohn Baldwin meta->dev_name = snapshot_devs[i].dev_name; 1224483d953aSJohn Baldwin 1225483d953aSJohn Baldwin memset(meta->buffer.buf_start, 0, meta->buffer.buf_size); 1226483d953aSJohn Baldwin meta->buffer.buf = meta->buffer.buf_start; 1227483d953aSJohn Baldwin meta->buffer.buf_rem = meta->buffer.buf_size; 1228483d953aSJohn Baldwin 1229483d953aSJohn Baldwin ret = vm_snapshot_user_dev(&snapshot_devs[i], data_fd, xop, 1230483d953aSJohn Baldwin meta, &offset); 1231483d953aSJohn Baldwin if (ret != 0) 1232483d953aSJohn Baldwin goto snapshot_err; 1233483d953aSJohn Baldwin } 1234483d953aSJohn Baldwin 1235483d953aSJohn Baldwin xo_close_list_h(xop, JSON_DEV_ARR_KEY); 1236483d953aSJohn Baldwin 1237483d953aSJohn Baldwin snapshot_err: 1238483d953aSJohn Baldwin if (buffer != NULL) 1239483d953aSJohn Baldwin free(buffer); 1240483d953aSJohn Baldwin return (ret); 1241483d953aSJohn Baldwin } 1242483d953aSJohn Baldwin 1243483d953aSJohn Baldwin void 1244483d953aSJohn Baldwin checkpoint_cpu_add(int vcpu) 1245483d953aSJohn Baldwin { 1246483d953aSJohn Baldwin 1247483d953aSJohn Baldwin pthread_mutex_lock(&vcpu_lock); 1248483d953aSJohn Baldwin CPU_SET(vcpu, &vcpus_active); 1249483d953aSJohn Baldwin 1250483d953aSJohn Baldwin if (checkpoint_active) { 1251483d953aSJohn Baldwin CPU_SET(vcpu, &vcpus_suspended); 1252483d953aSJohn Baldwin while (checkpoint_active) 1253483d953aSJohn Baldwin pthread_cond_wait(&vcpus_can_run, &vcpu_lock); 1254483d953aSJohn Baldwin CPU_CLR(vcpu, &vcpus_suspended); 1255483d953aSJohn Baldwin } 1256483d953aSJohn Baldwin pthread_mutex_unlock(&vcpu_lock); 1257483d953aSJohn Baldwin } 1258483d953aSJohn Baldwin 1259483d953aSJohn Baldwin /* 1260483d953aSJohn Baldwin * When a vCPU is suspended for any reason, it calls 1261483d953aSJohn Baldwin * checkpoint_cpu_suspend(). This records that the vCPU is idle. 1262483d953aSJohn Baldwin * Before returning from suspension, checkpoint_cpu_resume() is 1263483d953aSJohn Baldwin * called. In suspend we note that the vCPU is idle. In resume we 1264483d953aSJohn Baldwin * pause the vCPU thread until the checkpoint is complete. The reason 1265483d953aSJohn Baldwin * for the two-step process is that vCPUs might already be stopped in 1266483d953aSJohn Baldwin * the debug server when a checkpoint is requested. This approach 1267483d953aSJohn Baldwin * allows us to account for and handle those vCPUs. 1268483d953aSJohn Baldwin */ 1269483d953aSJohn Baldwin void 1270483d953aSJohn Baldwin checkpoint_cpu_suspend(int vcpu) 1271483d953aSJohn Baldwin { 1272483d953aSJohn Baldwin 1273483d953aSJohn Baldwin pthread_mutex_lock(&vcpu_lock); 1274483d953aSJohn Baldwin CPU_SET(vcpu, &vcpus_suspended); 1275483d953aSJohn Baldwin if (checkpoint_active && CPU_CMP(&vcpus_active, &vcpus_suspended) == 0) 1276483d953aSJohn Baldwin pthread_cond_signal(&vcpus_idle); 1277483d953aSJohn Baldwin pthread_mutex_unlock(&vcpu_lock); 1278483d953aSJohn Baldwin } 1279483d953aSJohn Baldwin 1280483d953aSJohn Baldwin void 1281483d953aSJohn Baldwin checkpoint_cpu_resume(int vcpu) 1282483d953aSJohn Baldwin { 1283483d953aSJohn Baldwin 1284483d953aSJohn Baldwin pthread_mutex_lock(&vcpu_lock); 1285483d953aSJohn Baldwin while (checkpoint_active) 1286483d953aSJohn Baldwin pthread_cond_wait(&vcpus_can_run, &vcpu_lock); 1287483d953aSJohn Baldwin CPU_CLR(vcpu, &vcpus_suspended); 1288483d953aSJohn Baldwin pthread_mutex_unlock(&vcpu_lock); 1289483d953aSJohn Baldwin } 1290483d953aSJohn Baldwin 1291483d953aSJohn Baldwin static void 1292483d953aSJohn Baldwin vm_vcpu_pause(struct vmctx *ctx) 1293483d953aSJohn Baldwin { 1294483d953aSJohn Baldwin 1295483d953aSJohn Baldwin pthread_mutex_lock(&vcpu_lock); 1296483d953aSJohn Baldwin checkpoint_active = true; 1297483d953aSJohn Baldwin vm_suspend_cpu(ctx, -1); 1298483d953aSJohn Baldwin while (CPU_CMP(&vcpus_active, &vcpus_suspended) != 0) 1299483d953aSJohn Baldwin pthread_cond_wait(&vcpus_idle, &vcpu_lock); 1300483d953aSJohn Baldwin pthread_mutex_unlock(&vcpu_lock); 1301483d953aSJohn Baldwin } 1302483d953aSJohn Baldwin 1303483d953aSJohn Baldwin static void 1304483d953aSJohn Baldwin vm_vcpu_resume(struct vmctx *ctx) 1305483d953aSJohn Baldwin { 1306483d953aSJohn Baldwin 1307483d953aSJohn Baldwin pthread_mutex_lock(&vcpu_lock); 1308483d953aSJohn Baldwin checkpoint_active = false; 1309483d953aSJohn Baldwin pthread_mutex_unlock(&vcpu_lock); 1310483d953aSJohn Baldwin vm_resume_cpu(ctx, -1); 1311483d953aSJohn Baldwin pthread_cond_broadcast(&vcpus_can_run); 1312483d953aSJohn Baldwin } 1313483d953aSJohn Baldwin 1314483d953aSJohn Baldwin static int 1315edfb339dSRobert Wing vm_checkpoint(struct vmctx *ctx, const char *checkpoint_file, bool stop_vm) 1316483d953aSJohn Baldwin { 1317483d953aSJohn Baldwin int fd_checkpoint = 0, kdata_fd = 0; 1318483d953aSJohn Baldwin int ret = 0; 1319483d953aSJohn Baldwin int error = 0; 1320483d953aSJohn Baldwin size_t memsz; 1321483d953aSJohn Baldwin xo_handle_t *xop = NULL; 1322483d953aSJohn Baldwin char *meta_filename = NULL; 1323483d953aSJohn Baldwin char *kdata_filename = NULL; 1324483d953aSJohn Baldwin FILE *meta_file = NULL; 1325483d953aSJohn Baldwin 1326483d953aSJohn Baldwin kdata_filename = strcat_extension(checkpoint_file, ".kern"); 1327483d953aSJohn Baldwin if (kdata_filename == NULL) { 1328483d953aSJohn Baldwin fprintf(stderr, "Failed to construct kernel data filename.\n"); 1329483d953aSJohn Baldwin return (-1); 1330483d953aSJohn Baldwin } 1331483d953aSJohn Baldwin 1332483d953aSJohn Baldwin kdata_fd = open(kdata_filename, O_WRONLY | O_CREAT | O_TRUNC, 0700); 1333483d953aSJohn Baldwin if (kdata_fd < 0) { 1334483d953aSJohn Baldwin perror("Failed to open kernel data snapshot file."); 1335483d953aSJohn Baldwin error = -1; 1336483d953aSJohn Baldwin goto done; 1337483d953aSJohn Baldwin } 1338483d953aSJohn Baldwin 1339483d953aSJohn Baldwin fd_checkpoint = open(checkpoint_file, O_RDWR | O_CREAT | O_TRUNC, 0700); 1340483d953aSJohn Baldwin 1341483d953aSJohn Baldwin if (fd_checkpoint < 0) { 1342483d953aSJohn Baldwin perror("Failed to create checkpoint file"); 1343483d953aSJohn Baldwin error = -1; 1344483d953aSJohn Baldwin goto done; 1345483d953aSJohn Baldwin } 1346483d953aSJohn Baldwin 1347483d953aSJohn Baldwin meta_filename = strcat_extension(checkpoint_file, ".meta"); 1348483d953aSJohn Baldwin if (meta_filename == NULL) { 1349483d953aSJohn Baldwin fprintf(stderr, "Failed to construct vm metadata filename.\n"); 1350483d953aSJohn Baldwin goto done; 1351483d953aSJohn Baldwin } 1352483d953aSJohn Baldwin 1353483d953aSJohn Baldwin meta_file = fopen(meta_filename, "w"); 1354483d953aSJohn Baldwin if (meta_file == NULL) { 1355483d953aSJohn Baldwin perror("Failed to open vm metadata snapshot file."); 1356483d953aSJohn Baldwin goto done; 1357483d953aSJohn Baldwin } 1358483d953aSJohn Baldwin 1359483d953aSJohn Baldwin xop = xo_create_to_file(meta_file, XO_STYLE_JSON, XOF_PRETTY); 1360483d953aSJohn Baldwin if (xop == NULL) { 1361483d953aSJohn Baldwin perror("Failed to get libxo handle on metadata file."); 1362483d953aSJohn Baldwin goto done; 1363483d953aSJohn Baldwin } 1364483d953aSJohn Baldwin 1365483d953aSJohn Baldwin vm_vcpu_pause(ctx); 1366483d953aSJohn Baldwin 1367483d953aSJohn Baldwin ret = vm_pause_user_devs(ctx); 1368483d953aSJohn Baldwin if (ret != 0) { 1369483d953aSJohn Baldwin fprintf(stderr, "Could not pause devices\r\n"); 1370483d953aSJohn Baldwin error = ret; 1371483d953aSJohn Baldwin goto done; 1372483d953aSJohn Baldwin } 1373483d953aSJohn Baldwin 1374483d953aSJohn Baldwin memsz = vm_snapshot_mem(ctx, fd_checkpoint, 0, true); 1375483d953aSJohn Baldwin if (memsz == 0) { 1376483d953aSJohn Baldwin perror("Could not write guest memory to file"); 1377483d953aSJohn Baldwin error = -1; 1378483d953aSJohn Baldwin goto done; 1379483d953aSJohn Baldwin } 1380483d953aSJohn Baldwin 1381483d953aSJohn Baldwin ret = vm_snapshot_basic_metadata(ctx, xop, memsz); 1382483d953aSJohn Baldwin if (ret != 0) { 1383483d953aSJohn Baldwin fprintf(stderr, "Failed to snapshot vm basic metadata.\n"); 1384483d953aSJohn Baldwin error = -1; 1385483d953aSJohn Baldwin goto done; 1386483d953aSJohn Baldwin } 1387483d953aSJohn Baldwin 1388483d953aSJohn Baldwin 1389483d953aSJohn Baldwin ret = vm_snapshot_kern_structs(ctx, kdata_fd, xop); 1390483d953aSJohn Baldwin if (ret != 0) { 1391483d953aSJohn Baldwin fprintf(stderr, "Failed to snapshot vm kernel data.\n"); 1392483d953aSJohn Baldwin error = -1; 1393483d953aSJohn Baldwin goto done; 1394483d953aSJohn Baldwin } 1395483d953aSJohn Baldwin 1396483d953aSJohn Baldwin ret = vm_snapshot_user_devs(ctx, kdata_fd, xop); 1397483d953aSJohn Baldwin if (ret != 0) { 1398483d953aSJohn Baldwin fprintf(stderr, "Failed to snapshot device state.\n"); 1399483d953aSJohn Baldwin error = -1; 1400483d953aSJohn Baldwin goto done; 1401483d953aSJohn Baldwin } 1402483d953aSJohn Baldwin 1403483d953aSJohn Baldwin xo_finish_h(xop); 1404483d953aSJohn Baldwin 1405483d953aSJohn Baldwin if (stop_vm) { 1406483d953aSJohn Baldwin vm_destroy(ctx); 1407483d953aSJohn Baldwin exit(0); 1408483d953aSJohn Baldwin } 1409483d953aSJohn Baldwin 1410483d953aSJohn Baldwin done: 1411483d953aSJohn Baldwin ret = vm_resume_user_devs(ctx); 1412483d953aSJohn Baldwin if (ret != 0) 1413483d953aSJohn Baldwin fprintf(stderr, "Could not resume devices\r\n"); 1414483d953aSJohn Baldwin vm_vcpu_resume(ctx); 1415483d953aSJohn Baldwin if (fd_checkpoint > 0) 1416483d953aSJohn Baldwin close(fd_checkpoint); 1417483d953aSJohn Baldwin if (meta_filename != NULL) 1418483d953aSJohn Baldwin free(meta_filename); 1419483d953aSJohn Baldwin if (kdata_filename != NULL) 1420483d953aSJohn Baldwin free(kdata_filename); 1421483d953aSJohn Baldwin if (xop != NULL) 1422483d953aSJohn Baldwin xo_destroy(xop); 1423483d953aSJohn Baldwin if (meta_file != NULL) 1424483d953aSJohn Baldwin fclose(meta_file); 1425483d953aSJohn Baldwin if (kdata_fd > 0) 1426483d953aSJohn Baldwin close(kdata_fd); 1427483d953aSJohn Baldwin return (error); 1428483d953aSJohn Baldwin } 1429483d953aSJohn Baldwin 1430edfb339dSRobert Wing static int 1431edfb339dSRobert Wing handle_message(struct vmctx *ctx, nvlist_t *nvl) 1432483d953aSJohn Baldwin { 14334379c1daSRobert Wing const char *cmd; 1434c79331a4SRobert Wing struct ipc_command **ipc_cmd; 1435483d953aSJohn Baldwin 14364379c1daSRobert Wing if (!nvlist_exists_string(nvl, "cmd")) 1437c79331a4SRobert Wing return (EINVAL); 1438edfb339dSRobert Wing 14394379c1daSRobert Wing cmd = nvlist_get_string(nvl, "cmd"); 1440c79331a4SRobert Wing IPC_COMMAND_FOREACH(ipc_cmd, ipc_cmd_set) { 1441c79331a4SRobert Wing if (strcmp(cmd, (*ipc_cmd)->name) == 0) 1442c79331a4SRobert Wing return ((*ipc_cmd)->handler(ctx, nvl)); 1443483d953aSJohn Baldwin } 1444483d953aSJohn Baldwin 1445c79331a4SRobert Wing return (EOPNOTSUPP); 1446483d953aSJohn Baldwin } 1447483d953aSJohn Baldwin 1448483d953aSJohn Baldwin /* 1449483d953aSJohn Baldwin * Listen for commands from bhyvectl 1450483d953aSJohn Baldwin */ 1451483d953aSJohn Baldwin void * 1452483d953aSJohn Baldwin checkpoint_thread(void *param) 1453483d953aSJohn Baldwin { 1454690b7ea0SRobert Wing int fd; 1455483d953aSJohn Baldwin struct checkpoint_thread_info *thread_info; 1456edfb339dSRobert Wing nvlist_t *nvl; 1457483d953aSJohn Baldwin 1458483d953aSJohn Baldwin pthread_set_name_np(pthread_self(), "checkpoint thread"); 1459483d953aSJohn Baldwin thread_info = (struct checkpoint_thread_info *)param; 1460483d953aSJohn Baldwin 1461690b7ea0SRobert Wing while ((fd = accept(thread_info->socket_fd, NULL, NULL)) != -1) { 1462690b7ea0SRobert Wing nvl = nvlist_recv(fd, 0); 1463edfb339dSRobert Wing if (nvl != NULL) 1464edfb339dSRobert Wing handle_message(thread_info->ctx, nvl); 146538dfb062SRobert Wing else 1466edfb339dSRobert Wing EPRINTLN("nvlist_recv() failed: %s", strerror(errno)); 1467c79331a4SRobert Wing 1468690b7ea0SRobert Wing close(fd); 1469c79331a4SRobert Wing nvlist_destroy(nvl); 1470483d953aSJohn Baldwin } 1471483d953aSJohn Baldwin 1472483d953aSJohn Baldwin return (NULL); 1473483d953aSJohn Baldwin } 1474483d953aSJohn Baldwin 1475c79331a4SRobert Wing static int 1476c79331a4SRobert Wing vm_do_checkpoint(struct vmctx *ctx, const nvlist_t *nvl) 1477c79331a4SRobert Wing { 1478c79331a4SRobert Wing int error; 1479c79331a4SRobert Wing 1480c79331a4SRobert Wing if (!nvlist_exists_string(nvl, "filename") || 1481c79331a4SRobert Wing !nvlist_exists_bool(nvl, "suspend")) 1482c79331a4SRobert Wing error = EINVAL; 1483c79331a4SRobert Wing else 1484c79331a4SRobert Wing error = vm_checkpoint(ctx, nvlist_get_string(nvl, "filename"), 1485c79331a4SRobert Wing nvlist_get_bool(nvl, "suspend")); 1486c79331a4SRobert Wing 1487c79331a4SRobert Wing return (error); 1488c79331a4SRobert Wing } 1489c79331a4SRobert Wing IPC_COMMAND(ipc_cmd_set, checkpoint, vm_do_checkpoint); 1490c79331a4SRobert Wing 1491fdbc86cfSRobert Wing void 1492fdbc86cfSRobert Wing init_snapshot(void) 1493fdbc86cfSRobert Wing { 1494fdbc86cfSRobert Wing int err; 1495fdbc86cfSRobert Wing 1496fdbc86cfSRobert Wing err = pthread_mutex_init(&vcpu_lock, NULL); 1497fdbc86cfSRobert Wing if (err != 0) 1498fdbc86cfSRobert Wing errc(1, err, "checkpoint mutex init"); 1499fdbc86cfSRobert Wing err = pthread_cond_init(&vcpus_idle, NULL); 1500fdbc86cfSRobert Wing if (err != 0) 1501fdbc86cfSRobert Wing errc(1, err, "checkpoint cv init (vcpus_idle)"); 1502fdbc86cfSRobert Wing err = pthread_cond_init(&vcpus_can_run, NULL); 1503fdbc86cfSRobert Wing if (err != 0) 1504fdbc86cfSRobert Wing errc(1, err, "checkpoint cv init (vcpus_can_run)"); 1505fdbc86cfSRobert Wing } 1506fdbc86cfSRobert Wing 1507483d953aSJohn Baldwin /* 1508483d953aSJohn Baldwin * Create the listening socket for IPC with bhyvectl 1509483d953aSJohn Baldwin */ 1510483d953aSJohn Baldwin int 1511483d953aSJohn Baldwin init_checkpoint_thread(struct vmctx *ctx) 1512483d953aSJohn Baldwin { 1513483d953aSJohn Baldwin struct checkpoint_thread_info *checkpoint_info = NULL; 1514483d953aSJohn Baldwin struct sockaddr_un addr; 1515483d953aSJohn Baldwin int socket_fd; 1516483d953aSJohn Baldwin pthread_t checkpoint_pthread; 151751fbd894SRobert Wing int err; 1518483d953aSJohn Baldwin 1519483d953aSJohn Baldwin memset(&addr, 0, sizeof(addr)); 1520483d953aSJohn Baldwin 1521690b7ea0SRobert Wing socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); 1522483d953aSJohn Baldwin if (socket_fd < 0) { 152338dfb062SRobert Wing EPRINTLN("Socket creation failed: %s", strerror(errno)); 1524483d953aSJohn Baldwin err = -1; 1525483d953aSJohn Baldwin goto fail; 1526483d953aSJohn Baldwin } 1527483d953aSJohn Baldwin 1528483d953aSJohn Baldwin addr.sun_family = AF_UNIX; 1529483d953aSJohn Baldwin 15305ce2d4a1SRobert Wing snprintf(addr.sun_path, sizeof(addr.sun_path), "%s%s", 15313efc45f3SRobert Wing BHYVE_RUN_DIR, vm_get_name(ctx)); 1532483d953aSJohn Baldwin addr.sun_len = SUN_LEN(&addr); 1533483d953aSJohn Baldwin unlink(addr.sun_path); 1534483d953aSJohn Baldwin 1535483d953aSJohn Baldwin if (bind(socket_fd, (struct sockaddr *)&addr, addr.sun_len) != 0) { 153638dfb062SRobert Wing EPRINTLN("Failed to bind socket \"%s\": %s\n", 153738dfb062SRobert Wing addr.sun_path, strerror(errno)); 1538483d953aSJohn Baldwin err = -1; 1539483d953aSJohn Baldwin goto fail; 1540483d953aSJohn Baldwin } 1541483d953aSJohn Baldwin 1542690b7ea0SRobert Wing if (listen(socket_fd, 10) < 0) { 1543690b7ea0SRobert Wing EPRINTLN("ipc socket listen: %s\n", strerror(errno)); 1544690b7ea0SRobert Wing err = errno; 1545690b7ea0SRobert Wing goto fail; 1546690b7ea0SRobert Wing } 1547690b7ea0SRobert Wing 1548483d953aSJohn Baldwin checkpoint_info = calloc(1, sizeof(*checkpoint_info)); 1549483d953aSJohn Baldwin checkpoint_info->ctx = ctx; 1550483d953aSJohn Baldwin checkpoint_info->socket_fd = socket_fd; 1551483d953aSJohn Baldwin 155251fbd894SRobert Wing err = pthread_create(&checkpoint_pthread, NULL, checkpoint_thread, 1553483d953aSJohn Baldwin checkpoint_info); 155451fbd894SRobert Wing if (err != 0) 1555483d953aSJohn Baldwin goto fail; 1556483d953aSJohn Baldwin 1557483d953aSJohn Baldwin return (0); 1558483d953aSJohn Baldwin fail: 1559483d953aSJohn Baldwin free(checkpoint_info); 1560483d953aSJohn Baldwin if (socket_fd > 0) 1561483d953aSJohn Baldwin close(socket_fd); 1562483d953aSJohn Baldwin unlink(addr.sun_path); 1563483d953aSJohn Baldwin 1564483d953aSJohn Baldwin return (err); 1565483d953aSJohn Baldwin } 1566483d953aSJohn Baldwin 1567483d953aSJohn Baldwin void 1568483d953aSJohn Baldwin vm_snapshot_buf_err(const char *bufname, const enum vm_snapshot_op op) 1569483d953aSJohn Baldwin { 1570483d953aSJohn Baldwin const char *__op; 1571483d953aSJohn Baldwin 1572483d953aSJohn Baldwin if (op == VM_SNAPSHOT_SAVE) 1573483d953aSJohn Baldwin __op = "save"; 1574483d953aSJohn Baldwin else if (op == VM_SNAPSHOT_RESTORE) 1575483d953aSJohn Baldwin __op = "restore"; 1576483d953aSJohn Baldwin else 1577483d953aSJohn Baldwin __op = "unknown"; 1578483d953aSJohn Baldwin 1579483d953aSJohn Baldwin fprintf(stderr, "%s: snapshot-%s failed for %s\r\n", 1580483d953aSJohn Baldwin __func__, __op, bufname); 1581483d953aSJohn Baldwin } 1582483d953aSJohn Baldwin 1583483d953aSJohn Baldwin int 1584483d953aSJohn Baldwin vm_snapshot_buf(volatile void *data, size_t data_size, 1585483d953aSJohn Baldwin struct vm_snapshot_meta *meta) 1586483d953aSJohn Baldwin { 1587483d953aSJohn Baldwin struct vm_snapshot_buffer *buffer; 1588483d953aSJohn Baldwin int op; 1589483d953aSJohn Baldwin 1590483d953aSJohn Baldwin buffer = &meta->buffer; 1591483d953aSJohn Baldwin op = meta->op; 1592483d953aSJohn Baldwin 1593483d953aSJohn Baldwin if (buffer->buf_rem < data_size) { 1594483d953aSJohn Baldwin fprintf(stderr, "%s: buffer too small\r\n", __func__); 1595483d953aSJohn Baldwin return (E2BIG); 1596483d953aSJohn Baldwin } 1597483d953aSJohn Baldwin 1598483d953aSJohn Baldwin if (op == VM_SNAPSHOT_SAVE) 1599483d953aSJohn Baldwin memcpy(buffer->buf, (uint8_t *) data, data_size); 1600483d953aSJohn Baldwin else if (op == VM_SNAPSHOT_RESTORE) 1601483d953aSJohn Baldwin memcpy((uint8_t *) data, buffer->buf, data_size); 1602483d953aSJohn Baldwin else 1603483d953aSJohn Baldwin return (EINVAL); 1604483d953aSJohn Baldwin 1605483d953aSJohn Baldwin buffer->buf += data_size; 1606483d953aSJohn Baldwin buffer->buf_rem -= data_size; 1607483d953aSJohn Baldwin 1608483d953aSJohn Baldwin return (0); 1609483d953aSJohn Baldwin } 1610483d953aSJohn Baldwin 1611483d953aSJohn Baldwin size_t 1612483d953aSJohn Baldwin vm_get_snapshot_size(struct vm_snapshot_meta *meta) 1613483d953aSJohn Baldwin { 1614483d953aSJohn Baldwin size_t length; 1615483d953aSJohn Baldwin struct vm_snapshot_buffer *buffer; 1616483d953aSJohn Baldwin 1617483d953aSJohn Baldwin buffer = &meta->buffer; 1618483d953aSJohn Baldwin 1619483d953aSJohn Baldwin if (buffer->buf_size < buffer->buf_rem) { 1620483d953aSJohn Baldwin fprintf(stderr, "%s: Invalid buffer: size = %zu, rem = %zu\r\n", 1621483d953aSJohn Baldwin __func__, buffer->buf_size, buffer->buf_rem); 1622483d953aSJohn Baldwin length = 0; 1623483d953aSJohn Baldwin } else { 1624483d953aSJohn Baldwin length = buffer->buf_size - buffer->buf_rem; 1625483d953aSJohn Baldwin } 1626483d953aSJohn Baldwin 1627483d953aSJohn Baldwin return (length); 1628483d953aSJohn Baldwin } 1629483d953aSJohn Baldwin 1630483d953aSJohn Baldwin int 1631483d953aSJohn Baldwin vm_snapshot_guest2host_addr(void **addrp, size_t len, bool restore_null, 1632483d953aSJohn Baldwin struct vm_snapshot_meta *meta) 1633483d953aSJohn Baldwin { 1634483d953aSJohn Baldwin int ret; 1635483d953aSJohn Baldwin vm_paddr_t gaddr; 1636483d953aSJohn Baldwin 1637483d953aSJohn Baldwin if (meta->op == VM_SNAPSHOT_SAVE) { 1638483d953aSJohn Baldwin gaddr = paddr_host2guest(meta->ctx, *addrp); 1639483d953aSJohn Baldwin if (gaddr == (vm_paddr_t) -1) { 1640483d953aSJohn Baldwin if (!restore_null || 1641483d953aSJohn Baldwin (restore_null && (*addrp != NULL))) { 1642483d953aSJohn Baldwin ret = EFAULT; 1643483d953aSJohn Baldwin goto done; 1644483d953aSJohn Baldwin } 1645483d953aSJohn Baldwin } 1646483d953aSJohn Baldwin 1647483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(gaddr, meta, ret, done); 1648483d953aSJohn Baldwin } else if (meta->op == VM_SNAPSHOT_RESTORE) { 1649483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(gaddr, meta, ret, done); 1650483d953aSJohn Baldwin if (gaddr == (vm_paddr_t) -1) { 1651483d953aSJohn Baldwin if (!restore_null) { 1652483d953aSJohn Baldwin ret = EFAULT; 1653483d953aSJohn Baldwin goto done; 1654483d953aSJohn Baldwin } 1655483d953aSJohn Baldwin } 1656483d953aSJohn Baldwin 1657483d953aSJohn Baldwin *addrp = paddr_guest2host(meta->ctx, gaddr, len); 1658483d953aSJohn Baldwin } else { 1659483d953aSJohn Baldwin ret = EINVAL; 1660483d953aSJohn Baldwin } 1661483d953aSJohn Baldwin 1662483d953aSJohn Baldwin done: 1663483d953aSJohn Baldwin return (ret); 1664483d953aSJohn Baldwin } 1665483d953aSJohn Baldwin 1666483d953aSJohn Baldwin int 1667483d953aSJohn Baldwin vm_snapshot_buf_cmp(volatile void *data, size_t data_size, 1668483d953aSJohn Baldwin struct vm_snapshot_meta *meta) 1669483d953aSJohn Baldwin { 1670483d953aSJohn Baldwin struct vm_snapshot_buffer *buffer; 1671483d953aSJohn Baldwin int op; 1672483d953aSJohn Baldwin int ret; 1673483d953aSJohn Baldwin 1674483d953aSJohn Baldwin buffer = &meta->buffer; 1675483d953aSJohn Baldwin op = meta->op; 1676483d953aSJohn Baldwin 1677483d953aSJohn Baldwin if (buffer->buf_rem < data_size) { 1678483d953aSJohn Baldwin fprintf(stderr, "%s: buffer too small\r\n", __func__); 1679483d953aSJohn Baldwin ret = E2BIG; 1680483d953aSJohn Baldwin goto done; 1681483d953aSJohn Baldwin } 1682483d953aSJohn Baldwin 1683483d953aSJohn Baldwin if (op == VM_SNAPSHOT_SAVE) { 1684483d953aSJohn Baldwin ret = 0; 1685483d953aSJohn Baldwin memcpy(buffer->buf, (uint8_t *) data, data_size); 1686483d953aSJohn Baldwin } else if (op == VM_SNAPSHOT_RESTORE) { 1687483d953aSJohn Baldwin ret = memcmp((uint8_t *) data, buffer->buf, data_size); 1688483d953aSJohn Baldwin } else { 1689483d953aSJohn Baldwin ret = EINVAL; 1690483d953aSJohn Baldwin goto done; 1691483d953aSJohn Baldwin } 1692483d953aSJohn Baldwin 1693483d953aSJohn Baldwin buffer->buf += data_size; 1694483d953aSJohn Baldwin buffer->buf_rem -= data_size; 1695483d953aSJohn Baldwin 1696483d953aSJohn Baldwin done: 1697483d953aSJohn Baldwin return (ret); 1698483d953aSJohn Baldwin } 1699