1 /* 2 * Global State configuration 3 * 4 * Copyright (c) 2014-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/cutils.h" 15 #include "qemu/error-report.h" 16 #include "sysemu/runstate.h" 17 #include "qapi/error.h" 18 #include "migration.h" 19 #include "migration/global_state.h" 20 #include "migration/vmstate.h" 21 #include "trace.h" 22 23 typedef struct { 24 uint32_t size; 25 26 /* 27 * runstate was 100 bytes, zero padded, but we trimmed it to add a 28 * few fields and maintain backwards compatibility. 29 */ 30 uint8_t runstate[32]; 31 uint8_t has_vm_was_suspended; 32 uint8_t vm_was_suspended; 33 uint8_t unused[66]; 34 35 RunState state; 36 bool received; 37 } GlobalState; 38 39 static GlobalState global_state; 40 41 static void global_state_do_store(RunState state) 42 { 43 const char *state_str = RunState_str(state); 44 assert(strlen(state_str) < sizeof(global_state.runstate)); 45 strpadcpy((char *)global_state.runstate, sizeof(global_state.runstate), 46 state_str, '\0'); 47 global_state.has_vm_was_suspended = true; 48 global_state.vm_was_suspended = vm_get_suspended(); 49 50 memset(global_state.unused, 0, sizeof(global_state.unused)); 51 } 52 53 void global_state_store(void) 54 { 55 global_state_do_store(runstate_get()); 56 } 57 58 void global_state_store_running(void) 59 { 60 global_state_do_store(RUN_STATE_RUNNING); 61 } 62 63 bool global_state_received(void) 64 { 65 return global_state.received; 66 } 67 68 RunState global_state_get_runstate(void) 69 { 70 return global_state.state; 71 } 72 73 static bool global_state_needed(void *opaque) 74 { 75 return migrate_get_current()->store_global_state; 76 } 77 78 static int global_state_post_load(void *opaque, int version_id) 79 { 80 GlobalState *s = opaque; 81 Error *local_err = NULL; 82 int r; 83 char *runstate = (char *)s->runstate; 84 85 s->received = true; 86 trace_migrate_global_state_post_load(runstate); 87 88 if (strnlen((char *)s->runstate, 89 sizeof(s->runstate)) == sizeof(s->runstate)) { 90 /* 91 * This condition should never happen during migration, because 92 * all runstate names are shorter than 32 bytes (the size of 93 * s->runstate). However, a malicious stream could overflow 94 * the qapi_enum_parse() call, so we force the last character 95 * to a NUL byte. 96 */ 97 s->runstate[sizeof(s->runstate) - 1] = '\0'; 98 } 99 r = qapi_enum_parse(&RunState_lookup, runstate, -1, &local_err); 100 101 if (r == -1) { 102 if (local_err) { 103 error_report_err(local_err); 104 } 105 return -EINVAL; 106 } 107 s->state = r; 108 109 /* 110 * global_state is saved on the outgoing side before forcing a stopped 111 * state, so it may have saved state=suspended and vm_was_suspended=0. 112 * Now we are in a paused state, and when we later call vm_start, it must 113 * restore the suspended state, so we must set vm_was_suspended=1 here. 114 */ 115 vm_set_suspended(s->vm_was_suspended || r == RUN_STATE_SUSPENDED); 116 117 return 0; 118 } 119 120 static int global_state_pre_save(void *opaque) 121 { 122 GlobalState *s = opaque; 123 124 trace_migrate_global_state_pre_save((char *)s->runstate); 125 s->size = strnlen((char *)s->runstate, sizeof(s->runstate)) + 1; 126 assert(s->size <= sizeof(s->runstate)); 127 128 return 0; 129 } 130 131 static const VMStateDescription vmstate_globalstate = { 132 .name = "globalstate", 133 .version_id = 1, 134 .minimum_version_id = 1, 135 .post_load = global_state_post_load, 136 .pre_save = global_state_pre_save, 137 .needed = global_state_needed, 138 .fields = (const VMStateField[]) { 139 VMSTATE_UINT32(size, GlobalState), 140 VMSTATE_BUFFER(runstate, GlobalState), 141 VMSTATE_UINT8(has_vm_was_suspended, GlobalState), 142 VMSTATE_UINT8(vm_was_suspended, GlobalState), 143 VMSTATE_BUFFER(unused, GlobalState), 144 VMSTATE_END_OF_LIST() 145 }, 146 }; 147 148 void register_global_state(void) 149 { 150 /* We would use it independently that we receive it */ 151 strcpy((char *)&global_state.runstate, ""); 152 global_state.received = false; 153 vmstate_register(NULL, 0, &vmstate_globalstate, &global_state); 154 } 155