1 | /* |
2 | * QEMU Machine |
3 | * |
4 | * Copyright (C) 2014 Red Hat Inc |
5 | * |
6 | * Authors: |
7 | * Marcel Apfelbaum <marcel.a@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 | |
13 | #include "qemu/osdep.h" |
14 | #include "qemu/option.h" |
15 | #include "qapi/qmp/qerror.h" |
16 | #include "sysemu/replay.h" |
17 | #include "qemu/units.h" |
18 | #include "hw/boards.h" |
19 | #include "qapi/error.h" |
20 | #include "qapi/qapi-visit-common.h" |
21 | #include "qapi/visitor.h" |
22 | #include "hw/sysbus.h" |
23 | #include "sysemu/sysemu.h" |
24 | #include "sysemu/numa.h" |
25 | #include "qemu/error-report.h" |
26 | #include "sysemu/qtest.h" |
27 | #include "hw/pci/pci.h" |
28 | #include "hw/mem/nvdimm.h" |
29 | |
30 | GlobalProperty hw_compat_4_1[] = { |
31 | { "virtio-pci" , "x-pcie-flr-init" , "off" }, |
32 | }; |
33 | const size_t hw_compat_4_1_len = G_N_ELEMENTS(hw_compat_4_1); |
34 | |
35 | GlobalProperty hw_compat_4_0[] = { |
36 | { "VGA" , "edid" , "false" }, |
37 | { "secondary-vga" , "edid" , "false" }, |
38 | { "bochs-display" , "edid" , "false" }, |
39 | { "virtio-vga" , "edid" , "false" }, |
40 | { "virtio-gpu" , "edid" , "false" }, |
41 | { "virtio-device" , "use-started" , "false" }, |
42 | { "virtio-balloon-device" , "qemu-4-0-config-size" , "true" }, |
43 | { "pl031" , "migrate-tick-offset" , "false" }, |
44 | }; |
45 | const size_t hw_compat_4_0_len = G_N_ELEMENTS(hw_compat_4_0); |
46 | |
47 | GlobalProperty hw_compat_3_1[] = { |
48 | { "pcie-root-port" , "x-speed" , "2_5" }, |
49 | { "pcie-root-port" , "x-width" , "1" }, |
50 | { "memory-backend-file" , "x-use-canonical-path-for-ramblock-id" , "true" }, |
51 | { "memory-backend-memfd" , "x-use-canonical-path-for-ramblock-id" , "true" }, |
52 | { "tpm-crb" , "ppi" , "false" }, |
53 | { "tpm-tis" , "ppi" , "false" }, |
54 | { "usb-kbd" , "serial" , "42" }, |
55 | { "usb-mouse" , "serial" , "42" }, |
56 | { "usb-tablet" , "serial" , "42" }, |
57 | { "virtio-blk-device" , "discard" , "false" }, |
58 | { "virtio-blk-device" , "write-zeroes" , "false" }, |
59 | { "virtio-balloon-device" , "qemu-4-0-config-size" , "false" }, |
60 | { "pcie-root-port-base" , "disable-acs" , "true" }, /* Added in 4.1 */ |
61 | }; |
62 | const size_t hw_compat_3_1_len = G_N_ELEMENTS(hw_compat_3_1); |
63 | |
64 | GlobalProperty hw_compat_3_0[] = {}; |
65 | const size_t hw_compat_3_0_len = G_N_ELEMENTS(hw_compat_3_0); |
66 | |
67 | GlobalProperty hw_compat_2_12[] = { |
68 | { "migration" , "decompress-error-check" , "off" }, |
69 | { "hda-audio" , "use-timer" , "false" }, |
70 | { "cirrus-vga" , "global-vmstate" , "true" }, |
71 | { "VGA" , "global-vmstate" , "true" }, |
72 | { "vmware-svga" , "global-vmstate" , "true" }, |
73 | { "qxl-vga" , "global-vmstate" , "true" }, |
74 | }; |
75 | const size_t hw_compat_2_12_len = G_N_ELEMENTS(hw_compat_2_12); |
76 | |
77 | GlobalProperty hw_compat_2_11[] = { |
78 | { "hpet" , "hpet-offset-saved" , "false" }, |
79 | { "virtio-blk-pci" , "vectors" , "2" }, |
80 | { "vhost-user-blk-pci" , "vectors" , "2" }, |
81 | { "e1000" , "migrate_tso_props" , "off" }, |
82 | }; |
83 | const size_t hw_compat_2_11_len = G_N_ELEMENTS(hw_compat_2_11); |
84 | |
85 | GlobalProperty hw_compat_2_10[] = { |
86 | { "virtio-mouse-device" , "wheel-axis" , "false" }, |
87 | { "virtio-tablet-device" , "wheel-axis" , "false" }, |
88 | }; |
89 | const size_t hw_compat_2_10_len = G_N_ELEMENTS(hw_compat_2_10); |
90 | |
91 | GlobalProperty hw_compat_2_9[] = { |
92 | { "pci-bridge" , "shpc" , "off" }, |
93 | { "intel-iommu" , "pt" , "off" }, |
94 | { "virtio-net-device" , "x-mtu-bypass-backend" , "off" }, |
95 | { "pcie-root-port" , "x-migrate-msix" , "false" }, |
96 | }; |
97 | const size_t hw_compat_2_9_len = G_N_ELEMENTS(hw_compat_2_9); |
98 | |
99 | GlobalProperty hw_compat_2_8[] = { |
100 | { "fw_cfg_mem" , "x-file-slots" , "0x10" }, |
101 | { "fw_cfg_io" , "x-file-slots" , "0x10" }, |
102 | { "pflash_cfi01" , "old-multiple-chip-handling" , "on" }, |
103 | { "pci-bridge" , "shpc" , "on" }, |
104 | { TYPE_PCI_DEVICE, "x-pcie-extcap-init" , "off" }, |
105 | { "virtio-pci" , "x-pcie-deverr-init" , "off" }, |
106 | { "virtio-pci" , "x-pcie-lnkctl-init" , "off" }, |
107 | { "virtio-pci" , "x-pcie-pm-init" , "off" }, |
108 | { "cirrus-vga" , "vgamem_mb" , "8" }, |
109 | { "isa-cirrus-vga" , "vgamem_mb" , "8" }, |
110 | }; |
111 | const size_t hw_compat_2_8_len = G_N_ELEMENTS(hw_compat_2_8); |
112 | |
113 | GlobalProperty hw_compat_2_7[] = { |
114 | { "virtio-pci" , "page-per-vq" , "on" }, |
115 | { "virtio-serial-device" , "emergency-write" , "off" }, |
116 | { "ioapic" , "version" , "0x11" }, |
117 | { "intel-iommu" , "x-buggy-eim" , "true" }, |
118 | { "virtio-pci" , "x-ignore-backend-features" , "on" }, |
119 | }; |
120 | const size_t hw_compat_2_7_len = G_N_ELEMENTS(hw_compat_2_7); |
121 | |
122 | GlobalProperty hw_compat_2_6[] = { |
123 | { "virtio-mmio" , "format_transport_address" , "off" }, |
124 | /* Optional because not all virtio-pci devices support legacy mode */ |
125 | { "virtio-pci" , "disable-modern" , "on" , .optional = true }, |
126 | { "virtio-pci" , "disable-legacy" , "off" , .optional = true }, |
127 | }; |
128 | const size_t hw_compat_2_6_len = G_N_ELEMENTS(hw_compat_2_6); |
129 | |
130 | GlobalProperty hw_compat_2_5[] = { |
131 | { "isa-fdc" , "fallback" , "144" }, |
132 | { "pvscsi" , "x-old-pci-configuration" , "on" }, |
133 | { "pvscsi" , "x-disable-pcie" , "on" }, |
134 | { "vmxnet3" , "x-old-msi-offsets" , "on" }, |
135 | { "vmxnet3" , "x-disable-pcie" , "on" }, |
136 | }; |
137 | const size_t hw_compat_2_5_len = G_N_ELEMENTS(hw_compat_2_5); |
138 | |
139 | GlobalProperty hw_compat_2_4[] = { |
140 | { "virtio-blk-device" , "scsi" , "true" }, |
141 | { "e1000" , "extra_mac_registers" , "off" }, |
142 | { "virtio-pci" , "x-disable-pcie" , "on" }, |
143 | { "virtio-pci" , "migrate-extra" , "off" }, |
144 | { "fw_cfg_mem" , "dma_enabled" , "off" }, |
145 | { "fw_cfg_io" , "dma_enabled" , "off" } |
146 | }; |
147 | const size_t hw_compat_2_4_len = G_N_ELEMENTS(hw_compat_2_4); |
148 | |
149 | GlobalProperty hw_compat_2_3[] = { |
150 | { "virtio-blk-pci" , "any_layout" , "off" }, |
151 | { "virtio-balloon-pci" , "any_layout" , "off" }, |
152 | { "virtio-serial-pci" , "any_layout" , "off" }, |
153 | { "virtio-9p-pci" , "any_layout" , "off" }, |
154 | { "virtio-rng-pci" , "any_layout" , "off" }, |
155 | { TYPE_PCI_DEVICE, "x-pcie-lnksta-dllla" , "off" }, |
156 | { "migration" , "send-configuration" , "off" }, |
157 | { "migration" , "send-section-footer" , "off" }, |
158 | { "migration" , "store-global-state" , "off" }, |
159 | }; |
160 | const size_t hw_compat_2_3_len = G_N_ELEMENTS(hw_compat_2_3); |
161 | |
162 | GlobalProperty hw_compat_2_2[] = {}; |
163 | const size_t hw_compat_2_2_len = G_N_ELEMENTS(hw_compat_2_2); |
164 | |
165 | GlobalProperty hw_compat_2_1[] = { |
166 | { "intel-hda" , "old_msi_addr" , "on" }, |
167 | { "VGA" , "qemu-extended-regs" , "off" }, |
168 | { "secondary-vga" , "qemu-extended-regs" , "off" }, |
169 | { "virtio-scsi-pci" , "any_layout" , "off" }, |
170 | { "usb-mouse" , "usb_version" , "1" }, |
171 | { "usb-kbd" , "usb_version" , "1" }, |
172 | { "virtio-pci" , "virtio-pci-bus-master-bug-migration" , "on" }, |
173 | }; |
174 | const size_t hw_compat_2_1_len = G_N_ELEMENTS(hw_compat_2_1); |
175 | |
176 | static char *machine_get_accel(Object *obj, Error **errp) |
177 | { |
178 | MachineState *ms = MACHINE(obj); |
179 | |
180 | return g_strdup(ms->accel); |
181 | } |
182 | |
183 | static void machine_set_accel(Object *obj, const char *value, Error **errp) |
184 | { |
185 | MachineState *ms = MACHINE(obj); |
186 | |
187 | g_free(ms->accel); |
188 | ms->accel = g_strdup(value); |
189 | } |
190 | |
191 | static void machine_set_kernel_irqchip(Object *obj, Visitor *v, |
192 | const char *name, void *opaque, |
193 | Error **errp) |
194 | { |
195 | Error *err = NULL; |
196 | MachineState *ms = MACHINE(obj); |
197 | OnOffSplit mode; |
198 | |
199 | visit_type_OnOffSplit(v, name, &mode, &err); |
200 | if (err) { |
201 | error_propagate(errp, err); |
202 | return; |
203 | } else { |
204 | switch (mode) { |
205 | case ON_OFF_SPLIT_ON: |
206 | ms->kernel_irqchip_allowed = true; |
207 | ms->kernel_irqchip_required = true; |
208 | ms->kernel_irqchip_split = false; |
209 | break; |
210 | case ON_OFF_SPLIT_OFF: |
211 | ms->kernel_irqchip_allowed = false; |
212 | ms->kernel_irqchip_required = false; |
213 | ms->kernel_irqchip_split = false; |
214 | break; |
215 | case ON_OFF_SPLIT_SPLIT: |
216 | ms->kernel_irqchip_allowed = true; |
217 | ms->kernel_irqchip_required = true; |
218 | ms->kernel_irqchip_split = true; |
219 | break; |
220 | default: |
221 | /* The value was checked in visit_type_OnOffSplit() above. If |
222 | * we get here, then something is wrong in QEMU. |
223 | */ |
224 | abort(); |
225 | } |
226 | } |
227 | } |
228 | |
229 | static void machine_get_kvm_shadow_mem(Object *obj, Visitor *v, |
230 | const char *name, void *opaque, |
231 | Error **errp) |
232 | { |
233 | MachineState *ms = MACHINE(obj); |
234 | int64_t value = ms->kvm_shadow_mem; |
235 | |
236 | visit_type_int(v, name, &value, errp); |
237 | } |
238 | |
239 | static void machine_set_kvm_shadow_mem(Object *obj, Visitor *v, |
240 | const char *name, void *opaque, |
241 | Error **errp) |
242 | { |
243 | MachineState *ms = MACHINE(obj); |
244 | Error *error = NULL; |
245 | int64_t value; |
246 | |
247 | visit_type_int(v, name, &value, &error); |
248 | if (error) { |
249 | error_propagate(errp, error); |
250 | return; |
251 | } |
252 | |
253 | ms->kvm_shadow_mem = value; |
254 | } |
255 | |
256 | static char *machine_get_kernel(Object *obj, Error **errp) |
257 | { |
258 | MachineState *ms = MACHINE(obj); |
259 | |
260 | return g_strdup(ms->kernel_filename); |
261 | } |
262 | |
263 | static void machine_set_kernel(Object *obj, const char *value, Error **errp) |
264 | { |
265 | MachineState *ms = MACHINE(obj); |
266 | |
267 | g_free(ms->kernel_filename); |
268 | ms->kernel_filename = g_strdup(value); |
269 | } |
270 | |
271 | static char *machine_get_initrd(Object *obj, Error **errp) |
272 | { |
273 | MachineState *ms = MACHINE(obj); |
274 | |
275 | return g_strdup(ms->initrd_filename); |
276 | } |
277 | |
278 | static void machine_set_initrd(Object *obj, const char *value, Error **errp) |
279 | { |
280 | MachineState *ms = MACHINE(obj); |
281 | |
282 | g_free(ms->initrd_filename); |
283 | ms->initrd_filename = g_strdup(value); |
284 | } |
285 | |
286 | static char *machine_get_append(Object *obj, Error **errp) |
287 | { |
288 | MachineState *ms = MACHINE(obj); |
289 | |
290 | return g_strdup(ms->kernel_cmdline); |
291 | } |
292 | |
293 | static void machine_set_append(Object *obj, const char *value, Error **errp) |
294 | { |
295 | MachineState *ms = MACHINE(obj); |
296 | |
297 | g_free(ms->kernel_cmdline); |
298 | ms->kernel_cmdline = g_strdup(value); |
299 | } |
300 | |
301 | static char *machine_get_dtb(Object *obj, Error **errp) |
302 | { |
303 | MachineState *ms = MACHINE(obj); |
304 | |
305 | return g_strdup(ms->dtb); |
306 | } |
307 | |
308 | static void machine_set_dtb(Object *obj, const char *value, Error **errp) |
309 | { |
310 | MachineState *ms = MACHINE(obj); |
311 | |
312 | g_free(ms->dtb); |
313 | ms->dtb = g_strdup(value); |
314 | } |
315 | |
316 | static char *machine_get_dumpdtb(Object *obj, Error **errp) |
317 | { |
318 | MachineState *ms = MACHINE(obj); |
319 | |
320 | return g_strdup(ms->dumpdtb); |
321 | } |
322 | |
323 | static void machine_set_dumpdtb(Object *obj, const char *value, Error **errp) |
324 | { |
325 | MachineState *ms = MACHINE(obj); |
326 | |
327 | g_free(ms->dumpdtb); |
328 | ms->dumpdtb = g_strdup(value); |
329 | } |
330 | |
331 | static void machine_get_phandle_start(Object *obj, Visitor *v, |
332 | const char *name, void *opaque, |
333 | Error **errp) |
334 | { |
335 | MachineState *ms = MACHINE(obj); |
336 | int64_t value = ms->phandle_start; |
337 | |
338 | visit_type_int(v, name, &value, errp); |
339 | } |
340 | |
341 | static void machine_set_phandle_start(Object *obj, Visitor *v, |
342 | const char *name, void *opaque, |
343 | Error **errp) |
344 | { |
345 | MachineState *ms = MACHINE(obj); |
346 | Error *error = NULL; |
347 | int64_t value; |
348 | |
349 | visit_type_int(v, name, &value, &error); |
350 | if (error) { |
351 | error_propagate(errp, error); |
352 | return; |
353 | } |
354 | |
355 | ms->phandle_start = value; |
356 | } |
357 | |
358 | static char *machine_get_dt_compatible(Object *obj, Error **errp) |
359 | { |
360 | MachineState *ms = MACHINE(obj); |
361 | |
362 | return g_strdup(ms->dt_compatible); |
363 | } |
364 | |
365 | static void machine_set_dt_compatible(Object *obj, const char *value, Error **errp) |
366 | { |
367 | MachineState *ms = MACHINE(obj); |
368 | |
369 | g_free(ms->dt_compatible); |
370 | ms->dt_compatible = g_strdup(value); |
371 | } |
372 | |
373 | static bool machine_get_dump_guest_core(Object *obj, Error **errp) |
374 | { |
375 | MachineState *ms = MACHINE(obj); |
376 | |
377 | return ms->dump_guest_core; |
378 | } |
379 | |
380 | static void machine_set_dump_guest_core(Object *obj, bool value, Error **errp) |
381 | { |
382 | MachineState *ms = MACHINE(obj); |
383 | |
384 | ms->dump_guest_core = value; |
385 | } |
386 | |
387 | static bool machine_get_mem_merge(Object *obj, Error **errp) |
388 | { |
389 | MachineState *ms = MACHINE(obj); |
390 | |
391 | return ms->mem_merge; |
392 | } |
393 | |
394 | static void machine_set_mem_merge(Object *obj, bool value, Error **errp) |
395 | { |
396 | MachineState *ms = MACHINE(obj); |
397 | |
398 | ms->mem_merge = value; |
399 | } |
400 | |
401 | static bool machine_get_usb(Object *obj, Error **errp) |
402 | { |
403 | MachineState *ms = MACHINE(obj); |
404 | |
405 | return ms->usb; |
406 | } |
407 | |
408 | static void machine_set_usb(Object *obj, bool value, Error **errp) |
409 | { |
410 | MachineState *ms = MACHINE(obj); |
411 | |
412 | ms->usb = value; |
413 | ms->usb_disabled = !value; |
414 | } |
415 | |
416 | static bool machine_get_graphics(Object *obj, Error **errp) |
417 | { |
418 | MachineState *ms = MACHINE(obj); |
419 | |
420 | return ms->enable_graphics; |
421 | } |
422 | |
423 | static void machine_set_graphics(Object *obj, bool value, Error **errp) |
424 | { |
425 | MachineState *ms = MACHINE(obj); |
426 | |
427 | ms->enable_graphics = value; |
428 | } |
429 | |
430 | static bool machine_get_igd_gfx_passthru(Object *obj, Error **errp) |
431 | { |
432 | MachineState *ms = MACHINE(obj); |
433 | |
434 | return ms->igd_gfx_passthru; |
435 | } |
436 | |
437 | static void machine_set_igd_gfx_passthru(Object *obj, bool value, Error **errp) |
438 | { |
439 | MachineState *ms = MACHINE(obj); |
440 | |
441 | ms->igd_gfx_passthru = value; |
442 | } |
443 | |
444 | static char *machine_get_firmware(Object *obj, Error **errp) |
445 | { |
446 | MachineState *ms = MACHINE(obj); |
447 | |
448 | return g_strdup(ms->firmware); |
449 | } |
450 | |
451 | static void machine_set_firmware(Object *obj, const char *value, Error **errp) |
452 | { |
453 | MachineState *ms = MACHINE(obj); |
454 | |
455 | g_free(ms->firmware); |
456 | ms->firmware = g_strdup(value); |
457 | } |
458 | |
459 | static void machine_set_suppress_vmdesc(Object *obj, bool value, Error **errp) |
460 | { |
461 | MachineState *ms = MACHINE(obj); |
462 | |
463 | ms->suppress_vmdesc = value; |
464 | } |
465 | |
466 | static bool machine_get_suppress_vmdesc(Object *obj, Error **errp) |
467 | { |
468 | MachineState *ms = MACHINE(obj); |
469 | |
470 | return ms->suppress_vmdesc; |
471 | } |
472 | |
473 | static void machine_set_enforce_config_section(Object *obj, bool value, |
474 | Error **errp) |
475 | { |
476 | MachineState *ms = MACHINE(obj); |
477 | |
478 | warn_report("enforce-config-section is deprecated, please use " |
479 | "-global migration.send-configuration=on|off instead" ); |
480 | |
481 | ms->enforce_config_section = value; |
482 | } |
483 | |
484 | static bool machine_get_enforce_config_section(Object *obj, Error **errp) |
485 | { |
486 | MachineState *ms = MACHINE(obj); |
487 | |
488 | return ms->enforce_config_section; |
489 | } |
490 | |
491 | static char *machine_get_memory_encryption(Object *obj, Error **errp) |
492 | { |
493 | MachineState *ms = MACHINE(obj); |
494 | |
495 | return g_strdup(ms->memory_encryption); |
496 | } |
497 | |
498 | static void machine_set_memory_encryption(Object *obj, const char *value, |
499 | Error **errp) |
500 | { |
501 | MachineState *ms = MACHINE(obj); |
502 | |
503 | g_free(ms->memory_encryption); |
504 | ms->memory_encryption = g_strdup(value); |
505 | } |
506 | |
507 | static bool machine_get_nvdimm(Object *obj, Error **errp) |
508 | { |
509 | MachineState *ms = MACHINE(obj); |
510 | |
511 | return ms->nvdimms_state->is_enabled; |
512 | } |
513 | |
514 | static void machine_set_nvdimm(Object *obj, bool value, Error **errp) |
515 | { |
516 | MachineState *ms = MACHINE(obj); |
517 | |
518 | ms->nvdimms_state->is_enabled = value; |
519 | } |
520 | |
521 | static char *machine_get_nvdimm_persistence(Object *obj, Error **errp) |
522 | { |
523 | MachineState *ms = MACHINE(obj); |
524 | |
525 | return g_strdup(ms->nvdimms_state->persistence_string); |
526 | } |
527 | |
528 | static void machine_set_nvdimm_persistence(Object *obj, const char *value, |
529 | Error **errp) |
530 | { |
531 | MachineState *ms = MACHINE(obj); |
532 | NVDIMMState *nvdimms_state = ms->nvdimms_state; |
533 | |
534 | if (strcmp(value, "cpu" ) == 0) { |
535 | nvdimms_state->persistence = 3; |
536 | } else if (strcmp(value, "mem-ctrl" ) == 0) { |
537 | nvdimms_state->persistence = 2; |
538 | } else { |
539 | error_setg(errp, "-machine nvdimm-persistence=%s: unsupported option" , |
540 | value); |
541 | return; |
542 | } |
543 | |
544 | g_free(nvdimms_state->persistence_string); |
545 | nvdimms_state->persistence_string = g_strdup(value); |
546 | } |
547 | |
548 | void machine_class_allow_dynamic_sysbus_dev(MachineClass *mc, const char *type) |
549 | { |
550 | strList *item = g_new0(strList, 1); |
551 | |
552 | item->value = g_strdup(type); |
553 | item->next = mc->allowed_dynamic_sysbus_devices; |
554 | mc->allowed_dynamic_sysbus_devices = item; |
555 | } |
556 | |
557 | static void validate_sysbus_device(SysBusDevice *sbdev, void *opaque) |
558 | { |
559 | MachineState *machine = opaque; |
560 | MachineClass *mc = MACHINE_GET_CLASS(machine); |
561 | bool allowed = false; |
562 | strList *wl; |
563 | |
564 | for (wl = mc->allowed_dynamic_sysbus_devices; |
565 | !allowed && wl; |
566 | wl = wl->next) { |
567 | allowed |= !!object_dynamic_cast(OBJECT(sbdev), wl->value); |
568 | } |
569 | |
570 | if (!allowed) { |
571 | error_report("Option '-device %s' cannot be handled by this machine" , |
572 | object_class_get_name(object_get_class(OBJECT(sbdev)))); |
573 | exit(1); |
574 | } |
575 | } |
576 | |
577 | static void machine_init_notify(Notifier *notifier, void *data) |
578 | { |
579 | MachineState *machine = MACHINE(qdev_get_machine()); |
580 | |
581 | /* |
582 | * Loop through all dynamically created sysbus devices and check if they are |
583 | * all allowed. If a device is not allowed, error out. |
584 | */ |
585 | foreach_dynamic_sysbus_device(validate_sysbus_device, machine); |
586 | } |
587 | |
588 | HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine) |
589 | { |
590 | int i; |
591 | HotpluggableCPUList *head = NULL; |
592 | MachineClass *mc = MACHINE_GET_CLASS(machine); |
593 | |
594 | /* force board to initialize possible_cpus if it hasn't been done yet */ |
595 | mc->possible_cpu_arch_ids(machine); |
596 | |
597 | for (i = 0; i < machine->possible_cpus->len; i++) { |
598 | Object *cpu; |
599 | HotpluggableCPUList *list_item = g_new0(typeof(*list_item), 1); |
600 | HotpluggableCPU *cpu_item = g_new0(typeof(*cpu_item), 1); |
601 | |
602 | cpu_item->type = g_strdup(machine->possible_cpus->cpus[i].type); |
603 | cpu_item->vcpus_count = machine->possible_cpus->cpus[i].vcpus_count; |
604 | cpu_item->props = g_memdup(&machine->possible_cpus->cpus[i].props, |
605 | sizeof(*cpu_item->props)); |
606 | |
607 | cpu = machine->possible_cpus->cpus[i].cpu; |
608 | if (cpu) { |
609 | cpu_item->has_qom_path = true; |
610 | cpu_item->qom_path = object_get_canonical_path(cpu); |
611 | } |
612 | list_item->value = cpu_item; |
613 | list_item->next = head; |
614 | head = list_item; |
615 | } |
616 | return head; |
617 | } |
618 | |
619 | /** |
620 | * machine_set_cpu_numa_node: |
621 | * @machine: machine object to modify |
622 | * @props: specifies which cpu objects to assign to |
623 | * numa node specified by @props.node_id |
624 | * @errp: if an error occurs, a pointer to an area to store the error |
625 | * |
626 | * Associate NUMA node specified by @props.node_id with cpu slots that |
627 | * match socket/core/thread-ids specified by @props. It's recommended to use |
628 | * query-hotpluggable-cpus.props values to specify affected cpu slots, |
629 | * which would lead to exact 1:1 mapping of cpu slots to NUMA node. |
630 | * |
631 | * However for CLI convenience it's possible to pass in subset of properties, |
632 | * which would affect all cpu slots that match it. |
633 | * Ex for pc machine: |
634 | * -smp 4,cores=2,sockets=2 -numa node,nodeid=0 -numa node,nodeid=1 \ |
635 | * -numa cpu,node-id=0,socket_id=0 \ |
636 | * -numa cpu,node-id=1,socket_id=1 |
637 | * will assign all child cores of socket 0 to node 0 and |
638 | * of socket 1 to node 1. |
639 | * |
640 | * On attempt of reassigning (already assigned) cpu slot to another NUMA node, |
641 | * return error. |
642 | * Empty subset is disallowed and function will return with error in this case. |
643 | */ |
644 | void machine_set_cpu_numa_node(MachineState *machine, |
645 | const CpuInstanceProperties *props, Error **errp) |
646 | { |
647 | MachineClass *mc = MACHINE_GET_CLASS(machine); |
648 | bool match = false; |
649 | int i; |
650 | |
651 | if (!mc->possible_cpu_arch_ids) { |
652 | error_setg(errp, "mapping of CPUs to NUMA node is not supported" ); |
653 | return; |
654 | } |
655 | |
656 | /* disabling node mapping is not supported, forbid it */ |
657 | assert(props->has_node_id); |
658 | |
659 | /* force board to initialize possible_cpus if it hasn't been done yet */ |
660 | mc->possible_cpu_arch_ids(machine); |
661 | |
662 | for (i = 0; i < machine->possible_cpus->len; i++) { |
663 | CPUArchId *slot = &machine->possible_cpus->cpus[i]; |
664 | |
665 | /* reject unsupported by board properties */ |
666 | if (props->has_thread_id && !slot->props.has_thread_id) { |
667 | error_setg(errp, "thread-id is not supported" ); |
668 | return; |
669 | } |
670 | |
671 | if (props->has_core_id && !slot->props.has_core_id) { |
672 | error_setg(errp, "core-id is not supported" ); |
673 | return; |
674 | } |
675 | |
676 | if (props->has_socket_id && !slot->props.has_socket_id) { |
677 | error_setg(errp, "socket-id is not supported" ); |
678 | return; |
679 | } |
680 | |
681 | if (props->has_die_id && !slot->props.has_die_id) { |
682 | error_setg(errp, "die-id is not supported" ); |
683 | return; |
684 | } |
685 | |
686 | /* skip slots with explicit mismatch */ |
687 | if (props->has_thread_id && props->thread_id != slot->props.thread_id) { |
688 | continue; |
689 | } |
690 | |
691 | if (props->has_core_id && props->core_id != slot->props.core_id) { |
692 | continue; |
693 | } |
694 | |
695 | if (props->has_die_id && props->die_id != slot->props.die_id) { |
696 | continue; |
697 | } |
698 | |
699 | if (props->has_socket_id && props->socket_id != slot->props.socket_id) { |
700 | continue; |
701 | } |
702 | |
703 | /* reject assignment if slot is already assigned, for compatibility |
704 | * of legacy cpu_index mapping with SPAPR core based mapping do not |
705 | * error out if cpu thread and matched core have the same node-id */ |
706 | if (slot->props.has_node_id && |
707 | slot->props.node_id != props->node_id) { |
708 | error_setg(errp, "CPU is already assigned to node-id: %" PRId64, |
709 | slot->props.node_id); |
710 | return; |
711 | } |
712 | |
713 | /* assign slot to node as it's matched '-numa cpu' key */ |
714 | match = true; |
715 | slot->props.node_id = props->node_id; |
716 | slot->props.has_node_id = props->has_node_id; |
717 | } |
718 | |
719 | if (!match) { |
720 | error_setg(errp, "no match found" ); |
721 | } |
722 | } |
723 | |
724 | static void smp_parse(MachineState *ms, QemuOpts *opts) |
725 | { |
726 | if (opts) { |
727 | unsigned cpus = qemu_opt_get_number(opts, "cpus" , 0); |
728 | unsigned sockets = qemu_opt_get_number(opts, "sockets" , 0); |
729 | unsigned cores = qemu_opt_get_number(opts, "cores" , 0); |
730 | unsigned threads = qemu_opt_get_number(opts, "threads" , 0); |
731 | |
732 | /* compute missing values, prefer sockets over cores over threads */ |
733 | if (cpus == 0 || sockets == 0) { |
734 | cores = cores > 0 ? cores : 1; |
735 | threads = threads > 0 ? threads : 1; |
736 | if (cpus == 0) { |
737 | sockets = sockets > 0 ? sockets : 1; |
738 | cpus = cores * threads * sockets; |
739 | } else { |
740 | ms->smp.max_cpus = |
741 | qemu_opt_get_number(opts, "maxcpus" , cpus); |
742 | sockets = ms->smp.max_cpus / (cores * threads); |
743 | } |
744 | } else if (cores == 0) { |
745 | threads = threads > 0 ? threads : 1; |
746 | cores = cpus / (sockets * threads); |
747 | cores = cores > 0 ? cores : 1; |
748 | } else if (threads == 0) { |
749 | threads = cpus / (cores * sockets); |
750 | threads = threads > 0 ? threads : 1; |
751 | } else if (sockets * cores * threads < cpus) { |
752 | error_report("cpu topology: " |
753 | "sockets (%u) * cores (%u) * threads (%u) < " |
754 | "smp_cpus (%u)" , |
755 | sockets, cores, threads, cpus); |
756 | exit(1); |
757 | } |
758 | |
759 | ms->smp.max_cpus = |
760 | qemu_opt_get_number(opts, "maxcpus" , cpus); |
761 | |
762 | if (ms->smp.max_cpus < cpus) { |
763 | error_report("maxcpus must be equal to or greater than smp" ); |
764 | exit(1); |
765 | } |
766 | |
767 | if (sockets * cores * threads > ms->smp.max_cpus) { |
768 | error_report("cpu topology: " |
769 | "sockets (%u) * cores (%u) * threads (%u) > " |
770 | "maxcpus (%u)" , |
771 | sockets, cores, threads, |
772 | ms->smp.max_cpus); |
773 | exit(1); |
774 | } |
775 | |
776 | if (sockets * cores * threads != ms->smp.max_cpus) { |
777 | warn_report("Invalid CPU topology deprecated: " |
778 | "sockets (%u) * cores (%u) * threads (%u) " |
779 | "!= maxcpus (%u)" , |
780 | sockets, cores, threads, |
781 | ms->smp.max_cpus); |
782 | } |
783 | |
784 | ms->smp.cpus = cpus; |
785 | ms->smp.cores = cores; |
786 | ms->smp.threads = threads; |
787 | } |
788 | |
789 | if (ms->smp.cpus > 1) { |
790 | Error *blocker = NULL; |
791 | error_setg(&blocker, QERR_REPLAY_NOT_SUPPORTED, "smp" ); |
792 | replay_add_blocker(blocker); |
793 | } |
794 | } |
795 | |
796 | static void machine_class_init(ObjectClass *oc, void *data) |
797 | { |
798 | MachineClass *mc = MACHINE_CLASS(oc); |
799 | |
800 | /* Default 128 MB as guest ram size */ |
801 | mc->default_ram_size = 128 * MiB; |
802 | mc->rom_file_has_mr = true; |
803 | mc->smp_parse = smp_parse; |
804 | |
805 | /* numa node memory size aligned on 8MB by default. |
806 | * On Linux, each node's border has to be 8MB aligned |
807 | */ |
808 | mc->numa_mem_align_shift = 23; |
809 | mc->numa_auto_assign_ram = numa_default_auto_assign_ram; |
810 | |
811 | object_class_property_add_str(oc, "accel" , |
812 | machine_get_accel, machine_set_accel, &error_abort); |
813 | object_class_property_set_description(oc, "accel" , |
814 | "Accelerator list" , &error_abort); |
815 | |
816 | object_class_property_add(oc, "kernel-irqchip" , "on|off|split" , |
817 | NULL, machine_set_kernel_irqchip, |
818 | NULL, NULL, &error_abort); |
819 | object_class_property_set_description(oc, "kernel-irqchip" , |
820 | "Configure KVM in-kernel irqchip" , &error_abort); |
821 | |
822 | object_class_property_add(oc, "kvm-shadow-mem" , "int" , |
823 | machine_get_kvm_shadow_mem, machine_set_kvm_shadow_mem, |
824 | NULL, NULL, &error_abort); |
825 | object_class_property_set_description(oc, "kvm-shadow-mem" , |
826 | "KVM shadow MMU size" , &error_abort); |
827 | |
828 | object_class_property_add_str(oc, "kernel" , |
829 | machine_get_kernel, machine_set_kernel, &error_abort); |
830 | object_class_property_set_description(oc, "kernel" , |
831 | "Linux kernel image file" , &error_abort); |
832 | |
833 | object_class_property_add_str(oc, "initrd" , |
834 | machine_get_initrd, machine_set_initrd, &error_abort); |
835 | object_class_property_set_description(oc, "initrd" , |
836 | "Linux initial ramdisk file" , &error_abort); |
837 | |
838 | object_class_property_add_str(oc, "append" , |
839 | machine_get_append, machine_set_append, &error_abort); |
840 | object_class_property_set_description(oc, "append" , |
841 | "Linux kernel command line" , &error_abort); |
842 | |
843 | object_class_property_add_str(oc, "dtb" , |
844 | machine_get_dtb, machine_set_dtb, &error_abort); |
845 | object_class_property_set_description(oc, "dtb" , |
846 | "Linux kernel device tree file" , &error_abort); |
847 | |
848 | object_class_property_add_str(oc, "dumpdtb" , |
849 | machine_get_dumpdtb, machine_set_dumpdtb, &error_abort); |
850 | object_class_property_set_description(oc, "dumpdtb" , |
851 | "Dump current dtb to a file and quit" , &error_abort); |
852 | |
853 | object_class_property_add(oc, "phandle-start" , "int" , |
854 | machine_get_phandle_start, machine_set_phandle_start, |
855 | NULL, NULL, &error_abort); |
856 | object_class_property_set_description(oc, "phandle-start" , |
857 | "The first phandle ID we may generate dynamically" , &error_abort); |
858 | |
859 | object_class_property_add_str(oc, "dt-compatible" , |
860 | machine_get_dt_compatible, machine_set_dt_compatible, &error_abort); |
861 | object_class_property_set_description(oc, "dt-compatible" , |
862 | "Overrides the \"compatible\" property of the dt root node" , |
863 | &error_abort); |
864 | |
865 | object_class_property_add_bool(oc, "dump-guest-core" , |
866 | machine_get_dump_guest_core, machine_set_dump_guest_core, &error_abort); |
867 | object_class_property_set_description(oc, "dump-guest-core" , |
868 | "Include guest memory in a core dump" , &error_abort); |
869 | |
870 | object_class_property_add_bool(oc, "mem-merge" , |
871 | machine_get_mem_merge, machine_set_mem_merge, &error_abort); |
872 | object_class_property_set_description(oc, "mem-merge" , |
873 | "Enable/disable memory merge support" , &error_abort); |
874 | |
875 | object_class_property_add_bool(oc, "usb" , |
876 | machine_get_usb, machine_set_usb, &error_abort); |
877 | object_class_property_set_description(oc, "usb" , |
878 | "Set on/off to enable/disable usb" , &error_abort); |
879 | |
880 | object_class_property_add_bool(oc, "graphics" , |
881 | machine_get_graphics, machine_set_graphics, &error_abort); |
882 | object_class_property_set_description(oc, "graphics" , |
883 | "Set on/off to enable/disable graphics emulation" , &error_abort); |
884 | |
885 | object_class_property_add_bool(oc, "igd-passthru" , |
886 | machine_get_igd_gfx_passthru, machine_set_igd_gfx_passthru, |
887 | &error_abort); |
888 | object_class_property_set_description(oc, "igd-passthru" , |
889 | "Set on/off to enable/disable igd passthrou" , &error_abort); |
890 | |
891 | object_class_property_add_str(oc, "firmware" , |
892 | machine_get_firmware, machine_set_firmware, |
893 | &error_abort); |
894 | object_class_property_set_description(oc, "firmware" , |
895 | "Firmware image" , &error_abort); |
896 | |
897 | object_class_property_add_bool(oc, "suppress-vmdesc" , |
898 | machine_get_suppress_vmdesc, machine_set_suppress_vmdesc, |
899 | &error_abort); |
900 | object_class_property_set_description(oc, "suppress-vmdesc" , |
901 | "Set on to disable self-describing migration" , &error_abort); |
902 | |
903 | object_class_property_add_bool(oc, "enforce-config-section" , |
904 | machine_get_enforce_config_section, machine_set_enforce_config_section, |
905 | &error_abort); |
906 | object_class_property_set_description(oc, "enforce-config-section" , |
907 | "Set on to enforce configuration section migration" , &error_abort); |
908 | |
909 | object_class_property_add_str(oc, "memory-encryption" , |
910 | machine_get_memory_encryption, machine_set_memory_encryption, |
911 | &error_abort); |
912 | object_class_property_set_description(oc, "memory-encryption" , |
913 | "Set memory encryption object to use" , &error_abort); |
914 | } |
915 | |
916 | static void machine_class_base_init(ObjectClass *oc, void *data) |
917 | { |
918 | if (!object_class_is_abstract(oc)) { |
919 | MachineClass *mc = MACHINE_CLASS(oc); |
920 | const char *cname = object_class_get_name(oc); |
921 | assert(g_str_has_suffix(cname, TYPE_MACHINE_SUFFIX)); |
922 | mc->name = g_strndup(cname, |
923 | strlen(cname) - strlen(TYPE_MACHINE_SUFFIX)); |
924 | mc->compat_props = g_ptr_array_new(); |
925 | } |
926 | } |
927 | |
928 | static void machine_initfn(Object *obj) |
929 | { |
930 | MachineState *ms = MACHINE(obj); |
931 | MachineClass *mc = MACHINE_GET_CLASS(obj); |
932 | |
933 | ms->kernel_irqchip_allowed = true; |
934 | ms->kernel_irqchip_split = mc->default_kernel_irqchip_split; |
935 | ms->kvm_shadow_mem = -1; |
936 | ms->dump_guest_core = true; |
937 | ms->mem_merge = true; |
938 | ms->enable_graphics = true; |
939 | |
940 | if (mc->nvdimm_supported) { |
941 | Object *obj = OBJECT(ms); |
942 | |
943 | ms->nvdimms_state = g_new0(NVDIMMState, 1); |
944 | object_property_add_bool(obj, "nvdimm" , |
945 | machine_get_nvdimm, machine_set_nvdimm, |
946 | &error_abort); |
947 | object_property_set_description(obj, "nvdimm" , |
948 | "Set on/off to enable/disable " |
949 | "NVDIMM instantiation" , NULL); |
950 | |
951 | object_property_add_str(obj, "nvdimm-persistence" , |
952 | machine_get_nvdimm_persistence, |
953 | machine_set_nvdimm_persistence, |
954 | &error_abort); |
955 | object_property_set_description(obj, "nvdimm-persistence" , |
956 | "Set NVDIMM persistence" |
957 | "Valid values are cpu, mem-ctrl" , |
958 | NULL); |
959 | } |
960 | |
961 | if (mc->numa_mem_supported) { |
962 | ms->numa_state = g_new0(NumaState, 1); |
963 | } |
964 | |
965 | /* Register notifier when init is done for sysbus sanity checks */ |
966 | ms->sysbus_notifier.notify = machine_init_notify; |
967 | qemu_add_machine_init_done_notifier(&ms->sysbus_notifier); |
968 | } |
969 | |
970 | static void machine_finalize(Object *obj) |
971 | { |
972 | MachineState *ms = MACHINE(obj); |
973 | |
974 | g_free(ms->accel); |
975 | g_free(ms->kernel_filename); |
976 | g_free(ms->initrd_filename); |
977 | g_free(ms->kernel_cmdline); |
978 | g_free(ms->dtb); |
979 | g_free(ms->dumpdtb); |
980 | g_free(ms->dt_compatible); |
981 | g_free(ms->firmware); |
982 | g_free(ms->device_memory); |
983 | g_free(ms->nvdimms_state); |
984 | g_free(ms->numa_state); |
985 | } |
986 | |
987 | bool machine_usb(MachineState *machine) |
988 | { |
989 | return machine->usb; |
990 | } |
991 | |
992 | bool machine_kernel_irqchip_allowed(MachineState *machine) |
993 | { |
994 | return machine->kernel_irqchip_allowed; |
995 | } |
996 | |
997 | bool machine_kernel_irqchip_required(MachineState *machine) |
998 | { |
999 | return machine->kernel_irqchip_required; |
1000 | } |
1001 | |
1002 | bool machine_kernel_irqchip_split(MachineState *machine) |
1003 | { |
1004 | return machine->kernel_irqchip_split; |
1005 | } |
1006 | |
1007 | int machine_kvm_shadow_mem(MachineState *machine) |
1008 | { |
1009 | return machine->kvm_shadow_mem; |
1010 | } |
1011 | |
1012 | int machine_phandle_start(MachineState *machine) |
1013 | { |
1014 | return machine->phandle_start; |
1015 | } |
1016 | |
1017 | bool machine_dump_guest_core(MachineState *machine) |
1018 | { |
1019 | return machine->dump_guest_core; |
1020 | } |
1021 | |
1022 | bool machine_mem_merge(MachineState *machine) |
1023 | { |
1024 | return machine->mem_merge; |
1025 | } |
1026 | |
1027 | static char *cpu_slot_to_string(const CPUArchId *cpu) |
1028 | { |
1029 | GString *s = g_string_new(NULL); |
1030 | if (cpu->props.has_socket_id) { |
1031 | g_string_append_printf(s, "socket-id: %" PRId64, cpu->props.socket_id); |
1032 | } |
1033 | if (cpu->props.has_die_id) { |
1034 | g_string_append_printf(s, "die-id: %" PRId64, cpu->props.die_id); |
1035 | } |
1036 | if (cpu->props.has_core_id) { |
1037 | if (s->len) { |
1038 | g_string_append_printf(s, ", " ); |
1039 | } |
1040 | g_string_append_printf(s, "core-id: %" PRId64, cpu->props.core_id); |
1041 | } |
1042 | if (cpu->props.has_thread_id) { |
1043 | if (s->len) { |
1044 | g_string_append_printf(s, ", " ); |
1045 | } |
1046 | g_string_append_printf(s, "thread-id: %" PRId64, cpu->props.thread_id); |
1047 | } |
1048 | return g_string_free(s, false); |
1049 | } |
1050 | |
1051 | static void machine_numa_finish_cpu_init(MachineState *machine) |
1052 | { |
1053 | int i; |
1054 | bool default_mapping; |
1055 | GString *s = g_string_new(NULL); |
1056 | MachineClass *mc = MACHINE_GET_CLASS(machine); |
1057 | const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(machine); |
1058 | |
1059 | assert(machine->numa_state->num_nodes); |
1060 | for (i = 0; i < possible_cpus->len; i++) { |
1061 | if (possible_cpus->cpus[i].props.has_node_id) { |
1062 | break; |
1063 | } |
1064 | } |
1065 | default_mapping = (i == possible_cpus->len); |
1066 | |
1067 | for (i = 0; i < possible_cpus->len; i++) { |
1068 | const CPUArchId *cpu_slot = &possible_cpus->cpus[i]; |
1069 | |
1070 | if (!cpu_slot->props.has_node_id) { |
1071 | /* fetch default mapping from board and enable it */ |
1072 | CpuInstanceProperties props = cpu_slot->props; |
1073 | |
1074 | props.node_id = mc->get_default_cpu_node_id(machine, i); |
1075 | if (!default_mapping) { |
1076 | /* record slots with not set mapping, |
1077 | * TODO: make it hard error in future */ |
1078 | char *cpu_str = cpu_slot_to_string(cpu_slot); |
1079 | g_string_append_printf(s, "%sCPU %d [%s]" , |
1080 | s->len ? ", " : "" , i, cpu_str); |
1081 | g_free(cpu_str); |
1082 | |
1083 | /* non mapped cpus used to fallback to node 0 */ |
1084 | props.node_id = 0; |
1085 | } |
1086 | |
1087 | props.has_node_id = true; |
1088 | machine_set_cpu_numa_node(machine, &props, &error_fatal); |
1089 | } |
1090 | } |
1091 | if (s->len && !qtest_enabled()) { |
1092 | warn_report("CPU(s) not present in any NUMA nodes: %s" , |
1093 | s->str); |
1094 | warn_report("All CPU(s) up to maxcpus should be described " |
1095 | "in NUMA config, ability to start up with partial NUMA " |
1096 | "mappings is obsoleted and will be removed in future" ); |
1097 | } |
1098 | g_string_free(s, true); |
1099 | } |
1100 | |
1101 | void machine_run_board_init(MachineState *machine) |
1102 | { |
1103 | MachineClass *machine_class = MACHINE_GET_CLASS(machine); |
1104 | |
1105 | if (machine_class->numa_mem_supported) { |
1106 | numa_complete_configuration(machine); |
1107 | if (machine->numa_state->num_nodes) { |
1108 | machine_numa_finish_cpu_init(machine); |
1109 | } |
1110 | } |
1111 | |
1112 | /* If the machine supports the valid_cpu_types check and the user |
1113 | * specified a CPU with -cpu check here that the user CPU is supported. |
1114 | */ |
1115 | if (machine_class->valid_cpu_types && machine->cpu_type) { |
1116 | ObjectClass *class = object_class_by_name(machine->cpu_type); |
1117 | int i; |
1118 | |
1119 | for (i = 0; machine_class->valid_cpu_types[i]; i++) { |
1120 | if (object_class_dynamic_cast(class, |
1121 | machine_class->valid_cpu_types[i])) { |
1122 | /* The user specificed CPU is in the valid field, we are |
1123 | * good to go. |
1124 | */ |
1125 | break; |
1126 | } |
1127 | } |
1128 | |
1129 | if (!machine_class->valid_cpu_types[i]) { |
1130 | /* The user specified CPU is not valid */ |
1131 | error_report("Invalid CPU type: %s" , machine->cpu_type); |
1132 | error_printf("The valid types are: %s" , |
1133 | machine_class->valid_cpu_types[0]); |
1134 | for (i = 1; machine_class->valid_cpu_types[i]; i++) { |
1135 | error_printf(", %s" , machine_class->valid_cpu_types[i]); |
1136 | } |
1137 | error_printf("\n" ); |
1138 | |
1139 | exit(1); |
1140 | } |
1141 | } |
1142 | |
1143 | machine_class->init(machine); |
1144 | } |
1145 | |
1146 | static const TypeInfo machine_info = { |
1147 | .name = TYPE_MACHINE, |
1148 | .parent = TYPE_OBJECT, |
1149 | .abstract = true, |
1150 | .class_size = sizeof(MachineClass), |
1151 | .class_init = machine_class_init, |
1152 | .class_base_init = machine_class_base_init, |
1153 | .instance_size = sizeof(MachineState), |
1154 | .instance_init = machine_initfn, |
1155 | .instance_finalize = machine_finalize, |
1156 | }; |
1157 | |
1158 | static void machine_register_types(void) |
1159 | { |
1160 | type_register_static(&machine_info); |
1161 | } |
1162 | |
1163 | type_init(machine_register_types) |
1164 | |