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 | |
48 | static 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 | |
58 | static 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 | |
70 | const VMStateInfo slirp_vmstate_info_nullptr = { |
71 | .name = "uint64" , |
72 | .get = get_nullptr, |
73 | .put = put_nullptr, |
74 | }; |
75 | |
76 | /* 8 bit unsigned int */ |
77 | |
78 | static 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 | |
86 | static 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 | |
94 | const VMStateInfo slirp_vmstate_info_uint8 = { |
95 | .name = "uint8" , |
96 | .get = get_uint8, |
97 | .put = put_uint8, |
98 | }; |
99 | |
100 | /* 16 bit unsigned int */ |
101 | |
102 | static 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 | |
110 | static 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 | |
118 | const VMStateInfo slirp_vmstate_info_uint16 = { |
119 | .name = "uint16" , |
120 | .get = get_uint16, |
121 | .put = put_uint16, |
122 | }; |
123 | |
124 | /* 32 bit unsigned int */ |
125 | |
126 | static 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 | |
134 | static 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 | |
142 | const VMStateInfo slirp_vmstate_info_uint32 = { |
143 | .name = "uint32" , |
144 | .get = get_uint32, |
145 | .put = put_uint32, |
146 | }; |
147 | |
148 | /* 16 bit int */ |
149 | |
150 | static 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 | |
158 | static 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 | |
166 | const VMStateInfo slirp_vmstate_info_int16 = { |
167 | .name = "int16" , |
168 | .get = get_int16, |
169 | .put = put_int16, |
170 | }; |
171 | |
172 | /* 32 bit int */ |
173 | |
174 | static 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 | |
182 | static 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 | |
190 | const 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 | */ |
202 | static 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 | |
217 | static 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 | |
232 | const VMStateInfo slirp_vmstate_info_tmp = { |
233 | .name = "tmp" , |
234 | .get = get_tmp, |
235 | .put = put_tmp, |
236 | }; |
237 | |
238 | /* uint8_t buffers */ |
239 | |
240 | static 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 | |
247 | static 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 | |
254 | const VMStateInfo slirp_vmstate_info_buffer = { |
255 | .name = "buffer" , |
256 | .get = get_buffer, |
257 | .put = put_buffer, |
258 | }; |
259 | |
260 | static 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 | |
283 | static 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 | |
297 | static 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 | |
362 | int 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 | |
368 | static 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 | |
379 | int 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 | |