1 /* 2 * VMState interpreter 3 * 4 * Copyright (c) 2009-2017 Red Hat Inc 5 * 6 * Authors: 7 * Juan Quintela <quintela@redhat.com> 8 * 9 * This work is licensed under the terms of the GNU GPL, version 2 or later. 10 * See the COPYING file in the top-level directory. 11 */ 12 13 #include "qemu/osdep.h" 14 #include "qemu-common.h" 15 #include "migration/migration.h" 16 #include "migration/vmstate.h" 17 #include "qemu-file.h" 18 #include "qemu/bitops.h" 19 #include "qemu/error-report.h" 20 #include "trace.h" 21 #include "qjson.h" 22 23 static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, 24 void *opaque, QJSON *vmdesc); 25 static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, 26 void *opaque); 27 28 static int vmstate_n_elems(void *opaque, VMStateField *field) 29 { 30 int n_elems = 1; 31 32 if (field->flags & VMS_ARRAY) { 33 n_elems = field->num; 34 } else if (field->flags & VMS_VARRAY_INT32) { 35 n_elems = *(int32_t *)(opaque+field->num_offset); 36 } else if (field->flags & VMS_VARRAY_UINT32) { 37 n_elems = *(uint32_t *)(opaque+field->num_offset); 38 } else if (field->flags & VMS_VARRAY_UINT16) { 39 n_elems = *(uint16_t *)(opaque+field->num_offset); 40 } else if (field->flags & VMS_VARRAY_UINT8) { 41 n_elems = *(uint8_t *)(opaque+field->num_offset); 42 } 43 44 if (field->flags & VMS_MULTIPLY_ELEMENTS) { 45 n_elems *= field->num; 46 } 47 48 trace_vmstate_n_elems(field->name, n_elems); 49 return n_elems; 50 } 51 52 static int vmstate_size(void *opaque, VMStateField *field) 53 { 54 int size = field->size; 55 56 if (field->flags & VMS_VBUFFER) { 57 size = *(int32_t *)(opaque+field->size_offset); 58 if (field->flags & VMS_MULTIPLY) { 59 size *= field->size; 60 } 61 } 62 63 return size; 64 } 65 66 static void vmstate_handle_alloc(void *ptr, VMStateField *field, void *opaque) 67 { 68 if (field->flags & VMS_POINTER && field->flags & VMS_ALLOC) { 69 gsize size = vmstate_size(opaque, field); 70 size *= vmstate_n_elems(opaque, field); 71 if (size) { 72 *(void **)ptr = g_malloc(size); 73 } 74 } 75 } 76 77 int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, 78 void *opaque, int version_id) 79 { 80 VMStateField *field = vmsd->fields; 81 int ret = 0; 82 83 trace_vmstate_load_state(vmsd->name, version_id); 84 if (version_id > vmsd->version_id) { 85 error_report("%s: incoming version_id %d is too new " 86 "for local version_id %d", 87 vmsd->name, version_id, vmsd->version_id); 88 trace_vmstate_load_state_end(vmsd->name, "too new", -EINVAL); 89 return -EINVAL; 90 } 91 if (version_id < vmsd->minimum_version_id) { 92 if (vmsd->load_state_old && 93 version_id >= vmsd->minimum_version_id_old) { 94 ret = vmsd->load_state_old(f, opaque, version_id); 95 trace_vmstate_load_state_end(vmsd->name, "old path", ret); 96 return ret; 97 } 98 error_report("%s: incoming version_id %d is too old " 99 "for local minimum version_id %d", 100 vmsd->name, version_id, vmsd->minimum_version_id); 101 trace_vmstate_load_state_end(vmsd->name, "too old", -EINVAL); 102 return -EINVAL; 103 } 104 if (vmsd->pre_load) { 105 int ret = vmsd->pre_load(opaque); 106 if (ret) { 107 return ret; 108 } 109 } 110 while (field->name) { 111 trace_vmstate_load_state_field(vmsd->name, field->name); 112 if ((field->field_exists && 113 field->field_exists(opaque, version_id)) || 114 (!field->field_exists && 115 field->version_id <= version_id)) { 116 void *first_elem = opaque + field->offset; 117 int i, n_elems = vmstate_n_elems(opaque, field); 118 int size = vmstate_size(opaque, field); 119 120 vmstate_handle_alloc(first_elem, field, opaque); 121 if (field->flags & VMS_POINTER) { 122 first_elem = *(void **)first_elem; 123 assert(first_elem || !n_elems || !size); 124 } 125 for (i = 0; i < n_elems; i++) { 126 void *curr_elem = first_elem + size * i; 127 128 if (field->flags & VMS_ARRAY_OF_POINTER) { 129 curr_elem = *(void **)curr_elem; 130 } 131 if (!curr_elem && size) { 132 /* if null pointer check placeholder and do not follow */ 133 assert(field->flags & VMS_ARRAY_OF_POINTER); 134 ret = vmstate_info_nullptr.get(f, curr_elem, size, NULL); 135 } else if (field->flags & VMS_STRUCT) { 136 ret = vmstate_load_state(f, field->vmsd, curr_elem, 137 field->vmsd->version_id); 138 } else { 139 ret = field->info->get(f, curr_elem, size, field); 140 } 141 if (ret >= 0) { 142 ret = qemu_file_get_error(f); 143 } 144 if (ret < 0) { 145 qemu_file_set_error(f, ret); 146 error_report("Failed to load %s:%s", vmsd->name, 147 field->name); 148 trace_vmstate_load_field_error(field->name, ret); 149 return ret; 150 } 151 } 152 } else if (field->flags & VMS_MUST_EXIST) { 153 error_report("Input validation failed: %s/%s", 154 vmsd->name, field->name); 155 return -1; 156 } 157 field++; 158 } 159 ret = vmstate_subsection_load(f, vmsd, opaque); 160 if (ret != 0) { 161 return ret; 162 } 163 if (vmsd->post_load) { 164 ret = vmsd->post_load(opaque, version_id); 165 } 166 trace_vmstate_load_state_end(vmsd->name, "end", ret); 167 return ret; 168 } 169 170 static int vmfield_name_num(VMStateField *start, VMStateField *search) 171 { 172 VMStateField *field; 173 int found = 0; 174 175 for (field = start; field->name; field++) { 176 if (!strcmp(field->name, search->name)) { 177 if (field == search) { 178 return found; 179 } 180 found++; 181 } 182 } 183 184 return -1; 185 } 186 187 static bool vmfield_name_is_unique(VMStateField *start, VMStateField *search) 188 { 189 VMStateField *field; 190 int found = 0; 191 192 for (field = start; field->name; field++) { 193 if (!strcmp(field->name, search->name)) { 194 found++; 195 /* name found more than once, so it's not unique */ 196 if (found > 1) { 197 return false; 198 } 199 } 200 } 201 202 return true; 203 } 204 205 static const char *vmfield_get_type_name(VMStateField *field) 206 { 207 const char *type = "unknown"; 208 209 if (field->flags & VMS_STRUCT) { 210 type = "struct"; 211 } else if (field->info->name) { 212 type = field->info->name; 213 } 214 215 return type; 216 } 217 218 static bool vmsd_can_compress(VMStateField *field) 219 { 220 if (field->field_exists) { 221 /* Dynamically existing fields mess up compression */ 222 return false; 223 } 224 225 if (field->flags & VMS_STRUCT) { 226 VMStateField *sfield = field->vmsd->fields; 227 while (sfield->name) { 228 if (!vmsd_can_compress(sfield)) { 229 /* Child elements can't compress, so can't we */ 230 return false; 231 } 232 sfield++; 233 } 234 235 if (field->vmsd->subsections) { 236 /* Subsections may come and go, better don't compress */ 237 return false; 238 } 239 } 240 241 return true; 242 } 243 244 static void vmsd_desc_field_start(const VMStateDescription *vmsd, QJSON *vmdesc, 245 VMStateField *field, int i, int max) 246 { 247 char *name, *old_name; 248 bool is_array = max > 1; 249 bool can_compress = vmsd_can_compress(field); 250 251 if (!vmdesc) { 252 return; 253 } 254 255 name = g_strdup(field->name); 256 257 /* Field name is not unique, need to make it unique */ 258 if (!vmfield_name_is_unique(vmsd->fields, field)) { 259 int num = vmfield_name_num(vmsd->fields, field); 260 old_name = name; 261 name = g_strdup_printf("%s[%d]", name, num); 262 g_free(old_name); 263 } 264 265 json_start_object(vmdesc, NULL); 266 json_prop_str(vmdesc, "name", name); 267 if (is_array) { 268 if (can_compress) { 269 json_prop_int(vmdesc, "array_len", max); 270 } else { 271 json_prop_int(vmdesc, "index", i); 272 } 273 } 274 json_prop_str(vmdesc, "type", vmfield_get_type_name(field)); 275 276 if (field->flags & VMS_STRUCT) { 277 json_start_object(vmdesc, "struct"); 278 } 279 280 g_free(name); 281 } 282 283 static void vmsd_desc_field_end(const VMStateDescription *vmsd, QJSON *vmdesc, 284 VMStateField *field, size_t size, int i) 285 { 286 if (!vmdesc) { 287 return; 288 } 289 290 if (field->flags & VMS_STRUCT) { 291 /* We printed a struct in between, close its child object */ 292 json_end_object(vmdesc); 293 } 294 295 json_prop_int(vmdesc, "size", size); 296 json_end_object(vmdesc); 297 } 298 299 300 bool vmstate_save_needed(const VMStateDescription *vmsd, void *opaque) 301 { 302 if (vmsd->needed && !vmsd->needed(opaque)) { 303 /* optional section not needed */ 304 return false; 305 } 306 return true; 307 } 308 309 310 void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, 311 void *opaque, QJSON *vmdesc) 312 { 313 VMStateField *field = vmsd->fields; 314 315 trace_vmstate_save_state_top(vmsd->name); 316 317 if (vmsd->pre_save) { 318 vmsd->pre_save(opaque); 319 } 320 321 if (vmdesc) { 322 json_prop_str(vmdesc, "vmsd_name", vmsd->name); 323 json_prop_int(vmdesc, "version", vmsd->version_id); 324 json_start_array(vmdesc, "fields"); 325 } 326 327 while (field->name) { 328 if (!field->field_exists || 329 field->field_exists(opaque, vmsd->version_id)) { 330 void *first_elem = opaque + field->offset; 331 int i, n_elems = vmstate_n_elems(opaque, field); 332 int size = vmstate_size(opaque, field); 333 int64_t old_offset, written_bytes; 334 QJSON *vmdesc_loop = vmdesc; 335 336 trace_vmstate_save_state_loop(vmsd->name, field->name, n_elems); 337 if (field->flags & VMS_POINTER) { 338 first_elem = *(void **)first_elem; 339 assert(first_elem || !n_elems || !size); 340 } 341 for (i = 0; i < n_elems; i++) { 342 void *curr_elem = first_elem + size * i; 343 344 vmsd_desc_field_start(vmsd, vmdesc_loop, field, i, n_elems); 345 old_offset = qemu_ftell_fast(f); 346 if (field->flags & VMS_ARRAY_OF_POINTER) { 347 assert(curr_elem); 348 curr_elem = *(void **)curr_elem; 349 } 350 if (!curr_elem && size) { 351 /* if null pointer write placeholder and do not follow */ 352 assert(field->flags & VMS_ARRAY_OF_POINTER); 353 vmstate_info_nullptr.put(f, curr_elem, size, NULL, NULL); 354 } else if (field->flags & VMS_STRUCT) { 355 vmstate_save_state(f, field->vmsd, curr_elem, vmdesc_loop); 356 } else { 357 field->info->put(f, curr_elem, size, field, vmdesc_loop); 358 } 359 360 written_bytes = qemu_ftell_fast(f) - old_offset; 361 vmsd_desc_field_end(vmsd, vmdesc_loop, field, written_bytes, i); 362 363 /* Compressed arrays only care about the first element */ 364 if (vmdesc_loop && vmsd_can_compress(field)) { 365 vmdesc_loop = NULL; 366 } 367 } 368 } else { 369 if (field->flags & VMS_MUST_EXIST) { 370 error_report("Output state validation failed: %s/%s", 371 vmsd->name, field->name); 372 assert(!(field->flags & VMS_MUST_EXIST)); 373 } 374 } 375 field++; 376 } 377 378 if (vmdesc) { 379 json_end_array(vmdesc); 380 } 381 382 vmstate_subsection_save(f, vmsd, opaque, vmdesc); 383 } 384 385 static const VMStateDescription * 386 vmstate_get_subsection(const VMStateDescription **sub, char *idstr) 387 { 388 while (sub && *sub && (*sub)->needed) { 389 if (strcmp(idstr, (*sub)->name) == 0) { 390 return *sub; 391 } 392 sub++; 393 } 394 return NULL; 395 } 396 397 static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, 398 void *opaque) 399 { 400 trace_vmstate_subsection_load(vmsd->name); 401 402 while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) { 403 char idstr[256], *idstr_ret; 404 int ret; 405 uint8_t version_id, len, size; 406 const VMStateDescription *sub_vmsd; 407 408 len = qemu_peek_byte(f, 1); 409 if (len < strlen(vmsd->name) + 1) { 410 /* subsection name has be be "section_name/a" */ 411 trace_vmstate_subsection_load_bad(vmsd->name, "(short)", ""); 412 return 0; 413 } 414 size = qemu_peek_buffer(f, (uint8_t **)&idstr_ret, len, 2); 415 if (size != len) { 416 trace_vmstate_subsection_load_bad(vmsd->name, "(peek fail)", ""); 417 return 0; 418 } 419 memcpy(idstr, idstr_ret, size); 420 idstr[size] = 0; 421 422 if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) { 423 trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(prefix)"); 424 /* it doesn't have a valid subsection name */ 425 return 0; 426 } 427 sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr); 428 if (sub_vmsd == NULL) { 429 trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(lookup)"); 430 return -ENOENT; 431 } 432 qemu_file_skip(f, 1); /* subsection */ 433 qemu_file_skip(f, 1); /* len */ 434 qemu_file_skip(f, len); /* idstr */ 435 version_id = qemu_get_be32(f); 436 437 ret = vmstate_load_state(f, sub_vmsd, opaque, version_id); 438 if (ret) { 439 trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(child)"); 440 return ret; 441 } 442 } 443 444 trace_vmstate_subsection_load_good(vmsd->name); 445 return 0; 446 } 447 448 static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, 449 void *opaque, QJSON *vmdesc) 450 { 451 const VMStateDescription **sub = vmsd->subsections; 452 bool subsection_found = false; 453 454 trace_vmstate_subsection_save_top(vmsd->name); 455 while (sub && *sub && (*sub)->needed) { 456 if ((*sub)->needed(opaque)) { 457 const VMStateDescription *vmsdsub = *sub; 458 uint8_t len; 459 460 trace_vmstate_subsection_save_loop(vmsd->name, vmsdsub->name); 461 if (vmdesc) { 462 /* Only create subsection array when we have any */ 463 if (!subsection_found) { 464 json_start_array(vmdesc, "subsections"); 465 subsection_found = true; 466 } 467 468 json_start_object(vmdesc, NULL); 469 } 470 471 qemu_put_byte(f, QEMU_VM_SUBSECTION); 472 len = strlen(vmsdsub->name); 473 qemu_put_byte(f, len); 474 qemu_put_buffer(f, (uint8_t *)vmsdsub->name, len); 475 qemu_put_be32(f, vmsdsub->version_id); 476 vmstate_save_state(f, vmsdsub, opaque, vmdesc); 477 478 if (vmdesc) { 479 json_end_object(vmdesc); 480 } 481 } 482 sub++; 483 } 484 485 if (vmdesc && subsection_found) { 486 json_end_array(vmdesc); 487 } 488 } 489