/* * Global State configuration * * Copyright (c) 2014-2017 Red Hat Inc * * Authors: * Juan Quintela * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qemu/error-report.h" #include "sysemu/runstate.h" #include "qapi/error.h" #include "migration.h" #include "migration/global_state.h" #include "migration/vmstate.h" #include "trace.h" typedef struct { uint32_t size; /* * runstate was 100 bytes, zero padded, but we trimmed it to add a * few fields and maintain backwards compatibility. */ uint8_t runstate[32]; uint8_t has_vm_was_suspended; uint8_t vm_was_suspended; uint8_t unused[66]; RunState state; bool received; } GlobalState; static GlobalState global_state; static void global_state_do_store(RunState state) { const char *state_str = RunState_str(state); assert(strlen(state_str) < sizeof(global_state.runstate)); strpadcpy((char *)global_state.runstate, sizeof(global_state.runstate), state_str, '\0'); global_state.has_vm_was_suspended = true; global_state.vm_was_suspended = vm_get_suspended(); memset(global_state.unused, 0, sizeof(global_state.unused)); } void global_state_store(void) { global_state_do_store(runstate_get()); } void global_state_store_running(void) { global_state_do_store(RUN_STATE_RUNNING); } bool global_state_received(void) { return global_state.received; } RunState global_state_get_runstate(void) { return global_state.state; } static bool global_state_needed(void *opaque) { return migrate_get_current()->store_global_state; } static int global_state_post_load(void *opaque, int version_id) { GlobalState *s = opaque; Error *local_err = NULL; int r; char *runstate = (char *)s->runstate; s->received = true; trace_migrate_global_state_post_load(runstate); if (strnlen((char *)s->runstate, sizeof(s->runstate)) == sizeof(s->runstate)) { /* * This condition should never happen during migration, because * all runstate names are shorter than 32 bytes (the size of * s->runstate). However, a malicious stream could overflow * the qapi_enum_parse() call, so we force the last character * to a NUL byte. */ s->runstate[sizeof(s->runstate) - 1] = '\0'; } r = qapi_enum_parse(&RunState_lookup, runstate, -1, &local_err); if (r == -1) { if (local_err) { error_report_err(local_err); } return -EINVAL; } s->state = r; /* * global_state is saved on the outgoing side before forcing a stopped * state, so it may have saved state=suspended and vm_was_suspended=0. * Now we are in a paused state, and when we later call vm_start, it must * restore the suspended state, so we must set vm_was_suspended=1 here. */ vm_set_suspended(s->vm_was_suspended || r == RUN_STATE_SUSPENDED); return 0; } static int global_state_pre_save(void *opaque) { GlobalState *s = opaque; trace_migrate_global_state_pre_save((char *)s->runstate); s->size = strnlen((char *)s->runstate, sizeof(s->runstate)) + 1; assert(s->size <= sizeof(s->runstate)); return 0; } static const VMStateDescription vmstate_globalstate = { .name = "globalstate", .version_id = 1, .minimum_version_id = 1, .post_load = global_state_post_load, .pre_save = global_state_pre_save, .needed = global_state_needed, .fields = (const VMStateField[]) { VMSTATE_UINT32(size, GlobalState), VMSTATE_BUFFER(runstate, GlobalState), VMSTATE_UINT8(has_vm_was_suspended, GlobalState), VMSTATE_UINT8(vm_was_suspended, GlobalState), VMSTATE_BUFFER(unused, GlobalState), VMSTATE_END_OF_LIST() }, }; void register_global_state(void) { /* We would use it independently that we receive it */ strcpy((char *)&global_state.runstate, ""); global_state.received = false; vmstate_register(NULL, 0, &vmstate_globalstate, &global_state); }