1/* SPDX-License-Identifier: BSD-3-Clause */
2/*
3 * VMState interpreter
4 *
5 * Copyright (c) 2009-2018 Red Hat Inc
6 *
7 * Authors:
8 * Juan Quintela <quintela@redhat.com>
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 *
14 * 1. Redistributions of source code must retain the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer.
17 *
18 * 2. Redistributions in binary form must reproduce the above
19 * copyright notice, this list of conditions and the following
20 * disclaimer in the documentation and/or other materials provided
21 * with the distribution.
22 *
23 * 3. Neither the name of the copyright holder nor the names of its
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
30 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
31 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
32 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
33 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
34 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
36 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
38 * OF THE POSSIBILITY OF SUCH DAMAGE.
39 */
40#include <assert.h>
41#include <errno.h>
42#include <string.h>
43#include <glib.h>
44
45#include "stream.h"
46#include "vmstate.h"
47
48static int get_nullptr(SlirpIStream *f, void *pv, size_t size,
49 const VMStateField *field)
50{
51 if (slirp_istream_read_u8(f) == VMS_NULLPTR_MARKER) {
52 return 0;
53 }
54 g_warning("vmstate: get_nullptr expected VMS_NULLPTR_MARKER");
55 return -EINVAL;
56}
57
58static int put_nullptr(SlirpOStream *f, void *pv, size_t size,
59 const VMStateField *field)
60
61{
62 if (pv == NULL) {
63 slirp_ostream_write_u8(f, VMS_NULLPTR_MARKER);
64 return 0;
65 }
66 g_warning("vmstate: put_nullptr must be called with pv == NULL");
67 return -EINVAL;
68}
69
70const VMStateInfo slirp_vmstate_info_nullptr = {
71 .name = "uint64",
72 .get = get_nullptr,
73 .put = put_nullptr,
74};
75
76/* 8 bit unsigned int */
77
78static int get_uint8(SlirpIStream *f, void *pv, size_t size,
79 const VMStateField *field)
80{
81 uint8_t *v = pv;
82 *v = slirp_istream_read_u8(f);
83 return 0;
84}
85
86static int put_uint8(SlirpOStream *f, void *pv, size_t size,
87 const VMStateField *field)
88{
89 uint8_t *v = pv;
90 slirp_ostream_write_u8(f, *v);
91 return 0;
92}
93
94const VMStateInfo slirp_vmstate_info_uint8 = {
95 .name = "uint8",
96 .get = get_uint8,
97 .put = put_uint8,
98};
99
100/* 16 bit unsigned int */
101
102static int get_uint16(SlirpIStream *f, void *pv, size_t size,
103 const VMStateField *field)
104{
105 uint16_t *v = pv;
106 *v = slirp_istream_read_u16(f);
107 return 0;
108}
109
110static int put_uint16(SlirpOStream *f, void *pv, size_t size,
111 const VMStateField *field)
112{
113 uint16_t *v = pv;
114 slirp_ostream_write_u16(f, *v);
115 return 0;
116}
117
118const VMStateInfo slirp_vmstate_info_uint16 = {
119 .name = "uint16",
120 .get = get_uint16,
121 .put = put_uint16,
122};
123
124/* 32 bit unsigned int */
125
126static int get_uint32(SlirpIStream *f, void *pv, size_t size,
127 const VMStateField *field)
128{
129 uint32_t *v = pv;
130 *v = slirp_istream_read_u32(f);
131 return 0;
132}
133
134static int put_uint32(SlirpOStream *f, void *pv, size_t size,
135 const VMStateField *field)
136{
137 uint32_t *v = pv;
138 slirp_ostream_write_u32(f, *v);
139 return 0;
140}
141
142const VMStateInfo slirp_vmstate_info_uint32 = {
143 .name = "uint32",
144 .get = get_uint32,
145 .put = put_uint32,
146};
147
148/* 16 bit int */
149
150static int get_int16(SlirpIStream *f, void *pv, size_t size,
151 const VMStateField *field)
152{
153 int16_t *v = pv;
154 *v = slirp_istream_read_i16(f);
155 return 0;
156}
157
158static int put_int16(SlirpOStream *f, void *pv, size_t size,
159 const VMStateField *field)
160{
161 int16_t *v = pv;
162 slirp_ostream_write_i16(f, *v);
163 return 0;
164}
165
166const VMStateInfo slirp_vmstate_info_int16 = {
167 .name = "int16",
168 .get = get_int16,
169 .put = put_int16,
170};
171
172/* 32 bit int */
173
174static int get_int32(SlirpIStream *f, void *pv, size_t size,
175 const VMStateField *field)
176{
177 int32_t *v = pv;
178 *v = slirp_istream_read_i32(f);
179 return 0;
180}
181
182static int put_int32(SlirpOStream *f, void *pv, size_t size,
183 const VMStateField *field)
184{
185 int32_t *v = pv;
186 slirp_ostream_write_i32(f, *v);
187 return 0;
188}
189
190const VMStateInfo slirp_vmstate_info_int32 = {
191 .name = "int32",
192 .get = get_int32,
193 .put = put_int32,
194};
195
196/* vmstate_info_tmp, see VMSTATE_WITH_TMP, the idea is that we allocate
197 * a temporary buffer and the pre_load/pre_save methods in the child vmsd
198 * copy stuff from the parent into the child and do calculations to fill
199 * in fields that don't really exist in the parent but need to be in the
200 * stream.
201 */
202static int get_tmp(SlirpIStream *f, void *pv, size_t size,
203 const VMStateField *field)
204{
205 int ret;
206 const VMStateDescription *vmsd = field->vmsd;
207 int version_id = field->version_id;
208 void *tmp = g_malloc(size);
209
210 /* Writes the parent field which is at the start of the tmp */
211 *(void **)tmp = pv;
212 ret = slirp_vmstate_load_state(f, vmsd, tmp, version_id);
213 g_free(tmp);
214 return ret;
215}
216
217static int put_tmp(SlirpOStream *f, void *pv, size_t size,
218 const VMStateField *field)
219{
220 const VMStateDescription *vmsd = field->vmsd;
221 void *tmp = g_malloc(size);
222 int ret;
223
224 /* Writes the parent field which is at the start of the tmp */
225 *(void **)tmp = pv;
226 ret = slirp_vmstate_save_state(f, vmsd, tmp);
227 g_free(tmp);
228
229 return ret;
230}
231
232const VMStateInfo slirp_vmstate_info_tmp = {
233 .name = "tmp",
234 .get = get_tmp,
235 .put = put_tmp,
236};
237
238/* uint8_t buffers */
239
240static int get_buffer(SlirpIStream *f, void *pv, size_t size,
241 const VMStateField *field)
242{
243 slirp_istream_read(f, pv, size);
244 return 0;
245}
246
247static int put_buffer(SlirpOStream *f, void *pv, size_t size,
248 const VMStateField *field)
249{
250 slirp_ostream_write(f, pv, size);
251 return 0;
252}
253
254const VMStateInfo slirp_vmstate_info_buffer = {
255 .name = "buffer",
256 .get = get_buffer,
257 .put = put_buffer,
258};
259
260static int vmstate_n_elems(void *opaque, const VMStateField *field)
261{
262 int n_elems = 1;
263
264 if (field->flags & VMS_ARRAY) {
265 n_elems = field->num;
266 } else if (field->flags & VMS_VARRAY_INT32) {
267 n_elems = *(int32_t *)(opaque + field->num_offset);
268 } else if (field->flags & VMS_VARRAY_UINT32) {
269 n_elems = *(uint32_t *)(opaque + field->num_offset);
270 } else if (field->flags & VMS_VARRAY_UINT16) {
271 n_elems = *(uint16_t *)(opaque + field->num_offset);
272 } else if (field->flags & VMS_VARRAY_UINT8) {
273 n_elems = *(uint8_t *)(opaque + field->num_offset);
274 }
275
276 if (field->flags & VMS_MULTIPLY_ELEMENTS) {
277 n_elems *= field->num;
278 }
279
280 return n_elems;
281}
282
283static int vmstate_size(void *opaque, const VMStateField *field)
284{
285 int size = field->size;
286
287 if (field->flags & VMS_VBUFFER) {
288 size = *(int32_t *)(opaque + field->size_offset);
289 if (field->flags & VMS_MULTIPLY) {
290 size *= field->size;
291 }
292 }
293
294 return size;
295}
296
297static int vmstate_save_state_v(SlirpOStream *f, const VMStateDescription *vmsd,
298 void *opaque, int version_id)
299{
300 int ret = 0;
301 const VMStateField *field = vmsd->fields;
302
303 if (vmsd->pre_save) {
304 ret = vmsd->pre_save(opaque);
305 if (ret) {
306 g_warning("pre-save failed: %s", vmsd->name);
307 return ret;
308 }
309 }
310
311 while (field->name) {
312 if ((field->field_exists && field->field_exists(opaque, version_id)) ||
313 (!field->field_exists && field->version_id <= version_id)) {
314 void *first_elem = opaque + field->offset;
315 int i, n_elems = vmstate_n_elems(opaque, field);
316 int size = vmstate_size(opaque, field);
317
318 if (field->flags & VMS_POINTER) {
319 first_elem = *(void **)first_elem;
320 assert(first_elem || !n_elems || !size);
321 }
322 for (i = 0; i < n_elems; i++) {
323 void *curr_elem = first_elem + size * i;
324 ret = 0;
325
326 if (field->flags & VMS_ARRAY_OF_POINTER) {
327 assert(curr_elem);
328 curr_elem = *(void **)curr_elem;
329 }
330 if (!curr_elem && size) {
331 /* if null pointer write placeholder and do not follow */
332 assert(field->flags & VMS_ARRAY_OF_POINTER);
333 ret = slirp_vmstate_info_nullptr.put(f, curr_elem, size,
334 NULL);
335 } else if (field->flags & VMS_STRUCT) {
336 ret = slirp_vmstate_save_state(f, field->vmsd, curr_elem);
337 } else if (field->flags & VMS_VSTRUCT) {
338 ret = vmstate_save_state_v(f, field->vmsd, curr_elem,
339 field->struct_version_id);
340 } else {
341 ret = field->info->put(f, curr_elem, size, field);
342 }
343 if (ret) {
344 g_warning("Save of field %s/%s failed", vmsd->name,
345 field->name);
346 return ret;
347 }
348 }
349 } else {
350 if (field->flags & VMS_MUST_EXIST) {
351 g_warning("Output state validation failed: %s/%s", vmsd->name,
352 field->name);
353 assert(!(field->flags & VMS_MUST_EXIST));
354 }
355 }
356 field++;
357 }
358
359 return 0;
360}
361
362int slirp_vmstate_save_state(SlirpOStream *f, const VMStateDescription *vmsd,
363 void *opaque)
364{
365 return vmstate_save_state_v(f, vmsd, opaque, vmsd->version_id);
366}
367
368static void vmstate_handle_alloc(void *ptr, VMStateField *field, void *opaque)
369{
370 if (field->flags & VMS_POINTER && field->flags & VMS_ALLOC) {
371 size_t size = vmstate_size(opaque, field);
372 size *= vmstate_n_elems(opaque, field);
373 if (size) {
374 *(void **)ptr = g_malloc(size);
375 }
376 }
377}
378
379int slirp_vmstate_load_state(SlirpIStream *f, const VMStateDescription *vmsd,
380 void *opaque, int version_id)
381{
382 VMStateField *field = vmsd->fields;
383 int ret = 0;
384
385 if (version_id > vmsd->version_id) {
386 g_warning("%s: incoming version_id %d is too new "
387 "for local version_id %d",
388 vmsd->name, version_id, vmsd->version_id);
389 return -EINVAL;
390 }
391 if (vmsd->pre_load) {
392 int ret = vmsd->pre_load(opaque);
393 if (ret) {
394 return ret;
395 }
396 }
397 while (field->name) {
398 if ((field->field_exists && field->field_exists(opaque, version_id)) ||
399 (!field->field_exists && field->version_id <= version_id)) {
400 void *first_elem = opaque + field->offset;
401 int i, n_elems = vmstate_n_elems(opaque, field);
402 int size = vmstate_size(opaque, field);
403
404 vmstate_handle_alloc(first_elem, field, opaque);
405 if (field->flags & VMS_POINTER) {
406 first_elem = *(void **)first_elem;
407 assert(first_elem || !n_elems || !size);
408 }
409 for (i = 0; i < n_elems; i++) {
410 void *curr_elem = first_elem + size * i;
411
412 if (field->flags & VMS_ARRAY_OF_POINTER) {
413 curr_elem = *(void **)curr_elem;
414 }
415 if (!curr_elem && size) {
416 /* if null pointer check placeholder and do not follow */
417 assert(field->flags & VMS_ARRAY_OF_POINTER);
418 ret = slirp_vmstate_info_nullptr.get(f, curr_elem, size,
419 NULL);
420 } else if (field->flags & VMS_STRUCT) {
421 ret = slirp_vmstate_load_state(f, field->vmsd, curr_elem,
422 field->vmsd->version_id);
423 } else if (field->flags & VMS_VSTRUCT) {
424 ret = slirp_vmstate_load_state(f, field->vmsd, curr_elem,
425 field->struct_version_id);
426 } else {
427 ret = field->info->get(f, curr_elem, size, field);
428 }
429 if (ret < 0) {
430 g_warning("Failed to load %s:%s", vmsd->name, field->name);
431 return ret;
432 }
433 }
434 } else if (field->flags & VMS_MUST_EXIST) {
435 g_warning("Input validation failed: %s/%s", vmsd->name,
436 field->name);
437 return -1;
438 }
439 field++;
440 }
441 if (vmsd->post_load) {
442 ret = vmsd->post_load(opaque, version_id);
443 }
444 return ret;
445}
446