@samitouri / QOSamiQemu / commits / a8aa1b220c

hw/tpm: Add support for VM migration with TPM CRB chunking

- Add subsection in VMState for TPM CRB with the newly introduced command and response buffer GByteArrays, along with a needed callback, so that newer QEMU only sends the buffers if it is necessary. - Implement a migration blocker to prevent migration of the VM if the user manually enables chunking capability, cap-chunk, but the machine type does not support it, using a new hw_compat property called allow_chunk_migration. - Add a post_load_errp hook so that during a migration, the buffers are validated before destination VM is started. Signed-off-by: Arun Menon <armenon@redhat.com> Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> Link: https://lore.kernel.org/qemu-devel/20260506075813.120781-7-armenon@redhat.com Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>

Arun Menon committed May 6, 2026 at 13:28 UTC a8aa1b220c71442bac961bdafa64da8b9604686b
2 files changed +68
hw/core/machine.c
+1
@@ -42,6 +42,7 @@
42 GlobalProperty hw_compat_11_0[] = {
43 { "chardev-vc", "encoding", "cp437" },
44 { "tpm-crb", "cap-chunk", "off" },
45 + { "tpm-crb", "x-allow-chunk-migration", "off" },
46 };
47 const size_t hw_compat_11_0_len = G_N_ELEMENTS(hw_compat_11_0);
48
hw/tpm/tpm_crb.c
+67
@@ -24,6 +24,7 @@
24 #include "hw/pci/pci_ids.h"
25 #include "hw/acpi/tpm.h"
26 #include "migration/vmstate.h"
27 +#include "migration/blocker.h"
28 #include "system/tpm_backend.h"
29 #include "system/tpm_util.h"
30 #include "system/reset.h"
@@ -50,6 +51,8 @@ struct CRBState {
51 TPMPPI ppi;
52
53 bool cap_chunk;
54 + bool allow_chunk_migration;
55 + Error *migration_blocker;
56 };
57 typedef struct CRBState CRBState;
58
@@ -349,18 +352,68 @@ static int tpm_crb_pre_save(void *opaque)
352 return 0;
353 }
354
355 +static bool tpm_crb_chunk_needed(void *opaque)
356 +{
357 + CRBState *s = opaque;
358 +
359 + if (!s->allow_chunk_migration) {
360 + return false;
361 + }
362 +
363 + return ((s->command_buffer && s->command_buffer->len > 0) ||
364 + (s->response_buffer && s->response_buffer->len > 0));
365 +}
366 +
367 +static bool tpm_crb_chunk_post_load(void *opaque, int version_id, Error **errp)
368 +{
369 + CRBState *s = opaque;
370 +
371 + /*
372 + * The external TPM emulator (example swtpm) determines the backend
373 + * buffer capacity (s->be_buffer_size). This check ensures that if we
374 + * migrate from a source with a PQC-enabled emulator that supports
375 + * larger buffers to a destination with a non-PQC emulator, the
376 + * migrated data does not exceed the destination's capacity.
377 + */
378 + if (s->response_buffer->len > s->be_buffer_size ||
379 + s->command_buffer->len > s->be_buffer_size) {
380 + error_setg(errp, "tpm-crb: Buffer sizes exceed backend capacity");
381 + return false;
382 + }
383 + return true;
384 +}
385 +
386 +static const VMStateDescription vmstate_tpm_crb_chunk = {
387 + .name = "tpm-crb/chunk",
388 + .version_id = 0,
389 + .needed = tpm_crb_chunk_needed,
390 + .post_load_errp = tpm_crb_chunk_post_load,
391 + .fields = (const VMStateField[]) {
392 + VMSTATE_GBYTEARRAY(command_buffer, CRBState, 0),
393 + VMSTATE_GBYTEARRAY(response_buffer, CRBState, 0),
394 + VMSTATE_UINT32(response_offset, CRBState),
395 + VMSTATE_END_OF_LIST()
396 + }
397 +};
398 +
399 static const VMStateDescription vmstate_tpm_crb = {
400 .name = "tpm-crb",
401 .pre_save = tpm_crb_pre_save,
402 .fields = (const VMStateField[]) {
403 VMSTATE_UINT32_ARRAY(regs, CRBState, TPM_CRB_R_MAX),
404 VMSTATE_END_OF_LIST(),
405 + },
406 + .subsections = (const VMStateDescription * const []) {
407 + &vmstate_tpm_crb_chunk,
408 + NULL,
409 }
410 };
411
412 static const Property tpm_crb_properties[] = {
413 DEFINE_PROP_TPMBE("tpmdev", CRBState, tpmbe),
414 DEFINE_PROP_BOOL("cap-chunk", CRBState, cap_chunk, true),
415 + DEFINE_PROP_BOOL("x-allow-chunk-migration", CRBState,
416 + allow_chunk_migration, true),
417 };
418
419 static void tpm_crb_reset(void *dev)
@@ -415,6 +468,7 @@ static void tpm_crb_reset(void *dev)
468 static void tpm_crb_realize(DeviceState *dev, Error **errp)
469 {
470 CRBState *s = CRB(dev);
471 + int ret;
472
473 if (!tpm_find()) {
474 error_setg(errp, "at most one TPM device is permitted");
@@ -424,6 +478,15 @@ static void tpm_crb_realize(DeviceState *dev, Error **errp)
478 error_setg(errp, "'tpmdev' property is required");
479 return;
480 }
481 + if (s->cap_chunk && !s->allow_chunk_migration) {
482 + error_setg(&s->migration_blocker,
483 + "The tpm-crb device does not support chunk migration with "
484 + "machine version less than 11.1");
485 + ret = migrate_add_blocker_normal(&s->migration_blocker, errp);
486 + if (ret < 0) {
487 + return;
488 + }
489 + }
490
491 memory_region_init_io(&s->mmio, OBJECT(s), &tpm_crb_memory_ops, s,
492 "tpm-crb-mmio", sizeof(s->regs));
@@ -454,6 +517,10 @@ static void tpm_crb_unrealize(DeviceState *dev)
517
518 g_clear_pointer(&s->command_buffer, g_byte_array_unref);
519 g_clear_pointer(&s->response_buffer, g_byte_array_unref);
520 +
521 + if (s->migration_blocker) {
522 + migrate_del_blocker(&s->migration_blocker);
523 + }
524 }
525
526 static void tpm_crb_class_init(ObjectClass *klass, const void *data)