1483d953aSJohn Baldwin /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3483d953aSJohn Baldwin * 4483d953aSJohn Baldwin * Copyright (c) 2016 Flavius Anton 5483d953aSJohn Baldwin * Copyright (c) 2016 Mihai Tiganus 6483d953aSJohn Baldwin * Copyright (c) 2016-2019 Mihai Carabas 7483d953aSJohn Baldwin * Copyright (c) 2017-2019 Darius Mihai 8483d953aSJohn Baldwin * Copyright (c) 2017-2019 Elena Mihailescu 9483d953aSJohn Baldwin * Copyright (c) 2018-2019 Sergiu Weisz 10483d953aSJohn Baldwin * All rights reserved. 11483d953aSJohn Baldwin * The bhyve-snapshot feature was developed under sponsorships 12483d953aSJohn Baldwin * from Matthew Grooms. 13483d953aSJohn Baldwin * 14483d953aSJohn Baldwin * Redistribution and use in source and binary forms, with or without 15483d953aSJohn Baldwin * modification, are permitted provided that the following conditions 16483d953aSJohn Baldwin * are met: 17483d953aSJohn Baldwin * 1. Redistributions of source code must retain the above copyright 18483d953aSJohn Baldwin * notice, this list of conditions and the following disclaimer. 19483d953aSJohn Baldwin * 2. Redistributions in binary form must reproduce the above copyright 20483d953aSJohn Baldwin * notice, this list of conditions and the following disclaimer in the 21483d953aSJohn Baldwin * documentation and/or other materials provided with the distribution. 22483d953aSJohn Baldwin * 23483d953aSJohn Baldwin * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 24483d953aSJohn Baldwin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25483d953aSJohn Baldwin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26483d953aSJohn Baldwin * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 27483d953aSJohn Baldwin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28483d953aSJohn Baldwin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29483d953aSJohn Baldwin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30483d953aSJohn Baldwin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31483d953aSJohn Baldwin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32483d953aSJohn Baldwin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33483d953aSJohn Baldwin * SUCH DAMAGE. 34483d953aSJohn Baldwin */ 35483d953aSJohn Baldwin 36483d953aSJohn Baldwin #include <sys/types.h> 37483d953aSJohn Baldwin #ifndef WITHOUT_CAPSICUM 38483d953aSJohn Baldwin #include <sys/capsicum.h> 39483d953aSJohn Baldwin #endif 40483d953aSJohn Baldwin #include <sys/mman.h> 41483d953aSJohn Baldwin #include <sys/socket.h> 42483d953aSJohn Baldwin #include <sys/stat.h> 43483d953aSJohn Baldwin #include <sys/time.h> 44483d953aSJohn Baldwin #include <sys/un.h> 45483d953aSJohn Baldwin 46483d953aSJohn Baldwin #ifndef WITHOUT_CAPSICUM 47483d953aSJohn Baldwin #include <capsicum_helpers.h> 48483d953aSJohn Baldwin #endif 49483d953aSJohn Baldwin #include <stdio.h> 50483d953aSJohn Baldwin #include <stdlib.h> 51483d953aSJohn Baldwin #include <string.h> 52483d953aSJohn Baldwin #include <err.h> 53483d953aSJohn Baldwin #include <errno.h> 54483d953aSJohn Baldwin #include <fcntl.h> 55483d953aSJohn Baldwin #include <libgen.h> 56483d953aSJohn Baldwin #include <signal.h> 57483d953aSJohn Baldwin #include <unistd.h> 58483d953aSJohn Baldwin #include <assert.h> 59483d953aSJohn Baldwin #include <errno.h> 60483d953aSJohn Baldwin #include <pthread.h> 61483d953aSJohn Baldwin #include <pthread_np.h> 62483d953aSJohn Baldwin #include <sysexits.h> 63483d953aSJohn Baldwin #include <stdbool.h> 64483d953aSJohn Baldwin #include <sys/ioctl.h> 65483d953aSJohn Baldwin 66483d953aSJohn Baldwin #include <machine/vmm.h> 67483d953aSJohn Baldwin #ifndef WITHOUT_CAPSICUM 68483d953aSJohn Baldwin #include <machine/vmm_dev.h> 69483d953aSJohn Baldwin #endif 70483d953aSJohn Baldwin #include <machine/vmm_snapshot.h> 71483d953aSJohn Baldwin #include <vmmapi.h> 72483d953aSJohn Baldwin 73483d953aSJohn Baldwin #include "bhyverun.h" 74483d953aSJohn Baldwin #include "acpi.h" 75f927afc1SMark Johnston #ifdef __amd64__ 76f927afc1SMark Johnston #include "amd64/atkbdc.h" 77f927afc1SMark Johnston #endif 7838dfb062SRobert Wing #include "debug.h" 79c79331a4SRobert Wing #include "ipc.h" 80483d953aSJohn Baldwin #include "mem.h" 81483d953aSJohn Baldwin #include "pci_emul.h" 82483d953aSJohn Baldwin #include "snapshot.h" 83483d953aSJohn Baldwin 84483d953aSJohn Baldwin #include <libxo/xo.h> 85483d953aSJohn Baldwin #include <ucl.h> 86483d953aSJohn Baldwin 87483d953aSJohn Baldwin struct spinner_info { 88483d953aSJohn Baldwin const size_t *crtval; 89483d953aSJohn Baldwin const size_t maxval; 90483d953aSJohn Baldwin const size_t total; 91483d953aSJohn Baldwin }; 92483d953aSJohn Baldwin 93483d953aSJohn Baldwin extern int guest_ncpus; 94483d953aSJohn Baldwin 95483d953aSJohn Baldwin static struct winsize winsize; 96483d953aSJohn Baldwin static sig_t old_winch_handler; 97483d953aSJohn Baldwin 98483d953aSJohn Baldwin #define KB (1024UL) 99483d953aSJohn Baldwin #define MB (1024UL * KB) 100483d953aSJohn Baldwin #define GB (1024UL * MB) 101483d953aSJohn Baldwin 102483d953aSJohn Baldwin #define SNAPSHOT_CHUNK (4 * MB) 103483d953aSJohn Baldwin #define PROG_BUF_SZ (8192) 104483d953aSJohn Baldwin 105b831c773SVitaliy Gusev #define SNAPSHOT_BUFFER_SIZE (40 * MB) 106483d953aSJohn Baldwin 10750aef9f6SVitaliy Gusev #define JSON_KERNEL_ARR_KEY "kern_structs" 108483d953aSJohn Baldwin #define JSON_DEV_ARR_KEY "devices" 109483d953aSJohn Baldwin #define JSON_BASIC_METADATA_KEY "basic metadata" 11050aef9f6SVitaliy Gusev #define JSON_SNAPSHOT_REQ_KEY "device" 111483d953aSJohn Baldwin #define JSON_SIZE_KEY "size" 112483d953aSJohn Baldwin #define JSON_FILE_OFFSET_KEY "file_offset" 113483d953aSJohn Baldwin 114483d953aSJohn Baldwin #define JSON_NCPUS_KEY "ncpus" 115483d953aSJohn Baldwin #define JSON_VMNAME_KEY "vmname" 116483d953aSJohn Baldwin #define JSON_MEMSIZE_KEY "memsize" 117483d953aSJohn Baldwin #define JSON_MEMFLAGS_KEY "memflags" 118483d953aSJohn Baldwin 119483d953aSJohn Baldwin #define min(a,b) \ 120483d953aSJohn Baldwin ({ \ 121483d953aSJohn Baldwin __typeof__ (a) _a = (a); \ 122483d953aSJohn Baldwin __typeof__ (b) _b = (b); \ 123483d953aSJohn Baldwin _a < _b ? _a : _b; \ 124483d953aSJohn Baldwin }) 125483d953aSJohn Baldwin 126c9faf698SMark Johnston static const struct vm_snapshot_kern_info snapshot_kern_structs[] = { 127483d953aSJohn Baldwin { "vhpet", STRUCT_VHPET }, 128483d953aSJohn Baldwin { "vm", STRUCT_VM }, 129483d953aSJohn Baldwin { "vioapic", STRUCT_VIOAPIC }, 130483d953aSJohn Baldwin { "vlapic", STRUCT_VLAPIC }, 131483d953aSJohn Baldwin { "vmcx", STRUCT_VMCX }, 132483d953aSJohn Baldwin { "vatpit", STRUCT_VATPIT }, 133483d953aSJohn Baldwin { "vatpic", STRUCT_VATPIC }, 134483d953aSJohn Baldwin { "vpmtmr", STRUCT_VPMTMR }, 135483d953aSJohn Baldwin { "vrtc", STRUCT_VRTC }, 136483d953aSJohn Baldwin }; 137483d953aSJohn Baldwin 138483d953aSJohn Baldwin static cpuset_t vcpus_active, vcpus_suspended; 1397de58287SVitaliy Gusev static pthread_mutex_t vcpu_lock = PTHREAD_MUTEX_INITIALIZER; 1407de58287SVitaliy Gusev static pthread_cond_t vcpus_idle = PTHREAD_COND_INITIALIZER; 1417de58287SVitaliy Gusev static pthread_cond_t vcpus_can_run = PTHREAD_COND_INITIALIZER; 142483d953aSJohn Baldwin static bool checkpoint_active; 143483d953aSJohn Baldwin 144483d953aSJohn Baldwin /* 145483d953aSJohn Baldwin * TODO: Harden this function and all of its callers since 'base_str' is a user 146483d953aSJohn Baldwin * provided string. 147483d953aSJohn Baldwin */ 148483d953aSJohn Baldwin static char * 149483d953aSJohn Baldwin strcat_extension(const char *base_str, const char *ext) 150483d953aSJohn Baldwin { 151483d953aSJohn Baldwin char *res; 152483d953aSJohn Baldwin size_t base_len, ext_len; 153483d953aSJohn Baldwin 154961e6a12SRobert Wing base_len = strnlen(base_str, NAME_MAX); 155961e6a12SRobert Wing ext_len = strnlen(ext, NAME_MAX); 156483d953aSJohn Baldwin 157961e6a12SRobert Wing if (base_len + ext_len > NAME_MAX) { 158b0936440SJohn Baldwin EPRINTLN("Filename exceeds maximum length."); 159483d953aSJohn Baldwin return (NULL); 160483d953aSJohn Baldwin } 161483d953aSJohn Baldwin 162483d953aSJohn Baldwin res = malloc(base_len + ext_len + 1); 163483d953aSJohn Baldwin if (res == NULL) { 164b0936440SJohn Baldwin EPRINTLN("Failed to allocate memory: %s", strerror(errno)); 165483d953aSJohn Baldwin return (NULL); 166483d953aSJohn Baldwin } 167483d953aSJohn Baldwin 168483d953aSJohn Baldwin memcpy(res, base_str, base_len); 169483d953aSJohn Baldwin memcpy(res + base_len, ext, ext_len); 170483d953aSJohn Baldwin res[base_len + ext_len] = 0; 171483d953aSJohn Baldwin 172483d953aSJohn Baldwin return (res); 173483d953aSJohn Baldwin } 174483d953aSJohn Baldwin 175483d953aSJohn Baldwin void 176483d953aSJohn Baldwin destroy_restore_state(struct restore_state *rstate) 177483d953aSJohn Baldwin { 178483d953aSJohn Baldwin if (rstate == NULL) { 179b0936440SJohn Baldwin EPRINTLN("Attempting to destroy NULL restore struct."); 180483d953aSJohn Baldwin return; 181483d953aSJohn Baldwin } 182483d953aSJohn Baldwin 183483d953aSJohn Baldwin if (rstate->kdata_map != MAP_FAILED) 184483d953aSJohn Baldwin munmap(rstate->kdata_map, rstate->kdata_len); 185483d953aSJohn Baldwin 186483d953aSJohn Baldwin if (rstate->kdata_fd > 0) 187483d953aSJohn Baldwin close(rstate->kdata_fd); 188483d953aSJohn Baldwin if (rstate->vmmem_fd > 0) 189483d953aSJohn Baldwin close(rstate->vmmem_fd); 190483d953aSJohn Baldwin 191483d953aSJohn Baldwin if (rstate->meta_root_obj != NULL) 192483d953aSJohn Baldwin ucl_object_unref(rstate->meta_root_obj); 193483d953aSJohn Baldwin if (rstate->meta_parser != NULL) 194483d953aSJohn Baldwin ucl_parser_free(rstate->meta_parser); 195483d953aSJohn Baldwin } 196483d953aSJohn Baldwin 197483d953aSJohn Baldwin static int 198483d953aSJohn Baldwin load_vmmem_file(const char *filename, struct restore_state *rstate) 199483d953aSJohn Baldwin { 200483d953aSJohn Baldwin struct stat sb; 201483d953aSJohn Baldwin int err; 202483d953aSJohn Baldwin 203483d953aSJohn Baldwin rstate->vmmem_fd = open(filename, O_RDONLY); 204483d953aSJohn Baldwin if (rstate->vmmem_fd < 0) { 205483d953aSJohn Baldwin perror("Failed to open restore file"); 206483d953aSJohn Baldwin return (-1); 207483d953aSJohn Baldwin } 208483d953aSJohn Baldwin 209483d953aSJohn Baldwin err = fstat(rstate->vmmem_fd, &sb); 210483d953aSJohn Baldwin if (err < 0) { 211483d953aSJohn Baldwin perror("Failed to stat restore file"); 212483d953aSJohn Baldwin goto err_load_vmmem; 213483d953aSJohn Baldwin } 214483d953aSJohn Baldwin 215483d953aSJohn Baldwin if (sb.st_size == 0) { 216483d953aSJohn Baldwin fprintf(stderr, "Restore file is empty.\n"); 217483d953aSJohn Baldwin goto err_load_vmmem; 218483d953aSJohn Baldwin } 219483d953aSJohn Baldwin 220483d953aSJohn Baldwin rstate->vmmem_len = sb.st_size; 221483d953aSJohn Baldwin 222483d953aSJohn Baldwin return (0); 223483d953aSJohn Baldwin 224483d953aSJohn Baldwin err_load_vmmem: 225483d953aSJohn Baldwin if (rstate->vmmem_fd > 0) 226483d953aSJohn Baldwin close(rstate->vmmem_fd); 227483d953aSJohn Baldwin return (-1); 228483d953aSJohn Baldwin } 229483d953aSJohn Baldwin 230483d953aSJohn Baldwin static int 231483d953aSJohn Baldwin load_kdata_file(const char *filename, struct restore_state *rstate) 232483d953aSJohn Baldwin { 233483d953aSJohn Baldwin struct stat sb; 234483d953aSJohn Baldwin int err; 235483d953aSJohn Baldwin 236483d953aSJohn Baldwin rstate->kdata_fd = open(filename, O_RDONLY); 237483d953aSJohn Baldwin if (rstate->kdata_fd < 0) { 238483d953aSJohn Baldwin perror("Failed to open kernel data file"); 239483d953aSJohn Baldwin return (-1); 240483d953aSJohn Baldwin } 241483d953aSJohn Baldwin 242483d953aSJohn Baldwin err = fstat(rstate->kdata_fd, &sb); 243483d953aSJohn Baldwin if (err < 0) { 244483d953aSJohn Baldwin perror("Failed to stat kernel data file"); 245483d953aSJohn Baldwin goto err_load_kdata; 246483d953aSJohn Baldwin } 247483d953aSJohn Baldwin 248483d953aSJohn Baldwin if (sb.st_size == 0) { 249483d953aSJohn Baldwin fprintf(stderr, "Kernel data file is empty.\n"); 250483d953aSJohn Baldwin goto err_load_kdata; 251483d953aSJohn Baldwin } 252483d953aSJohn Baldwin 253483d953aSJohn Baldwin rstate->kdata_len = sb.st_size; 254483d953aSJohn Baldwin rstate->kdata_map = mmap(NULL, rstate->kdata_len, PROT_READ, 255483d953aSJohn Baldwin MAP_SHARED, rstate->kdata_fd, 0); 256483d953aSJohn Baldwin if (rstate->kdata_map == MAP_FAILED) { 257483d953aSJohn Baldwin perror("Failed to map restore file"); 258483d953aSJohn Baldwin goto err_load_kdata; 259483d953aSJohn Baldwin } 260483d953aSJohn Baldwin 261483d953aSJohn Baldwin return (0); 262483d953aSJohn Baldwin 263483d953aSJohn Baldwin err_load_kdata: 264483d953aSJohn Baldwin if (rstate->kdata_fd > 0) 265483d953aSJohn Baldwin close(rstate->kdata_fd); 266483d953aSJohn Baldwin return (-1); 267483d953aSJohn Baldwin } 268483d953aSJohn Baldwin 269483d953aSJohn Baldwin static int 270483d953aSJohn Baldwin load_metadata_file(const char *filename, struct restore_state *rstate) 271483d953aSJohn Baldwin { 272c9faf698SMark Johnston ucl_object_t *obj; 273483d953aSJohn Baldwin struct ucl_parser *parser; 274483d953aSJohn Baldwin int err; 275483d953aSJohn Baldwin 276483d953aSJohn Baldwin parser = ucl_parser_new(UCL_PARSER_DEFAULT); 277483d953aSJohn Baldwin if (parser == NULL) { 278483d953aSJohn Baldwin fprintf(stderr, "Failed to initialize UCL parser.\n"); 279c9faf698SMark Johnston err = -1; 280483d953aSJohn Baldwin goto err_load_metadata; 281483d953aSJohn Baldwin } 282483d953aSJohn Baldwin 283483d953aSJohn Baldwin err = ucl_parser_add_file(parser, filename); 284483d953aSJohn Baldwin if (err == 0) { 285483d953aSJohn Baldwin fprintf(stderr, "Failed to parse metadata file: '%s'\n", 286483d953aSJohn Baldwin filename); 287483d953aSJohn Baldwin err = -1; 288483d953aSJohn Baldwin goto err_load_metadata; 289483d953aSJohn Baldwin } 290483d953aSJohn Baldwin 291483d953aSJohn Baldwin obj = ucl_parser_get_object(parser); 292483d953aSJohn Baldwin if (obj == NULL) { 293483d953aSJohn Baldwin fprintf(stderr, "Failed to parse object.\n"); 294483d953aSJohn Baldwin err = -1; 295483d953aSJohn Baldwin goto err_load_metadata; 296483d953aSJohn Baldwin } 297483d953aSJohn Baldwin 298483d953aSJohn Baldwin rstate->meta_parser = parser; 299483d953aSJohn Baldwin rstate->meta_root_obj = (ucl_object_t *)obj; 300483d953aSJohn Baldwin 301483d953aSJohn Baldwin return (0); 302483d953aSJohn Baldwin 303483d953aSJohn Baldwin err_load_metadata: 304483d953aSJohn Baldwin if (parser != NULL) 305483d953aSJohn Baldwin ucl_parser_free(parser); 306483d953aSJohn Baldwin return (err); 307483d953aSJohn Baldwin } 308483d953aSJohn Baldwin 309483d953aSJohn Baldwin int 310483d953aSJohn Baldwin load_restore_file(const char *filename, struct restore_state *rstate) 311483d953aSJohn Baldwin { 312483d953aSJohn Baldwin int err = 0; 313483d953aSJohn Baldwin char *kdata_filename = NULL, *meta_filename = NULL; 314483d953aSJohn Baldwin 315483d953aSJohn Baldwin assert(filename != NULL); 316483d953aSJohn Baldwin assert(rstate != NULL); 317483d953aSJohn Baldwin 318483d953aSJohn Baldwin memset(rstate, 0, sizeof(*rstate)); 319483d953aSJohn Baldwin rstate->kdata_map = MAP_FAILED; 320483d953aSJohn Baldwin 321483d953aSJohn Baldwin err = load_vmmem_file(filename, rstate); 322483d953aSJohn Baldwin if (err != 0) { 323483d953aSJohn Baldwin fprintf(stderr, "Failed to load guest RAM file.\n"); 324483d953aSJohn Baldwin goto err_restore; 325483d953aSJohn Baldwin } 326483d953aSJohn Baldwin 327483d953aSJohn Baldwin kdata_filename = strcat_extension(filename, ".kern"); 328483d953aSJohn Baldwin if (kdata_filename == NULL) { 329483d953aSJohn Baldwin fprintf(stderr, "Failed to construct kernel data filename.\n"); 330483d953aSJohn Baldwin goto err_restore; 331483d953aSJohn Baldwin } 332483d953aSJohn Baldwin 333483d953aSJohn Baldwin err = load_kdata_file(kdata_filename, rstate); 334483d953aSJohn Baldwin if (err != 0) { 335483d953aSJohn Baldwin fprintf(stderr, "Failed to load guest kernel data file.\n"); 336483d953aSJohn Baldwin goto err_restore; 337483d953aSJohn Baldwin } 338483d953aSJohn Baldwin 339483d953aSJohn Baldwin meta_filename = strcat_extension(filename, ".meta"); 340483d953aSJohn Baldwin if (meta_filename == NULL) { 341483d953aSJohn Baldwin fprintf(stderr, "Failed to construct kernel metadata filename.\n"); 342483d953aSJohn Baldwin goto err_restore; 343483d953aSJohn Baldwin } 344483d953aSJohn Baldwin 345483d953aSJohn Baldwin err = load_metadata_file(meta_filename, rstate); 346483d953aSJohn Baldwin if (err != 0) { 347483d953aSJohn Baldwin fprintf(stderr, "Failed to load guest metadata file.\n"); 348483d953aSJohn Baldwin goto err_restore; 349483d953aSJohn Baldwin } 350483d953aSJohn Baldwin 351483d953aSJohn Baldwin return (0); 352483d953aSJohn Baldwin 353483d953aSJohn Baldwin err_restore: 354483d953aSJohn Baldwin destroy_restore_state(rstate); 355483d953aSJohn Baldwin if (kdata_filename != NULL) 356483d953aSJohn Baldwin free(kdata_filename); 357483d953aSJohn Baldwin if (meta_filename != NULL) 358483d953aSJohn Baldwin free(meta_filename); 359483d953aSJohn Baldwin return (-1); 360483d953aSJohn Baldwin } 361483d953aSJohn Baldwin 362483d953aSJohn Baldwin #define JSON_GET_INT_OR_RETURN(key, obj, result_ptr, ret) \ 363483d953aSJohn Baldwin do { \ 364483d953aSJohn Baldwin const ucl_object_t *obj__; \ 365483d953aSJohn Baldwin obj__ = ucl_object_lookup(obj, key); \ 366483d953aSJohn Baldwin if (obj__ == NULL) { \ 367483d953aSJohn Baldwin fprintf(stderr, "Missing key: '%s'", key); \ 368483d953aSJohn Baldwin return (ret); \ 369483d953aSJohn Baldwin } \ 370483d953aSJohn Baldwin if (!ucl_object_toint_safe(obj__, result_ptr)) { \ 371483d953aSJohn Baldwin fprintf(stderr, "Cannot convert '%s' value to int.", key); \ 372483d953aSJohn Baldwin return (ret); \ 373483d953aSJohn Baldwin } \ 374483d953aSJohn Baldwin } while(0) 375483d953aSJohn Baldwin 376483d953aSJohn Baldwin #define JSON_GET_STRING_OR_RETURN(key, obj, result_ptr, ret) \ 377483d953aSJohn Baldwin do { \ 378483d953aSJohn Baldwin const ucl_object_t *obj__; \ 379483d953aSJohn Baldwin obj__ = ucl_object_lookup(obj, key); \ 380483d953aSJohn Baldwin if (obj__ == NULL) { \ 381483d953aSJohn Baldwin fprintf(stderr, "Missing key: '%s'", key); \ 382483d953aSJohn Baldwin return (ret); \ 383483d953aSJohn Baldwin } \ 384483d953aSJohn Baldwin if (!ucl_object_tostring_safe(obj__, result_ptr)) { \ 385483d953aSJohn Baldwin fprintf(stderr, "Cannot convert '%s' value to string.", key); \ 386483d953aSJohn Baldwin return (ret); \ 387483d953aSJohn Baldwin } \ 388483d953aSJohn Baldwin } while(0) 389483d953aSJohn Baldwin 390483d953aSJohn Baldwin static void * 391483d953aSJohn Baldwin lookup_check_dev(const char *dev_name, struct restore_state *rstate, 392483d953aSJohn Baldwin const ucl_object_t *obj, size_t *data_size) 393483d953aSJohn Baldwin { 394483d953aSJohn Baldwin const char *snapshot_req; 395483d953aSJohn Baldwin int64_t size, file_offset; 396483d953aSJohn Baldwin 397483d953aSJohn Baldwin snapshot_req = NULL; 398483d953aSJohn Baldwin JSON_GET_STRING_OR_RETURN(JSON_SNAPSHOT_REQ_KEY, obj, 399483d953aSJohn Baldwin &snapshot_req, NULL); 400483d953aSJohn Baldwin assert(snapshot_req != NULL); 401483d953aSJohn Baldwin if (!strcmp(snapshot_req, dev_name)) { 402483d953aSJohn Baldwin JSON_GET_INT_OR_RETURN(JSON_SIZE_KEY, obj, 403483d953aSJohn Baldwin &size, NULL); 404483d953aSJohn Baldwin assert(size >= 0); 405483d953aSJohn Baldwin 406483d953aSJohn Baldwin JSON_GET_INT_OR_RETURN(JSON_FILE_OFFSET_KEY, obj, 407483d953aSJohn Baldwin &file_offset, NULL); 408483d953aSJohn Baldwin assert(file_offset >= 0); 409ed721684SMark Johnston assert((uint64_t)file_offset + size <= rstate->kdata_len); 410483d953aSJohn Baldwin 411483d953aSJohn Baldwin *data_size = (size_t)size; 41263898728SMark Johnston return ((uint8_t *)rstate->kdata_map + file_offset); 413483d953aSJohn Baldwin } 414483d953aSJohn Baldwin 415483d953aSJohn Baldwin return (NULL); 416483d953aSJohn Baldwin } 417483d953aSJohn Baldwin 418483d953aSJohn Baldwin static void * 419ee5023f3SVitaliy Gusev lookup_dev(const char *dev_name, const char *key, struct restore_state *rstate, 420483d953aSJohn Baldwin size_t *data_size) 421483d953aSJohn Baldwin { 422483d953aSJohn Baldwin const ucl_object_t *devs = NULL, *obj = NULL; 423483d953aSJohn Baldwin ucl_object_iter_t it = NULL; 424483d953aSJohn Baldwin void *ret; 425483d953aSJohn Baldwin 426ee5023f3SVitaliy Gusev devs = ucl_object_lookup(rstate->meta_root_obj, key); 427483d953aSJohn Baldwin if (devs == NULL) { 428483d953aSJohn Baldwin fprintf(stderr, "Failed to find '%s' object.\n", 429483d953aSJohn Baldwin JSON_DEV_ARR_KEY); 430483d953aSJohn Baldwin return (NULL); 431483d953aSJohn Baldwin } 432483d953aSJohn Baldwin 433c9faf698SMark Johnston if (ucl_object_type(devs) != UCL_ARRAY) { 434483d953aSJohn Baldwin fprintf(stderr, "Object '%s' is not an array.\n", 435483d953aSJohn Baldwin JSON_DEV_ARR_KEY); 436483d953aSJohn Baldwin return (NULL); 437483d953aSJohn Baldwin } 438483d953aSJohn Baldwin 439483d953aSJohn Baldwin while ((obj = ucl_object_iterate(devs, &it, true)) != NULL) { 440483d953aSJohn Baldwin ret = lookup_check_dev(dev_name, rstate, obj, data_size); 441483d953aSJohn Baldwin if (ret != NULL) 442483d953aSJohn Baldwin return (ret); 443483d953aSJohn Baldwin } 444483d953aSJohn Baldwin 445483d953aSJohn Baldwin return (NULL); 446483d953aSJohn Baldwin } 447483d953aSJohn Baldwin 448483d953aSJohn Baldwin static const ucl_object_t * 449483d953aSJohn Baldwin lookup_basic_metadata_object(struct restore_state *rstate) 450483d953aSJohn Baldwin { 451483d953aSJohn Baldwin const ucl_object_t *basic_meta_obj = NULL; 452483d953aSJohn Baldwin 453483d953aSJohn Baldwin basic_meta_obj = ucl_object_lookup(rstate->meta_root_obj, 454483d953aSJohn Baldwin JSON_BASIC_METADATA_KEY); 455483d953aSJohn Baldwin if (basic_meta_obj == NULL) { 456483d953aSJohn Baldwin fprintf(stderr, "Failed to find '%s' object.\n", 457483d953aSJohn Baldwin JSON_BASIC_METADATA_KEY); 458483d953aSJohn Baldwin return (NULL); 459483d953aSJohn Baldwin } 460483d953aSJohn Baldwin 461c9faf698SMark Johnston if (ucl_object_type(basic_meta_obj) != UCL_OBJECT) { 462483d953aSJohn Baldwin fprintf(stderr, "Object '%s' is not a JSON object.\n", 463483d953aSJohn Baldwin JSON_BASIC_METADATA_KEY); 464483d953aSJohn Baldwin return (NULL); 465483d953aSJohn Baldwin } 466483d953aSJohn Baldwin 467483d953aSJohn Baldwin return (basic_meta_obj); 468483d953aSJohn Baldwin } 469483d953aSJohn Baldwin 470483d953aSJohn Baldwin const char * 471483d953aSJohn Baldwin lookup_vmname(struct restore_state *rstate) 472483d953aSJohn Baldwin { 473483d953aSJohn Baldwin const char *vmname; 474483d953aSJohn Baldwin const ucl_object_t *obj; 475483d953aSJohn Baldwin 476483d953aSJohn Baldwin obj = lookup_basic_metadata_object(rstate); 477483d953aSJohn Baldwin if (obj == NULL) 478483d953aSJohn Baldwin return (NULL); 479483d953aSJohn Baldwin 480483d953aSJohn Baldwin JSON_GET_STRING_OR_RETURN(JSON_VMNAME_KEY, obj, &vmname, NULL); 481483d953aSJohn Baldwin return (vmname); 482483d953aSJohn Baldwin } 483483d953aSJohn Baldwin 484483d953aSJohn Baldwin int 485483d953aSJohn Baldwin lookup_memflags(struct restore_state *rstate) 486483d953aSJohn Baldwin { 487483d953aSJohn Baldwin int64_t memflags; 488483d953aSJohn Baldwin const ucl_object_t *obj; 489483d953aSJohn Baldwin 490483d953aSJohn Baldwin obj = lookup_basic_metadata_object(rstate); 491483d953aSJohn Baldwin if (obj == NULL) 492483d953aSJohn Baldwin return (0); 493483d953aSJohn Baldwin 494483d953aSJohn Baldwin JSON_GET_INT_OR_RETURN(JSON_MEMFLAGS_KEY, obj, &memflags, 0); 495483d953aSJohn Baldwin 496483d953aSJohn Baldwin return ((int)memflags); 497483d953aSJohn Baldwin } 498483d953aSJohn Baldwin 499483d953aSJohn Baldwin size_t 500483d953aSJohn Baldwin lookup_memsize(struct restore_state *rstate) 501483d953aSJohn Baldwin { 502483d953aSJohn Baldwin int64_t memsize; 503483d953aSJohn Baldwin const ucl_object_t *obj; 504483d953aSJohn Baldwin 505483d953aSJohn Baldwin obj = lookup_basic_metadata_object(rstate); 506483d953aSJohn Baldwin if (obj == NULL) 507483d953aSJohn Baldwin return (0); 508483d953aSJohn Baldwin 509483d953aSJohn Baldwin JSON_GET_INT_OR_RETURN(JSON_MEMSIZE_KEY, obj, &memsize, 0); 510483d953aSJohn Baldwin if (memsize < 0) 511483d953aSJohn Baldwin memsize = 0; 512483d953aSJohn Baldwin 513483d953aSJohn Baldwin return ((size_t)memsize); 514483d953aSJohn Baldwin } 515483d953aSJohn Baldwin 516483d953aSJohn Baldwin 517483d953aSJohn Baldwin int 518483d953aSJohn Baldwin lookup_guest_ncpus(struct restore_state *rstate) 519483d953aSJohn Baldwin { 520483d953aSJohn Baldwin int64_t ncpus; 521483d953aSJohn Baldwin const ucl_object_t *obj; 522483d953aSJohn Baldwin 523483d953aSJohn Baldwin obj = lookup_basic_metadata_object(rstate); 524483d953aSJohn Baldwin if (obj == NULL) 525483d953aSJohn Baldwin return (0); 526483d953aSJohn Baldwin 527483d953aSJohn Baldwin JSON_GET_INT_OR_RETURN(JSON_NCPUS_KEY, obj, &ncpus, 0); 528483d953aSJohn Baldwin return ((int)ncpus); 529483d953aSJohn Baldwin } 530483d953aSJohn Baldwin 531483d953aSJohn Baldwin static void 53298d920d9SMark Johnston winch_handler(int signal __unused) 533483d953aSJohn Baldwin { 534483d953aSJohn Baldwin #ifdef TIOCGWINSZ 535483d953aSJohn Baldwin ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize); 536483d953aSJohn Baldwin #endif /* TIOCGWINSZ */ 537483d953aSJohn Baldwin } 538483d953aSJohn Baldwin 539483d953aSJohn Baldwin static int 540483d953aSJohn Baldwin print_progress(size_t crtval, const size_t maxval) 541483d953aSJohn Baldwin { 542483d953aSJohn Baldwin size_t rc; 543483d953aSJohn Baldwin double crtval_gb, maxval_gb; 544483d953aSJohn Baldwin size_t i, win_width, prog_start, prog_done, prog_end; 545483d953aSJohn Baldwin int mval_len; 546483d953aSJohn Baldwin 547483d953aSJohn Baldwin static char prog_buf[PROG_BUF_SZ]; 548483d953aSJohn Baldwin static const size_t len = sizeof(prog_buf); 549483d953aSJohn Baldwin 550483d953aSJohn Baldwin static size_t div; 551c9faf698SMark Johnston static const char *div_str; 552483d953aSJohn Baldwin 553483d953aSJohn Baldwin static char wip_bar[] = { '/', '-', '\\', '|' }; 554483d953aSJohn Baldwin static int wip_idx = 0; 555483d953aSJohn Baldwin 556483d953aSJohn Baldwin if (maxval == 0) { 557483d953aSJohn Baldwin printf("[0B / 0B]\r\n"); 558483d953aSJohn Baldwin return (0); 559483d953aSJohn Baldwin } 560483d953aSJohn Baldwin 561483d953aSJohn Baldwin if (crtval > maxval) 562483d953aSJohn Baldwin crtval = maxval; 563483d953aSJohn Baldwin 564483d953aSJohn Baldwin if (maxval > 10 * GB) { 565483d953aSJohn Baldwin div = GB; 566483d953aSJohn Baldwin div_str = "GiB"; 567483d953aSJohn Baldwin } else if (maxval > 10 * MB) { 568483d953aSJohn Baldwin div = MB; 569483d953aSJohn Baldwin div_str = "MiB"; 570483d953aSJohn Baldwin } else { 571483d953aSJohn Baldwin div = KB; 572483d953aSJohn Baldwin div_str = "KiB"; 573483d953aSJohn Baldwin } 574483d953aSJohn Baldwin 575483d953aSJohn Baldwin crtval_gb = (double) crtval / div; 576483d953aSJohn Baldwin maxval_gb = (double) maxval / div; 577483d953aSJohn Baldwin 578483d953aSJohn Baldwin rc = snprintf(prog_buf, len, "%.03lf", maxval_gb); 579483d953aSJohn Baldwin if (rc == len) { 580483d953aSJohn Baldwin fprintf(stderr, "Maxval too big\n"); 581483d953aSJohn Baldwin return (-1); 582483d953aSJohn Baldwin } 583483d953aSJohn Baldwin mval_len = rc; 584483d953aSJohn Baldwin 585483d953aSJohn Baldwin rc = snprintf(prog_buf, len, "\r[%*.03lf%s / %.03lf%s] |", 586483d953aSJohn Baldwin mval_len, crtval_gb, div_str, maxval_gb, div_str); 587483d953aSJohn Baldwin 588483d953aSJohn Baldwin if (rc == len) { 589483d953aSJohn Baldwin fprintf(stderr, "Buffer too small to print progress\n"); 590483d953aSJohn Baldwin return (-1); 591483d953aSJohn Baldwin } 592483d953aSJohn Baldwin 593483d953aSJohn Baldwin win_width = min(winsize.ws_col, len); 594483d953aSJohn Baldwin prog_start = rc; 595483d953aSJohn Baldwin 596483d953aSJohn Baldwin if (prog_start < (win_width - 2)) { 597483d953aSJohn Baldwin prog_end = win_width - prog_start - 2; 598483d953aSJohn Baldwin prog_done = prog_end * (crtval_gb / maxval_gb); 599483d953aSJohn Baldwin 600483d953aSJohn Baldwin for (i = prog_start; i < prog_start + prog_done; i++) 601483d953aSJohn Baldwin prog_buf[i] = '#'; 602483d953aSJohn Baldwin 603483d953aSJohn Baldwin if (crtval != maxval) { 604483d953aSJohn Baldwin prog_buf[i] = wip_bar[wip_idx]; 605483d953aSJohn Baldwin wip_idx = (wip_idx + 1) % sizeof(wip_bar); 606483d953aSJohn Baldwin i++; 607483d953aSJohn Baldwin } else { 608483d953aSJohn Baldwin prog_buf[i++] = '#'; 609483d953aSJohn Baldwin } 610483d953aSJohn Baldwin 611483d953aSJohn Baldwin for (; i < win_width - 2; i++) 612483d953aSJohn Baldwin prog_buf[i] = '_'; 613483d953aSJohn Baldwin 614483d953aSJohn Baldwin prog_buf[win_width - 2] = '|'; 615483d953aSJohn Baldwin } 616483d953aSJohn Baldwin 617483d953aSJohn Baldwin prog_buf[win_width - 1] = '\0'; 618483d953aSJohn Baldwin write(STDOUT_FILENO, prog_buf, win_width); 619483d953aSJohn Baldwin 620483d953aSJohn Baldwin return (0); 621483d953aSJohn Baldwin } 622483d953aSJohn Baldwin 623483d953aSJohn Baldwin static void * 624483d953aSJohn Baldwin snapshot_spinner_cb(void *arg) 625483d953aSJohn Baldwin { 626483d953aSJohn Baldwin int rc; 627483d953aSJohn Baldwin size_t crtval, maxval, total; 628483d953aSJohn Baldwin struct spinner_info *si; 629483d953aSJohn Baldwin struct timespec ts; 630483d953aSJohn Baldwin 631483d953aSJohn Baldwin si = arg; 632483d953aSJohn Baldwin if (si == NULL) 633483d953aSJohn Baldwin pthread_exit(NULL); 634483d953aSJohn Baldwin 635483d953aSJohn Baldwin ts.tv_sec = 0; 636483d953aSJohn Baldwin ts.tv_nsec = 50 * 1000 * 1000; /* 50 ms sleep time */ 637483d953aSJohn Baldwin 638483d953aSJohn Baldwin do { 639483d953aSJohn Baldwin crtval = *si->crtval; 640483d953aSJohn Baldwin maxval = si->maxval; 641483d953aSJohn Baldwin total = si->total; 642483d953aSJohn Baldwin 643483d953aSJohn Baldwin rc = print_progress(crtval, total); 644483d953aSJohn Baldwin if (rc < 0) { 645483d953aSJohn Baldwin fprintf(stderr, "Failed to parse progress\n"); 646483d953aSJohn Baldwin break; 647483d953aSJohn Baldwin } 648483d953aSJohn Baldwin 649483d953aSJohn Baldwin nanosleep(&ts, NULL); 650483d953aSJohn Baldwin } while (crtval < maxval); 651483d953aSJohn Baldwin 652483d953aSJohn Baldwin pthread_exit(NULL); 653483d953aSJohn Baldwin return NULL; 654483d953aSJohn Baldwin } 655483d953aSJohn Baldwin 656483d953aSJohn Baldwin static int 657483d953aSJohn Baldwin vm_snapshot_mem_part(const int snapfd, const size_t foff, void *src, 658483d953aSJohn Baldwin const size_t len, const size_t totalmem, const bool op_wr) 659483d953aSJohn Baldwin { 660483d953aSJohn Baldwin int rc; 661483d953aSJohn Baldwin size_t part_done, todo, rem; 662483d953aSJohn Baldwin ssize_t done; 663483d953aSJohn Baldwin bool show_progress; 664483d953aSJohn Baldwin pthread_t spinner_th; 665483d953aSJohn Baldwin struct spinner_info *si; 666483d953aSJohn Baldwin 667483d953aSJohn Baldwin if (lseek(snapfd, foff, SEEK_SET) < 0) { 668483d953aSJohn Baldwin perror("Failed to change file offset"); 669483d953aSJohn Baldwin return (-1); 670483d953aSJohn Baldwin } 671483d953aSJohn Baldwin 672483d953aSJohn Baldwin show_progress = false; 673483d953aSJohn Baldwin if (isatty(STDIN_FILENO) && (winsize.ws_col != 0)) 674483d953aSJohn Baldwin show_progress = true; 675483d953aSJohn Baldwin 676483d953aSJohn Baldwin part_done = foff; 677483d953aSJohn Baldwin rem = len; 678483d953aSJohn Baldwin 679483d953aSJohn Baldwin if (show_progress) { 680483d953aSJohn Baldwin si = &(struct spinner_info) { 681483d953aSJohn Baldwin .crtval = &part_done, 682483d953aSJohn Baldwin .maxval = foff + len, 683483d953aSJohn Baldwin .total = totalmem 684483d953aSJohn Baldwin }; 685483d953aSJohn Baldwin 686483d953aSJohn Baldwin rc = pthread_create(&spinner_th, 0, snapshot_spinner_cb, si); 687483d953aSJohn Baldwin if (rc) { 688483d953aSJohn Baldwin perror("Unable to create spinner thread"); 689483d953aSJohn Baldwin show_progress = false; 690483d953aSJohn Baldwin } 691483d953aSJohn Baldwin } 692483d953aSJohn Baldwin 693483d953aSJohn Baldwin while (rem > 0) { 694483d953aSJohn Baldwin if (show_progress) 695483d953aSJohn Baldwin todo = min(SNAPSHOT_CHUNK, rem); 696483d953aSJohn Baldwin else 697483d953aSJohn Baldwin todo = rem; 698483d953aSJohn Baldwin 699483d953aSJohn Baldwin if (op_wr) 700483d953aSJohn Baldwin done = write(snapfd, src, todo); 701483d953aSJohn Baldwin else 702483d953aSJohn Baldwin done = read(snapfd, src, todo); 703483d953aSJohn Baldwin if (done < 0) { 704483d953aSJohn Baldwin perror("Failed to write in file"); 705483d953aSJohn Baldwin return (-1); 706483d953aSJohn Baldwin } 707483d953aSJohn Baldwin 708c9faf698SMark Johnston src = (uint8_t *)src + done; 709483d953aSJohn Baldwin part_done += done; 710483d953aSJohn Baldwin rem -= done; 711483d953aSJohn Baldwin } 712483d953aSJohn Baldwin 713483d953aSJohn Baldwin if (show_progress) { 714483d953aSJohn Baldwin rc = pthread_join(spinner_th, NULL); 715483d953aSJohn Baldwin if (rc) 716483d953aSJohn Baldwin perror("Unable to end spinner thread"); 717483d953aSJohn Baldwin } 718483d953aSJohn Baldwin 719483d953aSJohn Baldwin return (0); 720483d953aSJohn Baldwin } 721483d953aSJohn Baldwin 722483d953aSJohn Baldwin static size_t 723483d953aSJohn Baldwin vm_snapshot_mem(struct vmctx *ctx, int snapfd, size_t memsz, const bool op_wr) 724483d953aSJohn Baldwin { 725483d953aSJohn Baldwin int ret; 726483d953aSJohn Baldwin size_t lowmem, highmem, totalmem; 727483d953aSJohn Baldwin char *baseaddr; 728483d953aSJohn Baldwin 729483d953aSJohn Baldwin ret = vm_get_guestmem_from_ctx(ctx, &baseaddr, &lowmem, &highmem); 730483d953aSJohn Baldwin if (ret) { 731483d953aSJohn Baldwin fprintf(stderr, "%s: unable to retrieve guest memory size\r\n", 732483d953aSJohn Baldwin __func__); 733483d953aSJohn Baldwin return (0); 734483d953aSJohn Baldwin } 735483d953aSJohn Baldwin totalmem = lowmem + highmem; 736483d953aSJohn Baldwin 737483d953aSJohn Baldwin if ((op_wr == false) && (totalmem != memsz)) { 738483d953aSJohn Baldwin fprintf(stderr, "%s: mem size mismatch: %ld vs %ld\r\n", 739483d953aSJohn Baldwin __func__, totalmem, memsz); 740483d953aSJohn Baldwin return (0); 741483d953aSJohn Baldwin } 742483d953aSJohn Baldwin 743483d953aSJohn Baldwin winsize.ws_col = 80; 744483d953aSJohn Baldwin #ifdef TIOCGWINSZ 745483d953aSJohn Baldwin ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize); 746483d953aSJohn Baldwin #endif /* TIOCGWINSZ */ 747483d953aSJohn Baldwin old_winch_handler = signal(SIGWINCH, winch_handler); 748483d953aSJohn Baldwin 749483d953aSJohn Baldwin ret = vm_snapshot_mem_part(snapfd, 0, baseaddr, lowmem, 750483d953aSJohn Baldwin totalmem, op_wr); 751483d953aSJohn Baldwin if (ret) { 752483d953aSJohn Baldwin fprintf(stderr, "%s: Could not %s lowmem\r\n", 753483d953aSJohn Baldwin __func__, op_wr ? "write" : "read"); 754483d953aSJohn Baldwin totalmem = 0; 755483d953aSJohn Baldwin goto done; 756483d953aSJohn Baldwin } 757483d953aSJohn Baldwin 758483d953aSJohn Baldwin if (highmem == 0) 759483d953aSJohn Baldwin goto done; 760483d953aSJohn Baldwin 761*e497fe86SMark Johnston ret = vm_snapshot_mem_part(snapfd, lowmem, 762*e497fe86SMark Johnston baseaddr + vm_get_highmem_base(ctx), highmem, totalmem, op_wr); 763483d953aSJohn Baldwin if (ret) { 764483d953aSJohn Baldwin fprintf(stderr, "%s: Could not %s highmem\r\n", 765483d953aSJohn Baldwin __func__, op_wr ? "write" : "read"); 766483d953aSJohn Baldwin totalmem = 0; 767483d953aSJohn Baldwin goto done; 768483d953aSJohn Baldwin } 769483d953aSJohn Baldwin 770483d953aSJohn Baldwin done: 771483d953aSJohn Baldwin printf("\r\n"); 772483d953aSJohn Baldwin signal(SIGWINCH, old_winch_handler); 773483d953aSJohn Baldwin 774483d953aSJohn Baldwin return (totalmem); 775483d953aSJohn Baldwin } 776483d953aSJohn Baldwin 777483d953aSJohn Baldwin int 778483d953aSJohn Baldwin restore_vm_mem(struct vmctx *ctx, struct restore_state *rstate) 779483d953aSJohn Baldwin { 780483d953aSJohn Baldwin size_t restored; 781483d953aSJohn Baldwin 782483d953aSJohn Baldwin restored = vm_snapshot_mem(ctx, rstate->vmmem_fd, rstate->vmmem_len, 783483d953aSJohn Baldwin false); 784483d953aSJohn Baldwin 785483d953aSJohn Baldwin if (restored != rstate->vmmem_len) 786483d953aSJohn Baldwin return (-1); 787483d953aSJohn Baldwin 788483d953aSJohn Baldwin return (0); 789483d953aSJohn Baldwin } 790483d953aSJohn Baldwin 791ee5023f3SVitaliy Gusev int 792ee5023f3SVitaliy Gusev vm_restore_kern_structs(struct vmctx *ctx, struct restore_state *rstate) 793483d953aSJohn Baldwin { 794ee5023f3SVitaliy Gusev for (unsigned i = 0; i < nitems(snapshot_kern_structs); i++) { 795ee5023f3SVitaliy Gusev const struct vm_snapshot_kern_info *info; 796483d953aSJohn Baldwin struct vm_snapshot_meta *meta; 797ee5023f3SVitaliy Gusev void *data; 798ee5023f3SVitaliy Gusev size_t size; 799483d953aSJohn Baldwin 800ee5023f3SVitaliy Gusev info = &snapshot_kern_structs[i]; 801ee5023f3SVitaliy Gusev data = lookup_dev(info->struct_name, JSON_KERNEL_ARR_KEY, rstate, &size); 802ee5023f3SVitaliy Gusev if (data == NULL) 803ee5023f3SVitaliy Gusev errx(EX_DATAERR, "Cannot find kern struct %s", 804ee5023f3SVitaliy Gusev info->struct_name); 805483d953aSJohn Baldwin 806ee5023f3SVitaliy Gusev if (size == 0) 807ee5023f3SVitaliy Gusev errx(EX_DATAERR, "data with zero size for %s", 808ee5023f3SVitaliy Gusev info->struct_name); 809483d953aSJohn Baldwin 810483d953aSJohn Baldwin meta = &(struct vm_snapshot_meta) { 811483d953aSJohn Baldwin .dev_name = info->struct_name, 812483d953aSJohn Baldwin .dev_req = info->req, 813483d953aSJohn Baldwin 814ee5023f3SVitaliy Gusev .buffer.buf_start = data, 815ee5023f3SVitaliy Gusev .buffer.buf_size = size, 816483d953aSJohn Baldwin 817ee5023f3SVitaliy Gusev .buffer.buf = data, 818ee5023f3SVitaliy Gusev .buffer.buf_rem = size, 819483d953aSJohn Baldwin 820483d953aSJohn Baldwin .op = VM_SNAPSHOT_RESTORE, 821483d953aSJohn Baldwin }; 822483d953aSJohn Baldwin 823ee5023f3SVitaliy Gusev if (vm_snapshot_req(ctx, meta)) 824ee5023f3SVitaliy Gusev err(EX_DATAERR, "Failed to restore %s", 825ee5023f3SVitaliy Gusev info->struct_name); 826483d953aSJohn Baldwin } 827483d953aSJohn Baldwin return (0); 828483d953aSJohn Baldwin } 829483d953aSJohn Baldwin 830c9faf698SMark Johnston static int 831381ef27dSVitaliy Gusev vm_restore_device(struct restore_state *rstate, vm_snapshot_dev_cb func, 832381ef27dSVitaliy Gusev const char *name, void *data) 833483d953aSJohn Baldwin { 834483d953aSJohn Baldwin void *dev_ptr; 835483d953aSJohn Baldwin size_t dev_size; 836483d953aSJohn Baldwin int ret; 837483d953aSJohn Baldwin struct vm_snapshot_meta *meta; 838483d953aSJohn Baldwin 839381ef27dSVitaliy Gusev dev_ptr = lookup_dev(name, JSON_DEV_ARR_KEY, rstate, &dev_size); 840381ef27dSVitaliy Gusev 841483d953aSJohn Baldwin if (dev_ptr == NULL) { 842381ef27dSVitaliy Gusev EPRINTLN("Failed to lookup dev: %s", name); 843381ef27dSVitaliy Gusev return (EINVAL); 844483d953aSJohn Baldwin } 845483d953aSJohn Baldwin 846483d953aSJohn Baldwin if (dev_size == 0) { 847381ef27dSVitaliy Gusev EPRINTLN("Restore device size is 0: %s", name); 848381ef27dSVitaliy Gusev return (EINVAL); 849483d953aSJohn Baldwin } 850483d953aSJohn Baldwin 851483d953aSJohn Baldwin meta = &(struct vm_snapshot_meta) { 852381ef27dSVitaliy Gusev .dev_name = name, 853381ef27dSVitaliy Gusev .dev_data = data, 854483d953aSJohn Baldwin 855483d953aSJohn Baldwin .buffer.buf_start = dev_ptr, 856483d953aSJohn Baldwin .buffer.buf_size = dev_size, 857483d953aSJohn Baldwin 858483d953aSJohn Baldwin .buffer.buf = dev_ptr, 859483d953aSJohn Baldwin .buffer.buf_rem = dev_size, 860483d953aSJohn Baldwin 861483d953aSJohn Baldwin .op = VM_SNAPSHOT_RESTORE, 862483d953aSJohn Baldwin }; 863483d953aSJohn Baldwin 864381ef27dSVitaliy Gusev ret = func(meta); 865483d953aSJohn Baldwin if (ret != 0) { 866381ef27dSVitaliy Gusev EPRINTLN("Failed to restore dev: %s %d", name, ret); 867381ef27dSVitaliy Gusev return (ret); 868483d953aSJohn Baldwin } 869483d953aSJohn Baldwin 870483d953aSJohn Baldwin return (0); 871483d953aSJohn Baldwin } 872483d953aSJohn Baldwin 873483d953aSJohn Baldwin int 874b10d65a4SVitaliy Gusev vm_restore_devices(struct restore_state *rstate) 875483d953aSJohn Baldwin { 876483d953aSJohn Baldwin int ret; 877381ef27dSVitaliy Gusev struct pci_devinst *pdi = NULL; 878483d953aSJohn Baldwin 879381ef27dSVitaliy Gusev while ((pdi = pci_next(pdi)) != NULL) { 880381ef27dSVitaliy Gusev ret = vm_restore_device(rstate, pci_snapshot, pdi->pi_name, pdi); 881381ef27dSVitaliy Gusev if (ret) 882483d953aSJohn Baldwin return (ret); 883483d953aSJohn Baldwin } 884483d953aSJohn Baldwin 885f927afc1SMark Johnston #ifdef __amd64__ 886f927afc1SMark Johnston ret = vm_restore_device(rstate, atkbdc_snapshot, "atkbdc", NULL); 887f927afc1SMark Johnston #else 888f927afc1SMark Johnston ret = 0; 889f927afc1SMark Johnston #endif 890f927afc1SMark Johnston return (ret); 891483d953aSJohn Baldwin } 892483d953aSJohn Baldwin 893483d953aSJohn Baldwin int 894b10d65a4SVitaliy Gusev vm_pause_devices(void) 895483d953aSJohn Baldwin { 896483d953aSJohn Baldwin int ret; 897381ef27dSVitaliy Gusev struct pci_devinst *pdi = NULL; 898483d953aSJohn Baldwin 899381ef27dSVitaliy Gusev while ((pdi = pci_next(pdi)) != NULL) { 900381ef27dSVitaliy Gusev ret = pci_pause(pdi); 901381ef27dSVitaliy Gusev if (ret) { 902381ef27dSVitaliy Gusev EPRINTLN("Cannot pause dev %s: %d", pdi->pi_name, ret); 903483d953aSJohn Baldwin return (ret); 904483d953aSJohn Baldwin } 905381ef27dSVitaliy Gusev } 906483d953aSJohn Baldwin 907483d953aSJohn Baldwin return (0); 908483d953aSJohn Baldwin } 909483d953aSJohn Baldwin 910483d953aSJohn Baldwin int 911b10d65a4SVitaliy Gusev vm_resume_devices(void) 912483d953aSJohn Baldwin { 913483d953aSJohn Baldwin int ret; 914381ef27dSVitaliy Gusev struct pci_devinst *pdi = NULL; 915483d953aSJohn Baldwin 916381ef27dSVitaliy Gusev while ((pdi = pci_next(pdi)) != NULL) { 917381ef27dSVitaliy Gusev ret = pci_resume(pdi); 918381ef27dSVitaliy Gusev if (ret) { 919381ef27dSVitaliy Gusev EPRINTLN("Cannot resume '%s': %d", pdi->pi_name, ret); 920483d953aSJohn Baldwin return (ret); 921483d953aSJohn Baldwin } 922381ef27dSVitaliy Gusev } 923483d953aSJohn Baldwin 924483d953aSJohn Baldwin return (0); 925483d953aSJohn Baldwin } 926483d953aSJohn Baldwin 927483d953aSJohn Baldwin static int 928ee5023f3SVitaliy Gusev vm_save_kern_struct(struct vmctx *ctx, int data_fd, xo_handle_t *xop, 9290f735657SJohn Baldwin const char *array_key, struct vm_snapshot_meta *meta, off_t *offset) 930483d953aSJohn Baldwin { 931483d953aSJohn Baldwin int ret; 932483d953aSJohn Baldwin size_t data_size; 933483d953aSJohn Baldwin ssize_t write_cnt; 934483d953aSJohn Baldwin 9350f735657SJohn Baldwin ret = vm_snapshot_req(ctx, meta); 936483d953aSJohn Baldwin if (ret != 0) { 937483d953aSJohn Baldwin fprintf(stderr, "%s: Failed to snapshot struct %s\r\n", 938483d953aSJohn Baldwin __func__, meta->dev_name); 939483d953aSJohn Baldwin ret = -1; 940483d953aSJohn Baldwin goto done; 941483d953aSJohn Baldwin } 942483d953aSJohn Baldwin 943483d953aSJohn Baldwin data_size = vm_get_snapshot_size(meta); 944483d953aSJohn Baldwin 945ed721684SMark Johnston /* XXX-MJ no handling for short writes. */ 946483d953aSJohn Baldwin write_cnt = write(data_fd, meta->buffer.buf_start, data_size); 947ed721684SMark Johnston if (write_cnt < 0 || (size_t)write_cnt != data_size) { 948483d953aSJohn Baldwin perror("Failed to write all snapshotted data."); 949483d953aSJohn Baldwin ret = -1; 950483d953aSJohn Baldwin goto done; 951483d953aSJohn Baldwin } 952483d953aSJohn Baldwin 953483d953aSJohn Baldwin /* Write metadata. */ 954483d953aSJohn Baldwin xo_open_instance_h(xop, array_key); 955ee5023f3SVitaliy Gusev xo_emit_h(xop, "{:" JSON_SNAPSHOT_REQ_KEY "/%s}\n", 956ee5023f3SVitaliy Gusev meta->dev_name); 957483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_SIZE_KEY "/%lu}\n", data_size); 958483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_FILE_OFFSET_KEY "/%lu}\n", *offset); 95950aef9f6SVitaliy Gusev xo_close_instance_h(xop, JSON_KERNEL_ARR_KEY); 960483d953aSJohn Baldwin 961483d953aSJohn Baldwin *offset += data_size; 962483d953aSJohn Baldwin 963483d953aSJohn Baldwin done: 964483d953aSJohn Baldwin return (ret); 965483d953aSJohn Baldwin } 966483d953aSJohn Baldwin 967483d953aSJohn Baldwin static int 968ee5023f3SVitaliy Gusev vm_save_kern_structs(struct vmctx *ctx, int data_fd, xo_handle_t *xop) 969483d953aSJohn Baldwin { 970c9faf698SMark Johnston int ret, error; 971c9faf698SMark Johnston size_t buf_size, i, offset; 972483d953aSJohn Baldwin char *buffer; 973483d953aSJohn Baldwin struct vm_snapshot_meta *meta; 974483d953aSJohn Baldwin 975483d953aSJohn Baldwin error = 0; 976483d953aSJohn Baldwin offset = 0; 977483d953aSJohn Baldwin buf_size = SNAPSHOT_BUFFER_SIZE; 978483d953aSJohn Baldwin 979483d953aSJohn Baldwin buffer = malloc(SNAPSHOT_BUFFER_SIZE * sizeof(char)); 980483d953aSJohn Baldwin if (buffer == NULL) { 981483d953aSJohn Baldwin error = ENOMEM; 982483d953aSJohn Baldwin perror("Failed to allocate memory for snapshot buffer"); 983483d953aSJohn Baldwin goto err_vm_snapshot_kern_data; 984483d953aSJohn Baldwin } 985483d953aSJohn Baldwin 986483d953aSJohn Baldwin meta = &(struct vm_snapshot_meta) { 987483d953aSJohn Baldwin .buffer.buf_start = buffer, 988483d953aSJohn Baldwin .buffer.buf_size = buf_size, 989483d953aSJohn Baldwin 990483d953aSJohn Baldwin .op = VM_SNAPSHOT_SAVE, 991483d953aSJohn Baldwin }; 992483d953aSJohn Baldwin 99350aef9f6SVitaliy Gusev xo_open_list_h(xop, JSON_KERNEL_ARR_KEY); 994483d953aSJohn Baldwin for (i = 0; i < nitems(snapshot_kern_structs); i++) { 995483d953aSJohn Baldwin meta->dev_name = snapshot_kern_structs[i].struct_name; 996483d953aSJohn Baldwin meta->dev_req = snapshot_kern_structs[i].req; 997483d953aSJohn Baldwin 998483d953aSJohn Baldwin memset(meta->buffer.buf_start, 0, meta->buffer.buf_size); 999483d953aSJohn Baldwin meta->buffer.buf = meta->buffer.buf_start; 1000483d953aSJohn Baldwin meta->buffer.buf_rem = meta->buffer.buf_size; 1001483d953aSJohn Baldwin 1002ee5023f3SVitaliy Gusev ret = vm_save_kern_struct(ctx, data_fd, xop, 10030f735657SJohn Baldwin JSON_DEV_ARR_KEY, meta, &offset); 1004483d953aSJohn Baldwin if (ret != 0) { 1005483d953aSJohn Baldwin error = -1; 1006483d953aSJohn Baldwin goto err_vm_snapshot_kern_data; 1007483d953aSJohn Baldwin } 1008483d953aSJohn Baldwin } 100950aef9f6SVitaliy Gusev xo_close_list_h(xop, JSON_KERNEL_ARR_KEY); 1010483d953aSJohn Baldwin 1011483d953aSJohn Baldwin err_vm_snapshot_kern_data: 1012483d953aSJohn Baldwin if (buffer != NULL) 1013483d953aSJohn Baldwin free(buffer); 1014483d953aSJohn Baldwin return (error); 1015483d953aSJohn Baldwin } 1016483d953aSJohn Baldwin 1017483d953aSJohn Baldwin static int 1018483d953aSJohn Baldwin vm_snapshot_basic_metadata(struct vmctx *ctx, xo_handle_t *xop, size_t memsz) 1019483d953aSJohn Baldwin { 1020483d953aSJohn Baldwin 1021483d953aSJohn Baldwin xo_open_container_h(xop, JSON_BASIC_METADATA_KEY); 1022483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_NCPUS_KEY "/%ld}\n", guest_ncpus); 10233efc45f3SRobert Wing xo_emit_h(xop, "{:" JSON_VMNAME_KEY "/%s}\n", vm_get_name(ctx)); 1024483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_MEMSIZE_KEY "/%lu}\n", memsz); 10253efc45f3SRobert Wing xo_emit_h(xop, "{:" JSON_MEMFLAGS_KEY "/%d}\n", vm_get_memflags(ctx)); 1026483d953aSJohn Baldwin xo_close_container_h(xop, JSON_BASIC_METADATA_KEY); 1027483d953aSJohn Baldwin 10283efc45f3SRobert Wing return (0); 1029483d953aSJohn Baldwin } 1030483d953aSJohn Baldwin 1031483d953aSJohn Baldwin static int 1032483d953aSJohn Baldwin vm_snapshot_dev_write_data(int data_fd, xo_handle_t *xop, const char *array_key, 1033483d953aSJohn Baldwin struct vm_snapshot_meta *meta, off_t *offset) 1034483d953aSJohn Baldwin { 1035ed721684SMark Johnston ssize_t ret; 1036483d953aSJohn Baldwin size_t data_size; 1037483d953aSJohn Baldwin 1038483d953aSJohn Baldwin data_size = vm_get_snapshot_size(meta); 1039483d953aSJohn Baldwin 1040ed721684SMark Johnston /* XXX-MJ no handling for short writes. */ 1041483d953aSJohn Baldwin ret = write(data_fd, meta->buffer.buf_start, data_size); 1042ed721684SMark Johnston if (ret < 0 || (size_t)ret != data_size) { 1043483d953aSJohn Baldwin perror("Failed to write all snapshotted data."); 1044483d953aSJohn Baldwin return (-1); 1045483d953aSJohn Baldwin } 1046483d953aSJohn Baldwin 1047483d953aSJohn Baldwin /* Write metadata. */ 1048483d953aSJohn Baldwin xo_open_instance_h(xop, array_key); 1049483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_SNAPSHOT_REQ_KEY "/%s}\n", meta->dev_name); 1050483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_SIZE_KEY "/%lu}\n", data_size); 1051483d953aSJohn Baldwin xo_emit_h(xop, "{:" JSON_FILE_OFFSET_KEY "/%lu}\n", *offset); 1052483d953aSJohn Baldwin xo_close_instance_h(xop, array_key); 1053483d953aSJohn Baldwin 1054483d953aSJohn Baldwin *offset += data_size; 1055483d953aSJohn Baldwin 1056483d953aSJohn Baldwin return (0); 1057483d953aSJohn Baldwin } 1058483d953aSJohn Baldwin 1059483d953aSJohn Baldwin static int 1060381ef27dSVitaliy Gusev vm_snapshot_device(vm_snapshot_dev_cb func, const char *dev_name, 1061381ef27dSVitaliy Gusev void *devdata, int data_fd, xo_handle_t *xop, 1062483d953aSJohn Baldwin struct vm_snapshot_meta *meta, off_t *offset) 1063483d953aSJohn Baldwin { 1064483d953aSJohn Baldwin int ret; 1065483d953aSJohn Baldwin 1066381ef27dSVitaliy Gusev memset(meta->buffer.buf_start, 0, meta->buffer.buf_size); 1067381ef27dSVitaliy Gusev meta->buffer.buf = meta->buffer.buf_start; 1068381ef27dSVitaliy Gusev meta->buffer.buf_rem = meta->buffer.buf_size; 1069381ef27dSVitaliy Gusev meta->dev_name = dev_name; 1070381ef27dSVitaliy Gusev meta->dev_data = devdata; 1071381ef27dSVitaliy Gusev 1072381ef27dSVitaliy Gusev ret = func(meta); 1073483d953aSJohn Baldwin if (ret != 0) { 1074381ef27dSVitaliy Gusev EPRINTLN("Failed to snapshot %s; ret=%d", dev_name, ret); 1075483d953aSJohn Baldwin return (ret); 1076483d953aSJohn Baldwin } 1077483d953aSJohn Baldwin 1078483d953aSJohn Baldwin ret = vm_snapshot_dev_write_data(data_fd, xop, JSON_DEV_ARR_KEY, meta, 1079483d953aSJohn Baldwin offset); 1080483d953aSJohn Baldwin if (ret != 0) 1081483d953aSJohn Baldwin return (ret); 1082483d953aSJohn Baldwin 1083483d953aSJohn Baldwin return (0); 1084483d953aSJohn Baldwin } 1085483d953aSJohn Baldwin 1086483d953aSJohn Baldwin static int 1087b10d65a4SVitaliy Gusev vm_snapshot_devices(int data_fd, xo_handle_t *xop) 1088483d953aSJohn Baldwin { 1089c9faf698SMark Johnston int ret; 1090483d953aSJohn Baldwin off_t offset; 1091483d953aSJohn Baldwin void *buffer; 1092381ef27dSVitaliy Gusev size_t buf_size; 1093483d953aSJohn Baldwin struct vm_snapshot_meta *meta; 1094381ef27dSVitaliy Gusev struct pci_devinst *pdi; 1095483d953aSJohn Baldwin 1096483d953aSJohn Baldwin buf_size = SNAPSHOT_BUFFER_SIZE; 1097483d953aSJohn Baldwin 1098483d953aSJohn Baldwin offset = lseek(data_fd, 0, SEEK_CUR); 1099483d953aSJohn Baldwin if (offset < 0) { 1100483d953aSJohn Baldwin perror("Failed to get data file current offset."); 1101483d953aSJohn Baldwin return (-1); 1102483d953aSJohn Baldwin } 1103483d953aSJohn Baldwin 1104483d953aSJohn Baldwin buffer = malloc(buf_size); 1105483d953aSJohn Baldwin if (buffer == NULL) { 1106483d953aSJohn Baldwin perror("Failed to allocate memory for snapshot buffer"); 1107483d953aSJohn Baldwin ret = ENOSPC; 1108483d953aSJohn Baldwin goto snapshot_err; 1109483d953aSJohn Baldwin } 1110483d953aSJohn Baldwin 1111483d953aSJohn Baldwin meta = &(struct vm_snapshot_meta) { 1112483d953aSJohn Baldwin .buffer.buf_start = buffer, 1113483d953aSJohn Baldwin .buffer.buf_size = buf_size, 1114483d953aSJohn Baldwin 1115483d953aSJohn Baldwin .op = VM_SNAPSHOT_SAVE, 1116483d953aSJohn Baldwin }; 1117483d953aSJohn Baldwin 1118483d953aSJohn Baldwin xo_open_list_h(xop, JSON_DEV_ARR_KEY); 1119483d953aSJohn Baldwin 1120381ef27dSVitaliy Gusev /* Save PCI devices */ 1121381ef27dSVitaliy Gusev pdi = NULL; 1122381ef27dSVitaliy Gusev while ((pdi = pci_next(pdi)) != NULL) { 1123381ef27dSVitaliy Gusev ret = vm_snapshot_device(pci_snapshot, pdi->pi_name, pdi, 1124381ef27dSVitaliy Gusev data_fd, xop, meta, &offset); 1125483d953aSJohn Baldwin if (ret != 0) 1126483d953aSJohn Baldwin goto snapshot_err; 1127483d953aSJohn Baldwin } 1128483d953aSJohn Baldwin 1129f927afc1SMark Johnston #ifdef __amd64__ 1130381ef27dSVitaliy Gusev ret = vm_snapshot_device(atkbdc_snapshot, "atkbdc", NULL, 1131381ef27dSVitaliy Gusev data_fd, xop, meta, &offset); 1132f927afc1SMark Johnston #else 1133f927afc1SMark Johnston ret = 0; 1134f927afc1SMark Johnston #endif 1135381ef27dSVitaliy Gusev 1136483d953aSJohn Baldwin xo_close_list_h(xop, JSON_DEV_ARR_KEY); 1137483d953aSJohn Baldwin 1138483d953aSJohn Baldwin snapshot_err: 1139483d953aSJohn Baldwin if (buffer != NULL) 1140483d953aSJohn Baldwin free(buffer); 1141483d953aSJohn Baldwin return (ret); 1142483d953aSJohn Baldwin } 1143483d953aSJohn Baldwin 1144483d953aSJohn Baldwin void 1145483d953aSJohn Baldwin checkpoint_cpu_add(int vcpu) 1146483d953aSJohn Baldwin { 1147483d953aSJohn Baldwin 1148483d953aSJohn Baldwin pthread_mutex_lock(&vcpu_lock); 1149483d953aSJohn Baldwin CPU_SET(vcpu, &vcpus_active); 1150483d953aSJohn Baldwin 1151483d953aSJohn Baldwin if (checkpoint_active) { 1152483d953aSJohn Baldwin CPU_SET(vcpu, &vcpus_suspended); 1153483d953aSJohn Baldwin while (checkpoint_active) 1154483d953aSJohn Baldwin pthread_cond_wait(&vcpus_can_run, &vcpu_lock); 1155483d953aSJohn Baldwin CPU_CLR(vcpu, &vcpus_suspended); 1156483d953aSJohn Baldwin } 1157483d953aSJohn Baldwin pthread_mutex_unlock(&vcpu_lock); 1158483d953aSJohn Baldwin } 1159483d953aSJohn Baldwin 1160483d953aSJohn Baldwin /* 1161483d953aSJohn Baldwin * When a vCPU is suspended for any reason, it calls 1162483d953aSJohn Baldwin * checkpoint_cpu_suspend(). This records that the vCPU is idle. 1163483d953aSJohn Baldwin * Before returning from suspension, checkpoint_cpu_resume() is 1164483d953aSJohn Baldwin * called. In suspend we note that the vCPU is idle. In resume we 1165483d953aSJohn Baldwin * pause the vCPU thread until the checkpoint is complete. The reason 1166483d953aSJohn Baldwin * for the two-step process is that vCPUs might already be stopped in 1167483d953aSJohn Baldwin * the debug server when a checkpoint is requested. This approach 1168483d953aSJohn Baldwin * allows us to account for and handle those vCPUs. 1169483d953aSJohn Baldwin */ 1170483d953aSJohn Baldwin void 1171483d953aSJohn Baldwin checkpoint_cpu_suspend(int vcpu) 1172483d953aSJohn Baldwin { 1173483d953aSJohn Baldwin 1174483d953aSJohn Baldwin pthread_mutex_lock(&vcpu_lock); 1175483d953aSJohn Baldwin CPU_SET(vcpu, &vcpus_suspended); 1176483d953aSJohn Baldwin if (checkpoint_active && CPU_CMP(&vcpus_active, &vcpus_suspended) == 0) 1177483d953aSJohn Baldwin pthread_cond_signal(&vcpus_idle); 1178483d953aSJohn Baldwin pthread_mutex_unlock(&vcpu_lock); 1179483d953aSJohn Baldwin } 1180483d953aSJohn Baldwin 1181483d953aSJohn Baldwin void 1182483d953aSJohn Baldwin checkpoint_cpu_resume(int vcpu) 1183483d953aSJohn Baldwin { 1184483d953aSJohn Baldwin 1185483d953aSJohn Baldwin pthread_mutex_lock(&vcpu_lock); 1186483d953aSJohn Baldwin while (checkpoint_active) 1187483d953aSJohn Baldwin pthread_cond_wait(&vcpus_can_run, &vcpu_lock); 1188483d953aSJohn Baldwin CPU_CLR(vcpu, &vcpus_suspended); 1189483d953aSJohn Baldwin pthread_mutex_unlock(&vcpu_lock); 1190483d953aSJohn Baldwin } 1191483d953aSJohn Baldwin 1192483d953aSJohn Baldwin static void 1193483d953aSJohn Baldwin vm_vcpu_pause(struct vmctx *ctx) 1194483d953aSJohn Baldwin { 1195483d953aSJohn Baldwin 1196483d953aSJohn Baldwin pthread_mutex_lock(&vcpu_lock); 1197483d953aSJohn Baldwin checkpoint_active = true; 11987d9ef309SJohn Baldwin vm_suspend_all_cpus(ctx); 1199483d953aSJohn Baldwin while (CPU_CMP(&vcpus_active, &vcpus_suspended) != 0) 1200483d953aSJohn Baldwin pthread_cond_wait(&vcpus_idle, &vcpu_lock); 1201483d953aSJohn Baldwin pthread_mutex_unlock(&vcpu_lock); 1202483d953aSJohn Baldwin } 1203483d953aSJohn Baldwin 1204483d953aSJohn Baldwin static void 1205483d953aSJohn Baldwin vm_vcpu_resume(struct vmctx *ctx) 1206483d953aSJohn Baldwin { 1207483d953aSJohn Baldwin 1208483d953aSJohn Baldwin pthread_mutex_lock(&vcpu_lock); 1209483d953aSJohn Baldwin checkpoint_active = false; 1210483d953aSJohn Baldwin pthread_mutex_unlock(&vcpu_lock); 12117d9ef309SJohn Baldwin vm_resume_all_cpus(ctx); 1212483d953aSJohn Baldwin pthread_cond_broadcast(&vcpus_can_run); 1213483d953aSJohn Baldwin } 1214483d953aSJohn Baldwin 1215483d953aSJohn Baldwin static int 121618126b64SVitaliy Gusev vm_checkpoint(struct vmctx *ctx, int fddir, const char *checkpoint_file, 121718126b64SVitaliy Gusev bool stop_vm) 1218483d953aSJohn Baldwin { 121918126b64SVitaliy Gusev int fd_checkpoint = 0, kdata_fd = 0, fd_meta; 1220483d953aSJohn Baldwin int ret = 0; 1221483d953aSJohn Baldwin int error = 0; 1222483d953aSJohn Baldwin size_t memsz; 1223483d953aSJohn Baldwin xo_handle_t *xop = NULL; 1224483d953aSJohn Baldwin char *meta_filename = NULL; 1225483d953aSJohn Baldwin char *kdata_filename = NULL; 1226483d953aSJohn Baldwin FILE *meta_file = NULL; 1227483d953aSJohn Baldwin 1228483d953aSJohn Baldwin kdata_filename = strcat_extension(checkpoint_file, ".kern"); 1229483d953aSJohn Baldwin if (kdata_filename == NULL) { 1230483d953aSJohn Baldwin fprintf(stderr, "Failed to construct kernel data filename.\n"); 1231483d953aSJohn Baldwin return (-1); 1232483d953aSJohn Baldwin } 1233483d953aSJohn Baldwin 123418126b64SVitaliy Gusev kdata_fd = openat(fddir, kdata_filename, O_WRONLY | O_CREAT | O_TRUNC, 0700); 1235483d953aSJohn Baldwin if (kdata_fd < 0) { 1236483d953aSJohn Baldwin perror("Failed to open kernel data snapshot file."); 1237483d953aSJohn Baldwin error = -1; 1238483d953aSJohn Baldwin goto done; 1239483d953aSJohn Baldwin } 1240483d953aSJohn Baldwin 124118126b64SVitaliy Gusev fd_checkpoint = openat(fddir, checkpoint_file, O_RDWR | O_CREAT | O_TRUNC, 0700); 1242483d953aSJohn Baldwin 1243483d953aSJohn Baldwin if (fd_checkpoint < 0) { 1244483d953aSJohn Baldwin perror("Failed to create checkpoint file"); 1245483d953aSJohn Baldwin error = -1; 1246483d953aSJohn Baldwin goto done; 1247483d953aSJohn Baldwin } 1248483d953aSJohn Baldwin 1249483d953aSJohn Baldwin meta_filename = strcat_extension(checkpoint_file, ".meta"); 1250483d953aSJohn Baldwin if (meta_filename == NULL) { 1251483d953aSJohn Baldwin fprintf(stderr, "Failed to construct vm metadata filename.\n"); 1252483d953aSJohn Baldwin goto done; 1253483d953aSJohn Baldwin } 1254483d953aSJohn Baldwin 125518126b64SVitaliy Gusev fd_meta = openat(fddir, meta_filename, O_WRONLY | O_CREAT | O_TRUNC, 0700); 125618126b64SVitaliy Gusev if (fd_meta != -1) 125718126b64SVitaliy Gusev meta_file = fdopen(fd_meta, "w"); 1258483d953aSJohn Baldwin if (meta_file == NULL) { 1259483d953aSJohn Baldwin perror("Failed to open vm metadata snapshot file."); 126018126b64SVitaliy Gusev close(fd_meta); 1261483d953aSJohn Baldwin goto done; 1262483d953aSJohn Baldwin } 1263483d953aSJohn Baldwin 1264483d953aSJohn Baldwin xop = xo_create_to_file(meta_file, XO_STYLE_JSON, XOF_PRETTY); 1265483d953aSJohn Baldwin if (xop == NULL) { 1266483d953aSJohn Baldwin perror("Failed to get libxo handle on metadata file."); 1267483d953aSJohn Baldwin goto done; 1268483d953aSJohn Baldwin } 1269483d953aSJohn Baldwin 1270483d953aSJohn Baldwin vm_vcpu_pause(ctx); 1271483d953aSJohn Baldwin 1272b10d65a4SVitaliy Gusev ret = vm_pause_devices(); 1273483d953aSJohn Baldwin if (ret != 0) { 1274483d953aSJohn Baldwin fprintf(stderr, "Could not pause devices\r\n"); 1275483d953aSJohn Baldwin error = ret; 1276483d953aSJohn Baldwin goto done; 1277483d953aSJohn Baldwin } 1278483d953aSJohn Baldwin 1279483d953aSJohn Baldwin memsz = vm_snapshot_mem(ctx, fd_checkpoint, 0, true); 1280483d953aSJohn Baldwin if (memsz == 0) { 1281483d953aSJohn Baldwin perror("Could not write guest memory to file"); 1282483d953aSJohn Baldwin error = -1; 1283483d953aSJohn Baldwin goto done; 1284483d953aSJohn Baldwin } 1285483d953aSJohn Baldwin 1286483d953aSJohn Baldwin ret = vm_snapshot_basic_metadata(ctx, xop, memsz); 1287483d953aSJohn Baldwin if (ret != 0) { 1288483d953aSJohn Baldwin fprintf(stderr, "Failed to snapshot vm basic metadata.\n"); 1289483d953aSJohn Baldwin error = -1; 1290483d953aSJohn Baldwin goto done; 1291483d953aSJohn Baldwin } 1292483d953aSJohn Baldwin 1293ee5023f3SVitaliy Gusev ret = vm_save_kern_structs(ctx, kdata_fd, xop); 1294483d953aSJohn Baldwin if (ret != 0) { 1295483d953aSJohn Baldwin fprintf(stderr, "Failed to snapshot vm kernel data.\n"); 1296483d953aSJohn Baldwin error = -1; 1297483d953aSJohn Baldwin goto done; 1298483d953aSJohn Baldwin } 1299483d953aSJohn Baldwin 1300b10d65a4SVitaliy Gusev ret = vm_snapshot_devices(kdata_fd, xop); 1301483d953aSJohn Baldwin if (ret != 0) { 1302483d953aSJohn Baldwin fprintf(stderr, "Failed to snapshot device state.\n"); 1303483d953aSJohn Baldwin error = -1; 1304483d953aSJohn Baldwin goto done; 1305483d953aSJohn Baldwin } 1306483d953aSJohn Baldwin 1307483d953aSJohn Baldwin xo_finish_h(xop); 1308483d953aSJohn Baldwin 1309483d953aSJohn Baldwin if (stop_vm) { 1310483d953aSJohn Baldwin vm_destroy(ctx); 1311483d953aSJohn Baldwin exit(0); 1312483d953aSJohn Baldwin } 1313483d953aSJohn Baldwin 1314483d953aSJohn Baldwin done: 1315b10d65a4SVitaliy Gusev ret = vm_resume_devices(); 1316483d953aSJohn Baldwin if (ret != 0) 1317483d953aSJohn Baldwin fprintf(stderr, "Could not resume devices\r\n"); 1318483d953aSJohn Baldwin vm_vcpu_resume(ctx); 1319483d953aSJohn Baldwin if (fd_checkpoint > 0) 1320483d953aSJohn Baldwin close(fd_checkpoint); 1321483d953aSJohn Baldwin if (meta_filename != NULL) 1322483d953aSJohn Baldwin free(meta_filename); 1323483d953aSJohn Baldwin if (kdata_filename != NULL) 1324483d953aSJohn Baldwin free(kdata_filename); 1325483d953aSJohn Baldwin if (xop != NULL) 1326483d953aSJohn Baldwin xo_destroy(xop); 1327483d953aSJohn Baldwin if (meta_file != NULL) 1328483d953aSJohn Baldwin fclose(meta_file); 1329483d953aSJohn Baldwin if (kdata_fd > 0) 1330483d953aSJohn Baldwin close(kdata_fd); 1331483d953aSJohn Baldwin return (error); 1332483d953aSJohn Baldwin } 1333483d953aSJohn Baldwin 1334edfb339dSRobert Wing static int 1335edfb339dSRobert Wing handle_message(struct vmctx *ctx, nvlist_t *nvl) 1336483d953aSJohn Baldwin { 13374379c1daSRobert Wing const char *cmd; 1338c79331a4SRobert Wing struct ipc_command **ipc_cmd; 1339483d953aSJohn Baldwin 13404379c1daSRobert Wing if (!nvlist_exists_string(nvl, "cmd")) 1341c79331a4SRobert Wing return (EINVAL); 1342edfb339dSRobert Wing 13434379c1daSRobert Wing cmd = nvlist_get_string(nvl, "cmd"); 1344c79331a4SRobert Wing IPC_COMMAND_FOREACH(ipc_cmd, ipc_cmd_set) { 1345c79331a4SRobert Wing if (strcmp(cmd, (*ipc_cmd)->name) == 0) 1346c79331a4SRobert Wing return ((*ipc_cmd)->handler(ctx, nvl)); 1347483d953aSJohn Baldwin } 1348483d953aSJohn Baldwin 1349c79331a4SRobert Wing return (EOPNOTSUPP); 1350483d953aSJohn Baldwin } 1351483d953aSJohn Baldwin 1352483d953aSJohn Baldwin /* 1353483d953aSJohn Baldwin * Listen for commands from bhyvectl 1354483d953aSJohn Baldwin */ 1355483d953aSJohn Baldwin void * 1356483d953aSJohn Baldwin checkpoint_thread(void *param) 1357483d953aSJohn Baldwin { 1358690b7ea0SRobert Wing int fd; 1359483d953aSJohn Baldwin struct checkpoint_thread_info *thread_info; 1360edfb339dSRobert Wing nvlist_t *nvl; 1361483d953aSJohn Baldwin 1362483d953aSJohn Baldwin pthread_set_name_np(pthread_self(), "checkpoint thread"); 1363483d953aSJohn Baldwin thread_info = (struct checkpoint_thread_info *)param; 1364483d953aSJohn Baldwin 1365690b7ea0SRobert Wing while ((fd = accept(thread_info->socket_fd, NULL, NULL)) != -1) { 1366690b7ea0SRobert Wing nvl = nvlist_recv(fd, 0); 1367edfb339dSRobert Wing if (nvl != NULL) 1368edfb339dSRobert Wing handle_message(thread_info->ctx, nvl); 136938dfb062SRobert Wing else 1370edfb339dSRobert Wing EPRINTLN("nvlist_recv() failed: %s", strerror(errno)); 1371c79331a4SRobert Wing 1372690b7ea0SRobert Wing close(fd); 1373c79331a4SRobert Wing nvlist_destroy(nvl); 1374483d953aSJohn Baldwin } 1375483d953aSJohn Baldwin 1376483d953aSJohn Baldwin return (NULL); 1377483d953aSJohn Baldwin } 1378483d953aSJohn Baldwin 1379c79331a4SRobert Wing static int 1380c79331a4SRobert Wing vm_do_checkpoint(struct vmctx *ctx, const nvlist_t *nvl) 1381c79331a4SRobert Wing { 1382c79331a4SRobert Wing int error; 1383c79331a4SRobert Wing 1384c79331a4SRobert Wing if (!nvlist_exists_string(nvl, "filename") || 138518126b64SVitaliy Gusev !nvlist_exists_bool(nvl, "suspend") || 138618126b64SVitaliy Gusev !nvlist_exists_descriptor(nvl, "fddir")) 1387c79331a4SRobert Wing error = EINVAL; 1388c79331a4SRobert Wing else 138918126b64SVitaliy Gusev error = vm_checkpoint(ctx, 139018126b64SVitaliy Gusev nvlist_get_descriptor(nvl, "fddir"), 139118126b64SVitaliy Gusev nvlist_get_string(nvl, "filename"), 1392c79331a4SRobert Wing nvlist_get_bool(nvl, "suspend")); 1393c79331a4SRobert Wing 1394c79331a4SRobert Wing return (error); 1395c79331a4SRobert Wing } 1396c79331a4SRobert Wing IPC_COMMAND(ipc_cmd_set, checkpoint, vm_do_checkpoint); 1397c79331a4SRobert Wing 1398483d953aSJohn Baldwin /* 1399483d953aSJohn Baldwin * Create the listening socket for IPC with bhyvectl 1400483d953aSJohn Baldwin */ 1401483d953aSJohn Baldwin int 1402483d953aSJohn Baldwin init_checkpoint_thread(struct vmctx *ctx) 1403483d953aSJohn Baldwin { 1404483d953aSJohn Baldwin struct checkpoint_thread_info *checkpoint_info = NULL; 1405483d953aSJohn Baldwin struct sockaddr_un addr; 1406483d953aSJohn Baldwin int socket_fd; 1407483d953aSJohn Baldwin pthread_t checkpoint_pthread; 140851fbd894SRobert Wing int err; 1409577ddca9SVitaliy Gusev #ifndef WITHOUT_CAPSICUM 1410577ddca9SVitaliy Gusev cap_rights_t rights; 1411577ddca9SVitaliy Gusev #endif 1412483d953aSJohn Baldwin 1413483d953aSJohn Baldwin memset(&addr, 0, sizeof(addr)); 1414483d953aSJohn Baldwin 1415690b7ea0SRobert Wing socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); 1416483d953aSJohn Baldwin if (socket_fd < 0) { 141738dfb062SRobert Wing EPRINTLN("Socket creation failed: %s", strerror(errno)); 1418483d953aSJohn Baldwin err = -1; 1419483d953aSJohn Baldwin goto fail; 1420483d953aSJohn Baldwin } 1421483d953aSJohn Baldwin 1422483d953aSJohn Baldwin addr.sun_family = AF_UNIX; 1423483d953aSJohn Baldwin 14245ce2d4a1SRobert Wing snprintf(addr.sun_path, sizeof(addr.sun_path), "%s%s", 14253efc45f3SRobert Wing BHYVE_RUN_DIR, vm_get_name(ctx)); 1426483d953aSJohn Baldwin addr.sun_len = SUN_LEN(&addr); 1427483d953aSJohn Baldwin unlink(addr.sun_path); 1428483d953aSJohn Baldwin 1429483d953aSJohn Baldwin if (bind(socket_fd, (struct sockaddr *)&addr, addr.sun_len) != 0) { 143038dfb062SRobert Wing EPRINTLN("Failed to bind socket \"%s\": %s\n", 143138dfb062SRobert Wing addr.sun_path, strerror(errno)); 1432483d953aSJohn Baldwin err = -1; 1433483d953aSJohn Baldwin goto fail; 1434483d953aSJohn Baldwin } 1435483d953aSJohn Baldwin 1436690b7ea0SRobert Wing if (listen(socket_fd, 10) < 0) { 1437690b7ea0SRobert Wing EPRINTLN("ipc socket listen: %s\n", strerror(errno)); 1438690b7ea0SRobert Wing err = errno; 1439690b7ea0SRobert Wing goto fail; 1440690b7ea0SRobert Wing } 1441690b7ea0SRobert Wing 1442577ddca9SVitaliy Gusev #ifndef WITHOUT_CAPSICUM 1443577ddca9SVitaliy Gusev cap_rights_init(&rights, CAP_ACCEPT, CAP_READ, CAP_RECV, CAP_WRITE, 1444577ddca9SVitaliy Gusev CAP_SEND, CAP_GETSOCKOPT); 1445577ddca9SVitaliy Gusev 1446577ddca9SVitaliy Gusev if (caph_rights_limit(socket_fd, &rights) == -1) 1447577ddca9SVitaliy Gusev errx(EX_OSERR, "Unable to apply rights for sandbox"); 1448577ddca9SVitaliy Gusev #endif 1449483d953aSJohn Baldwin checkpoint_info = calloc(1, sizeof(*checkpoint_info)); 1450483d953aSJohn Baldwin checkpoint_info->ctx = ctx; 1451483d953aSJohn Baldwin checkpoint_info->socket_fd = socket_fd; 1452483d953aSJohn Baldwin 145351fbd894SRobert Wing err = pthread_create(&checkpoint_pthread, NULL, checkpoint_thread, 1454483d953aSJohn Baldwin checkpoint_info); 145551fbd894SRobert Wing if (err != 0) 1456483d953aSJohn Baldwin goto fail; 1457483d953aSJohn Baldwin 1458483d953aSJohn Baldwin return (0); 1459483d953aSJohn Baldwin fail: 1460483d953aSJohn Baldwin free(checkpoint_info); 1461483d953aSJohn Baldwin if (socket_fd > 0) 1462483d953aSJohn Baldwin close(socket_fd); 1463483d953aSJohn Baldwin unlink(addr.sun_path); 1464483d953aSJohn Baldwin 1465483d953aSJohn Baldwin return (err); 1466483d953aSJohn Baldwin } 1467483d953aSJohn Baldwin 1468483d953aSJohn Baldwin void 1469483d953aSJohn Baldwin vm_snapshot_buf_err(const char *bufname, const enum vm_snapshot_op op) 1470483d953aSJohn Baldwin { 1471483d953aSJohn Baldwin const char *__op; 1472483d953aSJohn Baldwin 1473483d953aSJohn Baldwin if (op == VM_SNAPSHOT_SAVE) 1474483d953aSJohn Baldwin __op = "save"; 1475483d953aSJohn Baldwin else if (op == VM_SNAPSHOT_RESTORE) 1476483d953aSJohn Baldwin __op = "restore"; 1477483d953aSJohn Baldwin else 1478483d953aSJohn Baldwin __op = "unknown"; 1479483d953aSJohn Baldwin 1480483d953aSJohn Baldwin fprintf(stderr, "%s: snapshot-%s failed for %s\r\n", 1481483d953aSJohn Baldwin __func__, __op, bufname); 1482483d953aSJohn Baldwin } 1483483d953aSJohn Baldwin 1484483d953aSJohn Baldwin int 14858b1adff8SMark Johnston vm_snapshot_buf(void *data, size_t data_size, struct vm_snapshot_meta *meta) 1486483d953aSJohn Baldwin { 1487483d953aSJohn Baldwin struct vm_snapshot_buffer *buffer; 1488483d953aSJohn Baldwin int op; 1489483d953aSJohn Baldwin 1490483d953aSJohn Baldwin buffer = &meta->buffer; 1491483d953aSJohn Baldwin op = meta->op; 1492483d953aSJohn Baldwin 1493483d953aSJohn Baldwin if (buffer->buf_rem < data_size) { 1494483d953aSJohn Baldwin fprintf(stderr, "%s: buffer too small\r\n", __func__); 1495483d953aSJohn Baldwin return (E2BIG); 1496483d953aSJohn Baldwin } 1497483d953aSJohn Baldwin 1498483d953aSJohn Baldwin if (op == VM_SNAPSHOT_SAVE) 14998b1adff8SMark Johnston memcpy(buffer->buf, data, data_size); 1500483d953aSJohn Baldwin else if (op == VM_SNAPSHOT_RESTORE) 15018b1adff8SMark Johnston memcpy(data, buffer->buf, data_size); 1502483d953aSJohn Baldwin else 1503483d953aSJohn Baldwin return (EINVAL); 1504483d953aSJohn Baldwin 1505483d953aSJohn Baldwin buffer->buf += data_size; 1506483d953aSJohn Baldwin buffer->buf_rem -= data_size; 1507483d953aSJohn Baldwin 1508483d953aSJohn Baldwin return (0); 1509483d953aSJohn Baldwin } 1510483d953aSJohn Baldwin 1511483d953aSJohn Baldwin size_t 1512483d953aSJohn Baldwin vm_get_snapshot_size(struct vm_snapshot_meta *meta) 1513483d953aSJohn Baldwin { 1514483d953aSJohn Baldwin size_t length; 1515483d953aSJohn Baldwin struct vm_snapshot_buffer *buffer; 1516483d953aSJohn Baldwin 1517483d953aSJohn Baldwin buffer = &meta->buffer; 1518483d953aSJohn Baldwin 1519483d953aSJohn Baldwin if (buffer->buf_size < buffer->buf_rem) { 1520483d953aSJohn Baldwin fprintf(stderr, "%s: Invalid buffer: size = %zu, rem = %zu\r\n", 1521483d953aSJohn Baldwin __func__, buffer->buf_size, buffer->buf_rem); 1522483d953aSJohn Baldwin length = 0; 1523483d953aSJohn Baldwin } else { 1524483d953aSJohn Baldwin length = buffer->buf_size - buffer->buf_rem; 1525483d953aSJohn Baldwin } 1526483d953aSJohn Baldwin 1527483d953aSJohn Baldwin return (length); 1528483d953aSJohn Baldwin } 1529483d953aSJohn Baldwin 1530483d953aSJohn Baldwin int 15310f735657SJohn Baldwin vm_snapshot_guest2host_addr(struct vmctx *ctx, void **addrp, size_t len, 15320f735657SJohn Baldwin bool restore_null, struct vm_snapshot_meta *meta) 1533483d953aSJohn Baldwin { 1534483d953aSJohn Baldwin int ret; 1535483d953aSJohn Baldwin vm_paddr_t gaddr; 1536483d953aSJohn Baldwin 1537483d953aSJohn Baldwin if (meta->op == VM_SNAPSHOT_SAVE) { 15380f735657SJohn Baldwin gaddr = paddr_host2guest(ctx, *addrp); 1539483d953aSJohn Baldwin if (gaddr == (vm_paddr_t) -1) { 1540483d953aSJohn Baldwin if (!restore_null || 1541483d953aSJohn Baldwin (restore_null && (*addrp != NULL))) { 1542483d953aSJohn Baldwin ret = EFAULT; 1543483d953aSJohn Baldwin goto done; 1544483d953aSJohn Baldwin } 1545483d953aSJohn Baldwin } 1546483d953aSJohn Baldwin 1547483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(gaddr, meta, ret, done); 1548483d953aSJohn Baldwin } else if (meta->op == VM_SNAPSHOT_RESTORE) { 1549483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(gaddr, meta, ret, done); 1550483d953aSJohn Baldwin if (gaddr == (vm_paddr_t) -1) { 1551483d953aSJohn Baldwin if (!restore_null) { 1552483d953aSJohn Baldwin ret = EFAULT; 1553483d953aSJohn Baldwin goto done; 1554483d953aSJohn Baldwin } 1555483d953aSJohn Baldwin } 1556483d953aSJohn Baldwin 15570f735657SJohn Baldwin *addrp = paddr_guest2host(ctx, gaddr, len); 1558483d953aSJohn Baldwin } else { 1559483d953aSJohn Baldwin ret = EINVAL; 1560483d953aSJohn Baldwin } 1561483d953aSJohn Baldwin 1562483d953aSJohn Baldwin done: 1563483d953aSJohn Baldwin return (ret); 1564483d953aSJohn Baldwin } 1565483d953aSJohn Baldwin 1566483d953aSJohn Baldwin int 15678b1adff8SMark Johnston vm_snapshot_buf_cmp(void *data, size_t data_size, struct vm_snapshot_meta *meta) 1568483d953aSJohn Baldwin { 1569483d953aSJohn Baldwin struct vm_snapshot_buffer *buffer; 1570483d953aSJohn Baldwin int op; 1571483d953aSJohn Baldwin int ret; 1572483d953aSJohn Baldwin 1573483d953aSJohn Baldwin buffer = &meta->buffer; 1574483d953aSJohn Baldwin op = meta->op; 1575483d953aSJohn Baldwin 1576483d953aSJohn Baldwin if (buffer->buf_rem < data_size) { 1577483d953aSJohn Baldwin fprintf(stderr, "%s: buffer too small\r\n", __func__); 1578483d953aSJohn Baldwin ret = E2BIG; 1579483d953aSJohn Baldwin goto done; 1580483d953aSJohn Baldwin } 1581483d953aSJohn Baldwin 1582483d953aSJohn Baldwin if (op == VM_SNAPSHOT_SAVE) { 1583483d953aSJohn Baldwin ret = 0; 15848b1adff8SMark Johnston memcpy(buffer->buf, data, data_size); 1585483d953aSJohn Baldwin } else if (op == VM_SNAPSHOT_RESTORE) { 15868b1adff8SMark Johnston ret = memcmp(data, buffer->buf, data_size); 1587483d953aSJohn Baldwin } else { 1588483d953aSJohn Baldwin ret = EINVAL; 1589483d953aSJohn Baldwin goto done; 1590483d953aSJohn Baldwin } 1591483d953aSJohn Baldwin 1592483d953aSJohn Baldwin buffer->buf += data_size; 1593483d953aSJohn Baldwin buffer->buf_rem -= data_size; 1594483d953aSJohn Baldwin 1595483d953aSJohn Baldwin done: 1596483d953aSJohn Baldwin return (ret); 1597483d953aSJohn Baldwin } 1598