mirror of https://github.com/xqemu/xqemu.git
New VMstate save/load infrastructure
This patch introduces VMState infrastructure, to convert the save/load functions of devices to a table approach. This new approach has the following advantages: - it is type-safe - you can't have load/save functions out of sync - will allows us to have new interesting commands, like dump <device>, that shows all its internal state. - Just now, the only added type is arrays, but we can add structures. - Uses old load_state() function for loading old state. Signed-off-by: Juan Quintela <quintela@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
parent
4082be4df4
commit
9ed7d6ae0f
102
hw/hw.h
102
hw/hw.h
|
@ -270,4 +270,106 @@ typedef int QEMUBootSetHandler(void *opaque, const char *boot_devices);
|
||||||
void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque);
|
void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque);
|
||||||
int qemu_boot_set(const char *boot_devices);
|
int qemu_boot_set(const char *boot_devices);
|
||||||
|
|
||||||
|
typedef struct VMStateInfo VMStateInfo;
|
||||||
|
typedef struct VMStateDescription VMStateDescription;
|
||||||
|
|
||||||
|
struct VMStateInfo {
|
||||||
|
const char *name;
|
||||||
|
int (*get)(QEMUFile *f, void *pv, size_t size);
|
||||||
|
void (*put)(QEMUFile *f, const void *pv, size_t size);
|
||||||
|
};
|
||||||
|
|
||||||
|
enum VMStateFlags {
|
||||||
|
VMS_SINGLE = 0x001,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *name;
|
||||||
|
size_t offset;
|
||||||
|
size_t size;
|
||||||
|
const VMStateInfo *info;
|
||||||
|
enum VMStateFlags flags;
|
||||||
|
int version_id;
|
||||||
|
} VMStateField;
|
||||||
|
|
||||||
|
struct VMStateDescription {
|
||||||
|
const char *name;
|
||||||
|
int version_id;
|
||||||
|
int minimum_version_id;
|
||||||
|
int minimum_version_id_old;
|
||||||
|
LoadStateHandler *load_state_old;
|
||||||
|
VMStateField *fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const VMStateInfo vmstate_info_int8;
|
||||||
|
extern const VMStateInfo vmstate_info_int16;
|
||||||
|
extern const VMStateInfo vmstate_info_int32;
|
||||||
|
extern const VMStateInfo vmstate_info_int64;
|
||||||
|
|
||||||
|
extern const VMStateInfo vmstate_info_uint8;
|
||||||
|
extern const VMStateInfo vmstate_info_uint16;
|
||||||
|
extern const VMStateInfo vmstate_info_uint32;
|
||||||
|
extern const VMStateInfo vmstate_info_uint64;
|
||||||
|
|
||||||
|
#define VMSTATE_SINGLE(_field, _state, _version, _info, _type) { \
|
||||||
|
.name = (stringify(_field)), \
|
||||||
|
.version_id = (_version), \
|
||||||
|
.size = sizeof(_type), \
|
||||||
|
.info = &(_info), \
|
||||||
|
.flags = VMS_SINGLE, \
|
||||||
|
.offset = offsetof(_state, _field) \
|
||||||
|
+ type_check(_type,typeof_field(_state, _field)) \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* _f : field name
|
||||||
|
_s : struct state name
|
||||||
|
_v : version
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define VMSTATE_INT8_V(_f, _s, _v) \
|
||||||
|
VMSTATE_SINGLE(_f, _s, _v, vmstate_info_int8, int8_t)
|
||||||
|
#define VMSTATE_INT16_V(_f, _s, _v) \
|
||||||
|
VMSTATE_SINGLE(_f, _s, _v, vmstate_info_int16, int16_t)
|
||||||
|
#define VMSTATE_INT32_V(_f, _s, _v) \
|
||||||
|
VMSTATE_SINGLE(_f, _s, _v, vmstate_info_int32, int32_t)
|
||||||
|
#define VMSTATE_INT64_V(_f, _s, _v) \
|
||||||
|
VMSTATE_SINGLE(_f, _s, _v, vmstate_info_int64, int64_t)
|
||||||
|
|
||||||
|
#define VMSTATE_UINT8_V(_f, _s, _v) \
|
||||||
|
VMSTATE_SINGLE(_f, _s, _v, vmstate_info_uint8, uint8_t)
|
||||||
|
#define VMSTATE_UINT16_V(_f, _s, _v) \
|
||||||
|
VMSTATE_SINGLE(_f, _s, _v, vmstate_info_uint16, uint16_t)
|
||||||
|
#define VMSTATE_UINT32_V(_f, _s, _v) \
|
||||||
|
VMSTATE_SINGLE(_f, _s, _v, vmstate_info_uint32, uint32_t)
|
||||||
|
#define VMSTATE_UINT64_V(_f, _s, _v) \
|
||||||
|
VMSTATE_SINGLE(_f, _s, _v, vmstate_info_uint64, uint64_t)
|
||||||
|
|
||||||
|
#define VMSTATE_INT8(_f, _s) \
|
||||||
|
VMSTATE_INT8_V(_f, _s, 0)
|
||||||
|
#define VMSTATE_INT16(_f, _s) \
|
||||||
|
VMSTATE_INT16_V(_f, _s, 0)
|
||||||
|
#define VMSTATE_INT32(_f, _s) \
|
||||||
|
VMSTATE_INT32_V(_f, _s, 0)
|
||||||
|
#define VMSTATE_INT64(_f, _s) \
|
||||||
|
VMSTATE_INT64_V(_f, _s, 0)
|
||||||
|
|
||||||
|
#define VMSTATE_UINT8(_f, _s) \
|
||||||
|
VMSTATE_UINT8_V(_f, _s, 0)
|
||||||
|
#define VMSTATE_UINT16(_f, _s) \
|
||||||
|
VMSTATE_UINT16_V(_f, _s, 0)
|
||||||
|
#define VMSTATE_UINT32(_f, _s) \
|
||||||
|
VMSTATE_UINT32_V(_f, _s, 0)
|
||||||
|
#define VMSTATE_UINT64(_f, _s) \
|
||||||
|
VMSTATE_UINT64_V(_f, _s, 0)
|
||||||
|
|
||||||
|
#define VMSTATE_END_OF_LIST() \
|
||||||
|
{}
|
||||||
|
|
||||||
|
extern int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
|
||||||
|
void *opaque, int version_id);
|
||||||
|
extern void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
|
||||||
|
const void *opaque);
|
||||||
|
extern int vmstate_register(int instance_id, const VMStateDescription *vmsd,
|
||||||
|
void *base);
|
||||||
|
extern void vmstate_unregister(const char *idstr, void *opaque);
|
||||||
#endif
|
#endif
|
||||||
|
|
273
savevm.c
273
savevm.c
|
@ -639,6 +639,174 @@ uint64_t qemu_get_be64(QEMUFile *f)
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 8 bit int */
|
||||||
|
|
||||||
|
static int get_int8(QEMUFile *f, void *pv, size_t size)
|
||||||
|
{
|
||||||
|
int8_t *v = pv;
|
||||||
|
qemu_get_s8s(f, v);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_int8(QEMUFile *f, const void *pv, size_t size)
|
||||||
|
{
|
||||||
|
const int8_t *v = pv;
|
||||||
|
qemu_put_s8s(f, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
const VMStateInfo vmstate_info_int8 = {
|
||||||
|
.name = "int8",
|
||||||
|
.get = get_int8,
|
||||||
|
.put = put_int8,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 16 bit int */
|
||||||
|
|
||||||
|
static int get_int16(QEMUFile *f, void *pv, size_t size)
|
||||||
|
{
|
||||||
|
int16_t *v = pv;
|
||||||
|
qemu_get_sbe16s(f, v);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_int16(QEMUFile *f, const void *pv, size_t size)
|
||||||
|
{
|
||||||
|
const int16_t *v = pv;
|
||||||
|
qemu_put_sbe16s(f, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
const VMStateInfo vmstate_info_int16 = {
|
||||||
|
.name = "int16",
|
||||||
|
.get = get_int16,
|
||||||
|
.put = put_int16,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 32 bit int */
|
||||||
|
|
||||||
|
static int get_int32(QEMUFile *f, void *pv, size_t size)
|
||||||
|
{
|
||||||
|
int32_t *v = pv;
|
||||||
|
qemu_get_sbe32s(f, v);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_int32(QEMUFile *f, const void *pv, size_t size)
|
||||||
|
{
|
||||||
|
const int32_t *v = pv;
|
||||||
|
qemu_put_sbe32s(f, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
const VMStateInfo vmstate_info_int32 = {
|
||||||
|
.name = "int32",
|
||||||
|
.get = get_int32,
|
||||||
|
.put = put_int32,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 64 bit int */
|
||||||
|
|
||||||
|
static int get_int64(QEMUFile *f, void *pv, size_t size)
|
||||||
|
{
|
||||||
|
int64_t *v = pv;
|
||||||
|
qemu_get_sbe64s(f, v);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_int64(QEMUFile *f, const void *pv, size_t size)
|
||||||
|
{
|
||||||
|
const int64_t *v = pv;
|
||||||
|
qemu_put_sbe64s(f, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
const VMStateInfo vmstate_info_int64 = {
|
||||||
|
.name = "int64",
|
||||||
|
.get = get_int64,
|
||||||
|
.put = put_int64,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 8 bit unsigned int */
|
||||||
|
|
||||||
|
static int get_uint8(QEMUFile *f, void *pv, size_t size)
|
||||||
|
{
|
||||||
|
uint8_t *v = pv;
|
||||||
|
qemu_get_8s(f, v);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_uint8(QEMUFile *f, const void *pv, size_t size)
|
||||||
|
{
|
||||||
|
const uint8_t *v = pv;
|
||||||
|
qemu_put_8s(f, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
const VMStateInfo vmstate_info_uint8 = {
|
||||||
|
.name = "uint8",
|
||||||
|
.get = get_uint8,
|
||||||
|
.put = put_uint8,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 16 bit unsigned int */
|
||||||
|
|
||||||
|
static int get_uint16(QEMUFile *f, void *pv, size_t size)
|
||||||
|
{
|
||||||
|
uint16_t *v = pv;
|
||||||
|
qemu_get_be16s(f, v);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_uint16(QEMUFile *f, const void *pv, size_t size)
|
||||||
|
{
|
||||||
|
const uint16_t *v = pv;
|
||||||
|
qemu_put_be16s(f, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
const VMStateInfo vmstate_info_uint16 = {
|
||||||
|
.name = "uint16",
|
||||||
|
.get = get_uint16,
|
||||||
|
.put = put_uint16,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 32 bit unsigned int */
|
||||||
|
|
||||||
|
static int get_uint32(QEMUFile *f, void *pv, size_t size)
|
||||||
|
{
|
||||||
|
uint32_t *v = pv;
|
||||||
|
qemu_get_be32s(f, v);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_uint32(QEMUFile *f, const void *pv, size_t size)
|
||||||
|
{
|
||||||
|
const uint32_t *v = pv;
|
||||||
|
qemu_put_be32s(f, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
const VMStateInfo vmstate_info_uint32 = {
|
||||||
|
.name = "uint32",
|
||||||
|
.get = get_uint32,
|
||||||
|
.put = put_uint32,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 64 bit unsigned int */
|
||||||
|
|
||||||
|
static int get_uint64(QEMUFile *f, void *pv, size_t size)
|
||||||
|
{
|
||||||
|
uint64_t *v = pv;
|
||||||
|
qemu_get_be64s(f, v);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_uint64(QEMUFile *f, const void *pv, size_t size)
|
||||||
|
{
|
||||||
|
const uint64_t *v = pv;
|
||||||
|
qemu_put_be64s(f, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
const VMStateInfo vmstate_info_uint64 = {
|
||||||
|
.name = "uint64",
|
||||||
|
.get = get_uint64,
|
||||||
|
.put = put_uint64,
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct SaveStateEntry {
|
typedef struct SaveStateEntry {
|
||||||
char idstr[256];
|
char idstr[256];
|
||||||
int instance_id;
|
int instance_id;
|
||||||
|
@ -647,11 +815,13 @@ typedef struct SaveStateEntry {
|
||||||
SaveLiveStateHandler *save_live_state;
|
SaveLiveStateHandler *save_live_state;
|
||||||
SaveStateHandler *save_state;
|
SaveStateHandler *save_state;
|
||||||
LoadStateHandler *load_state;
|
LoadStateHandler *load_state;
|
||||||
|
const VMStateDescription *vmsd;
|
||||||
void *opaque;
|
void *opaque;
|
||||||
struct SaveStateEntry *next;
|
struct SaveStateEntry *next;
|
||||||
} SaveStateEntry;
|
} SaveStateEntry;
|
||||||
|
|
||||||
static SaveStateEntry *first_se;
|
static SaveStateEntry *first_se;
|
||||||
|
static int global_section_id;
|
||||||
|
|
||||||
/* TODO: Individual devices generally have very little idea about the rest
|
/* TODO: Individual devices generally have very little idea about the rest
|
||||||
of the system, so instance_id should be removed/replaced.
|
of the system, so instance_id should be removed/replaced.
|
||||||
|
@ -666,7 +836,6 @@ int register_savevm_live(const char *idstr,
|
||||||
void *opaque)
|
void *opaque)
|
||||||
{
|
{
|
||||||
SaveStateEntry *se, **pse;
|
SaveStateEntry *se, **pse;
|
||||||
static int global_section_id;
|
|
||||||
|
|
||||||
se = qemu_malloc(sizeof(SaveStateEntry));
|
se = qemu_malloc(sizeof(SaveStateEntry));
|
||||||
pstrcpy(se->idstr, sizeof(se->idstr), idstr);
|
pstrcpy(se->idstr, sizeof(se->idstr), idstr);
|
||||||
|
@ -677,6 +846,7 @@ int register_savevm_live(const char *idstr,
|
||||||
se->save_state = save_state;
|
se->save_state = save_state;
|
||||||
se->load_state = load_state;
|
se->load_state = load_state;
|
||||||
se->opaque = opaque;
|
se->opaque = opaque;
|
||||||
|
se->vmsd = NULL;
|
||||||
se->next = NULL;
|
se->next = NULL;
|
||||||
|
|
||||||
/* add at the end of list */
|
/* add at the end of list */
|
||||||
|
@ -719,17 +889,110 @@ void unregister_savevm(const char *idstr, void *opaque)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int vmstate_register(int instance_id, const VMStateDescription *vmsd,
|
||||||
|
void *opaque)
|
||||||
|
{
|
||||||
|
SaveStateEntry *se, **pse;
|
||||||
|
|
||||||
|
se = qemu_malloc(sizeof(SaveStateEntry));
|
||||||
|
pstrcpy(se->idstr, sizeof(se->idstr), vmsd->name);
|
||||||
|
se->instance_id = (instance_id == -1) ? 0 : instance_id;
|
||||||
|
se->version_id = vmsd->version_id;
|
||||||
|
se->section_id = global_section_id++;
|
||||||
|
se->save_live_state = NULL;
|
||||||
|
se->save_state = NULL;
|
||||||
|
se->load_state = NULL;
|
||||||
|
se->opaque = opaque;
|
||||||
|
se->vmsd = vmsd;
|
||||||
|
se->next = NULL;
|
||||||
|
|
||||||
|
/* add at the end of list */
|
||||||
|
pse = &first_se;
|
||||||
|
while (*pse != NULL) {
|
||||||
|
if (instance_id == -1
|
||||||
|
&& strcmp(se->idstr, (*pse)->idstr) == 0
|
||||||
|
&& se->instance_id <= (*pse)->instance_id)
|
||||||
|
se->instance_id = (*pse)->instance_id + 1;
|
||||||
|
pse = &(*pse)->next;
|
||||||
|
}
|
||||||
|
*pse = se;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vmstate_unregister(const char *idstr, void *opaque)
|
||||||
|
{
|
||||||
|
SaveStateEntry **pse;
|
||||||
|
|
||||||
|
pse = &first_se;
|
||||||
|
while (*pse != NULL) {
|
||||||
|
if (strcmp((*pse)->idstr, idstr) == 0 && (*pse)->opaque == opaque) {
|
||||||
|
SaveStateEntry *next = (*pse)->next;
|
||||||
|
qemu_free(*pse);
|
||||||
|
*pse = next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pse = &(*pse)->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
|
||||||
|
void *opaque, int version_id)
|
||||||
|
{
|
||||||
|
VMStateField *field = vmsd->fields;
|
||||||
|
|
||||||
|
if (version_id > vmsd->version_id) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (version_id < vmsd->minimum_version_id_old) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (version_id < vmsd->minimum_version_id) {
|
||||||
|
return vmsd->load_state_old(f, opaque, version_id);
|
||||||
|
}
|
||||||
|
while(field->name) {
|
||||||
|
if (field->version_id <= version_id) {
|
||||||
|
void *addr = opaque + field->offset;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = field->info->get(f, addr, field->size);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
|
||||||
|
const void *opaque)
|
||||||
|
{
|
||||||
|
VMStateField *field = vmsd->fields;
|
||||||
|
|
||||||
|
while(field->name) {
|
||||||
|
const void *addr = opaque + field->offset;
|
||||||
|
field->info->put(f, addr, field->size);
|
||||||
|
field++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id)
|
static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id)
|
||||||
{
|
{
|
||||||
return se->load_state(f, se->opaque, version_id);
|
if (!se->vmsd) { /* Old style */
|
||||||
|
return se->load_state(f, se->opaque, version_id);
|
||||||
|
}
|
||||||
|
return vmstate_load_state(f, se->vmsd, se->opaque, version_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vmstate_save(QEMUFile *f, SaveStateEntry *se)
|
static void vmstate_save(QEMUFile *f, SaveStateEntry *se)
|
||||||
{
|
{
|
||||||
se->save_state(f, se->opaque);
|
if (!se->vmsd) { /* Old style */
|
||||||
|
se->save_state(f, se->opaque);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vmstate_save_state(f,se->vmsd, se->opaque);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define QEMU_VM_FILE_MAGIC 0x5145564d
|
#define QEMU_VM_FILE_MAGIC 0x5145564d
|
||||||
#define QEMU_VM_FILE_VERSION_COMPAT 0x00000002
|
#define QEMU_VM_FILE_VERSION_COMPAT 0x00000002
|
||||||
#define QEMU_VM_FILE_VERSION 0x00000003
|
#define QEMU_VM_FILE_VERSION 0x00000003
|
||||||
|
@ -817,7 +1080,7 @@ int qemu_savevm_state_complete(QEMUFile *f)
|
||||||
for(se = first_se; se != NULL; se = se->next) {
|
for(se = first_se; se != NULL; se = se->next) {
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
if (se->save_state == NULL)
|
if (se->save_state == NULL && se->vmsd == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Section type */
|
/* Section type */
|
||||||
|
|
Loading…
Reference in New Issue