]> git.proxmox.com Git - mirror_qemu.git/blob - slirp/src/vmstate.c
Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging
[mirror_qemu.git] / slirp / src / vmstate.c
1 /*
2 * VMState interpreter
3 *
4 * Copyright (c) 2009-2018 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 #include <assert.h>
13 #include <errno.h>
14 #include <string.h>
15 #include <glib.h>
16
17 #include "stream.h"
18 #include "vmstate.h"
19
20 static int get_nullptr(SlirpIStream *f, void *pv, size_t size,
21 const VMStateField *field)
22 {
23 if (slirp_istream_read_u8(f) == VMS_NULLPTR_MARKER) {
24 return 0;
25 }
26 g_warning("vmstate: get_nullptr expected VMS_NULLPTR_MARKER");
27 return -EINVAL;
28 }
29
30 static int put_nullptr(SlirpOStream *f, void *pv, size_t size,
31 const VMStateField *field)
32
33 {
34 if (pv == NULL) {
35 slirp_ostream_write_u8(f, VMS_NULLPTR_MARKER);
36 return 0;
37 }
38 g_warning("vmstate: put_nullptr must be called with pv == NULL");
39 return -EINVAL;
40 }
41
42 const VMStateInfo slirp_vmstate_info_nullptr = {
43 .name = "uint64",
44 .get = get_nullptr,
45 .put = put_nullptr,
46 };
47
48 /* 8 bit unsigned int */
49
50 static int get_uint8(SlirpIStream *f, void *pv, size_t size, const VMStateField *field)
51 {
52 uint8_t *v = pv;
53 *v = slirp_istream_read_u8(f);
54 return 0;
55 }
56
57 static int put_uint8(SlirpOStream *f, void *pv, size_t size, const VMStateField *field)
58 {
59 uint8_t *v = pv;
60 slirp_ostream_write_u8(f, *v);
61 return 0;
62 }
63
64 const VMStateInfo slirp_vmstate_info_uint8 = {
65 .name = "uint8",
66 .get = get_uint8,
67 .put = put_uint8,
68 };
69
70 /* 16 bit unsigned int */
71
72 static int get_uint16(SlirpIStream *f, void *pv, size_t size,
73 const VMStateField *field)
74 {
75 uint16_t *v = pv;
76 *v = slirp_istream_read_u16(f);
77 return 0;
78 }
79
80 static int put_uint16(SlirpOStream *f, void *pv, size_t size,
81 const VMStateField *field)
82 {
83 uint16_t *v = pv;
84 slirp_ostream_write_u16(f, *v);
85 return 0;
86 }
87
88 const VMStateInfo slirp_vmstate_info_uint16 = {
89 .name = "uint16",
90 .get = get_uint16,
91 .put = put_uint16,
92 };
93
94 /* 32 bit unsigned int */
95
96 static int get_uint32(SlirpIStream *f, void *pv, size_t size,
97 const VMStateField *field)
98 {
99 uint32_t *v = pv;
100 *v = slirp_istream_read_u32(f);
101 return 0;
102 }
103
104 static int put_uint32(SlirpOStream *f, void *pv, size_t size,
105 const VMStateField *field)
106 {
107 uint32_t *v = pv;
108 slirp_ostream_write_u32(f, *v);
109 return 0;
110 }
111
112 const VMStateInfo slirp_vmstate_info_uint32 = {
113 .name = "uint32",
114 .get = get_uint32,
115 .put = put_uint32,
116 };
117
118 /* 16 bit int */
119
120 static int get_int16(SlirpIStream *f, void *pv, size_t size, const VMStateField *field)
121 {
122 int16_t *v = pv;
123 *v = slirp_istream_read_i16(f);
124 return 0;
125 }
126
127 static int put_int16(SlirpOStream *f, void *pv, size_t size, const VMStateField *field)
128 {
129 int16_t *v = pv;
130 slirp_ostream_write_i16(f, *v);
131 return 0;
132 }
133
134 const VMStateInfo slirp_vmstate_info_int16 = {
135 .name = "int16",
136 .get = get_int16,
137 .put = put_int16,
138 };
139
140 /* 32 bit int */
141
142 static int get_int32(SlirpIStream *f, void *pv, size_t size, const VMStateField *field)
143 {
144 int32_t *v = pv;
145 *v = slirp_istream_read_i32(f);
146 return 0;
147 }
148
149 static int put_int32(SlirpOStream *f, void *pv, size_t size, const VMStateField *field)
150 {
151 int32_t *v = pv;
152 slirp_ostream_write_i32(f, *v);
153 return 0;
154 }
155
156 const VMStateInfo slirp_vmstate_info_int32 = {
157 .name = "int32",
158 .get = get_int32,
159 .put = put_int32,
160 };
161
162 /* vmstate_info_tmp, see VMSTATE_WITH_TMP, the idea is that we allocate
163 * a temporary buffer and the pre_load/pre_save methods in the child vmsd
164 * copy stuff from the parent into the child and do calculations to fill
165 * in fields that don't really exist in the parent but need to be in the
166 * stream.
167 */
168 static int get_tmp(SlirpIStream *f, void *pv, size_t size, const VMStateField *field)
169 {
170 int ret;
171 const VMStateDescription *vmsd = field->vmsd;
172 int version_id = field->version_id;
173 void *tmp = g_malloc(size);
174
175 /* Writes the parent field which is at the start of the tmp */
176 *(void **)tmp = pv;
177 ret = slirp_vmstate_load_state(f, vmsd, tmp, version_id);
178 g_free(tmp);
179 return ret;
180 }
181
182 static int put_tmp(SlirpOStream *f, void *pv, size_t size, const VMStateField *field)
183 {
184 const VMStateDescription *vmsd = field->vmsd;
185 void *tmp = g_malloc(size);
186 int ret;
187
188 /* Writes the parent field which is at the start of the tmp */
189 *(void **)tmp = pv;
190 ret = slirp_vmstate_save_state(f, vmsd, tmp);
191 g_free(tmp);
192
193 return ret;
194 }
195
196 const VMStateInfo slirp_vmstate_info_tmp = {
197 .name = "tmp",
198 .get = get_tmp,
199 .put = put_tmp,
200 };
201
202 /* uint8_t buffers */
203
204 static int get_buffer(SlirpIStream *f, void *pv, size_t size,
205 const VMStateField *field)
206 {
207 slirp_istream_read(f, pv, size);
208 return 0;
209 }
210
211 static int put_buffer(SlirpOStream *f, void *pv, size_t size,
212 const VMStateField *field)
213 {
214 slirp_ostream_write(f, pv, size);
215 return 0;
216 }
217
218 const VMStateInfo slirp_vmstate_info_buffer = {
219 .name = "buffer",
220 .get = get_buffer,
221 .put = put_buffer,
222 };
223
224 static int vmstate_n_elems(void *opaque, const VMStateField *field)
225 {
226 int n_elems = 1;
227
228 if (field->flags & VMS_ARRAY) {
229 n_elems = field->num;
230 } else if (field->flags & VMS_VARRAY_INT32) {
231 n_elems = *(int32_t *)(opaque + field->num_offset);
232 } else if (field->flags & VMS_VARRAY_UINT32) {
233 n_elems = *(uint32_t *)(opaque + field->num_offset);
234 } else if (field->flags & VMS_VARRAY_UINT16) {
235 n_elems = *(uint16_t *)(opaque + field->num_offset);
236 } else if (field->flags & VMS_VARRAY_UINT8) {
237 n_elems = *(uint8_t *)(opaque + field->num_offset);
238 }
239
240 if (field->flags & VMS_MULTIPLY_ELEMENTS) {
241 n_elems *= field->num;
242 }
243
244 return n_elems;
245 }
246
247 static int vmstate_size(void *opaque, const VMStateField *field)
248 {
249 int size = field->size;
250
251 if (field->flags & VMS_VBUFFER) {
252 size = *(int32_t *)(opaque + field->size_offset);
253 if (field->flags & VMS_MULTIPLY) {
254 size *= field->size;
255 }
256 }
257
258 return size;
259 }
260
261 static int
262 vmstate_save_state_v(SlirpOStream *f, const VMStateDescription *vmsd,
263 void *opaque, int version_id)
264 {
265 int ret = 0;
266 const VMStateField *field = vmsd->fields;
267
268 if (vmsd->pre_save) {
269 ret = vmsd->pre_save(opaque);
270 if (ret) {
271 g_warning("pre-save failed: %s", vmsd->name);
272 return ret;
273 }
274 }
275
276 while (field->name) {
277 if ((field->field_exists &&
278 field->field_exists(opaque, version_id)) ||
279 (!field->field_exists &&
280 field->version_id <= version_id)) {
281 void *first_elem = opaque + field->offset;
282 int i, n_elems = vmstate_n_elems(opaque, field);
283 int size = vmstate_size(opaque, field);
284
285 if (field->flags & VMS_POINTER) {
286 first_elem = *(void **)first_elem;
287 assert(first_elem || !n_elems || !size);
288 }
289 for (i = 0; i < n_elems; i++) {
290 void *curr_elem = first_elem + size * i;
291 ret = 0;
292
293 if (field->flags & VMS_ARRAY_OF_POINTER) {
294 assert(curr_elem);
295 curr_elem = *(void **)curr_elem;
296 }
297 if (!curr_elem && size) {
298 /* if null pointer write placeholder and do not follow */
299 assert(field->flags & VMS_ARRAY_OF_POINTER);
300 ret = slirp_vmstate_info_nullptr.put(f, curr_elem, size, NULL);
301 } else if (field->flags & VMS_STRUCT) {
302 ret = slirp_vmstate_save_state(f, field->vmsd, curr_elem);
303 } else if (field->flags & VMS_VSTRUCT) {
304 ret = vmstate_save_state_v(f, field->vmsd, curr_elem,
305 field->struct_version_id);
306 } else {
307 ret = field->info->put(f, curr_elem, size, field);
308 }
309 if (ret) {
310 g_warning("Save of field %s/%s failed",
311 vmsd->name, field->name);
312 return ret;
313 }
314 }
315 } else {
316 if (field->flags & VMS_MUST_EXIST) {
317 g_warning("Output state validation failed: %s/%s",
318 vmsd->name, field->name);
319 assert(!(field->flags & VMS_MUST_EXIST));
320 }
321 }
322 field++;
323 }
324
325 return 0;
326 }
327
328 int slirp_vmstate_save_state(SlirpOStream *f, const VMStateDescription *vmsd,
329 void *opaque)
330 {
331 return vmstate_save_state_v(f, vmsd, opaque, vmsd->version_id);
332 }
333
334 static void vmstate_handle_alloc(void *ptr, VMStateField *field, void *opaque)
335 {
336 if (field->flags & VMS_POINTER && field->flags & VMS_ALLOC) {
337 size_t size = vmstate_size(opaque, field);
338 size *= vmstate_n_elems(opaque, field);
339 if (size) {
340 *(void **)ptr = g_malloc(size);
341 }
342 }
343 }
344
345 int slirp_vmstate_load_state(SlirpIStream *f, const VMStateDescription *vmsd,
346 void *opaque, int version_id)
347 {
348 VMStateField *field = vmsd->fields;
349 int ret = 0;
350
351 if (version_id > vmsd->version_id) {
352 g_warning("%s: incoming version_id %d is too new "
353 "for local version_id %d",
354 vmsd->name, version_id, vmsd->version_id);
355 return -EINVAL;
356 }
357 if (vmsd->pre_load) {
358 int ret = vmsd->pre_load(opaque);
359 if (ret) {
360 return ret;
361 }
362 }
363 while (field->name) {
364 if ((field->field_exists &&
365 field->field_exists(opaque, version_id)) ||
366 (!field->field_exists &&
367 field->version_id <= version_id)) {
368 void *first_elem = opaque + field->offset;
369 int i, n_elems = vmstate_n_elems(opaque, field);
370 int size = vmstate_size(opaque, field);
371
372 vmstate_handle_alloc(first_elem, field, opaque);
373 if (field->flags & VMS_POINTER) {
374 first_elem = *(void **)first_elem;
375 assert(first_elem || !n_elems || !size);
376 }
377 for (i = 0; i < n_elems; i++) {
378 void *curr_elem = first_elem + size * i;
379
380 if (field->flags & VMS_ARRAY_OF_POINTER) {
381 curr_elem = *(void **)curr_elem;
382 }
383 if (!curr_elem && size) {
384 /* if null pointer check placeholder and do not follow */
385 assert(field->flags & VMS_ARRAY_OF_POINTER);
386 ret = slirp_vmstate_info_nullptr.get(f, curr_elem, size, NULL);
387 } else if (field->flags & VMS_STRUCT) {
388 ret = slirp_vmstate_load_state(f, field->vmsd, curr_elem,
389 field->vmsd->version_id);
390 } else if (field->flags & VMS_VSTRUCT) {
391 ret = slirp_vmstate_load_state(f, field->vmsd, curr_elem,
392 field->struct_version_id);
393 } else {
394 ret = field->info->get(f, curr_elem, size, field);
395 }
396 if (ret < 0) {
397 g_warning("Failed to load %s:%s", vmsd->name,
398 field->name);
399 return ret;
400 }
401 }
402 } else if (field->flags & VMS_MUST_EXIST) {
403 g_warning("Input validation failed: %s/%s",
404 vmsd->name, field->name);
405 return -1;
406 }
407 field++;
408 }
409 if (vmsd->post_load) {
410 ret = vmsd->post_load(opaque, version_id);
411 }
412 return ret;
413 }