Commit a8aa1b220c for qemu.org
commit a8aa1b220c71442bac961bdafa64da8b9604686b
Author: Arun Menon <armenon@redhat.com>
Date: Wed May 6 13:28:13 2026 +0530
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>
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 00eb3432a7..17970b78b6 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -42,6 +42,7 @@
GlobalProperty hw_compat_11_0[] = {
{ "chardev-vc", "encoding", "cp437" },
{ "tpm-crb", "cap-chunk", "off" },
+ { "tpm-crb", "x-allow-chunk-migration", "off" },
};
const size_t hw_compat_11_0_len = G_N_ELEMENTS(hw_compat_11_0);
diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
index f85df08185..54fa2042b5 100644
--- a/hw/tpm/tpm_crb.c
+++ b/hw/tpm/tpm_crb.c
@@ -24,6 +24,7 @@
#include "hw/pci/pci_ids.h"
#include "hw/acpi/tpm.h"
#include "migration/vmstate.h"
+#include "migration/blocker.h"
#include "system/tpm_backend.h"
#include "system/tpm_util.h"
#include "system/reset.h"
@@ -50,6 +51,8 @@ struct CRBState {
TPMPPI ppi;
bool cap_chunk;
+ bool allow_chunk_migration;
+ Error *migration_blocker;
};
typedef struct CRBState CRBState;
@@ -349,18 +352,68 @@ static int tpm_crb_pre_save(void *opaque)
return 0;
}
+static bool tpm_crb_chunk_needed(void *opaque)
+{
+ CRBState *s = opaque;
+
+ if (!s->allow_chunk_migration) {
+ return false;
+ }
+
+ return ((s->command_buffer && s->command_buffer->len > 0) ||
+ (s->response_buffer && s->response_buffer->len > 0));
+}
+
+static bool tpm_crb_chunk_post_load(void *opaque, int version_id, Error **errp)
+{
+ CRBState *s = opaque;
+
+ /*
+ * The external TPM emulator (example swtpm) determines the backend
+ * buffer capacity (s->be_buffer_size). This check ensures that if we
+ * migrate from a source with a PQC-enabled emulator that supports
+ * larger buffers to a destination with a non-PQC emulator, the
+ * migrated data does not exceed the destination's capacity.
+ */
+ if (s->response_buffer->len > s->be_buffer_size ||
+ s->command_buffer->len > s->be_buffer_size) {
+ error_setg(errp, "tpm-crb: Buffer sizes exceed backend capacity");
+ return false;
+ }
+ return true;
+}
+
+static const VMStateDescription vmstate_tpm_crb_chunk = {
+ .name = "tpm-crb/chunk",
+ .version_id = 0,
+ .needed = tpm_crb_chunk_needed,
+ .post_load_errp = tpm_crb_chunk_post_load,
+ .fields = (const VMStateField[]) {
+ VMSTATE_GBYTEARRAY(command_buffer, CRBState, 0),
+ VMSTATE_GBYTEARRAY(response_buffer, CRBState, 0),
+ VMSTATE_UINT32(response_offset, CRBState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_tpm_crb = {
.name = "tpm-crb",
.pre_save = tpm_crb_pre_save,
.fields = (const VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, CRBState, TPM_CRB_R_MAX),
VMSTATE_END_OF_LIST(),
+ },
+ .subsections = (const VMStateDescription * const []) {
+ &vmstate_tpm_crb_chunk,
+ NULL,
}
};
static const Property tpm_crb_properties[] = {
DEFINE_PROP_TPMBE("tpmdev", CRBState, tpmbe),
DEFINE_PROP_BOOL("cap-chunk", CRBState, cap_chunk, true),
+ DEFINE_PROP_BOOL("x-allow-chunk-migration", CRBState,
+ allow_chunk_migration, true),
};
static void tpm_crb_reset(void *dev)
@@ -415,6 +468,7 @@ static void tpm_crb_reset(void *dev)
static void tpm_crb_realize(DeviceState *dev, Error **errp)
{
CRBState *s = CRB(dev);
+ int ret;
if (!tpm_find()) {
error_setg(errp, "at most one TPM device is permitted");
@@ -424,6 +478,15 @@ static void tpm_crb_realize(DeviceState *dev, Error **errp)
error_setg(errp, "'tpmdev' property is required");
return;
}
+ if (s->cap_chunk && !s->allow_chunk_migration) {
+ error_setg(&s->migration_blocker,
+ "The tpm-crb device does not support chunk migration with "
+ "machine version less than 11.1");
+ ret = migrate_add_blocker_normal(&s->migration_blocker, errp);
+ if (ret < 0) {
+ return;
+ }
+ }
memory_region_init_io(&s->mmio, OBJECT(s), &tpm_crb_memory_ops, s,
"tpm-crb-mmio", sizeof(s->regs));
@@ -454,6 +517,10 @@ static void tpm_crb_unrealize(DeviceState *dev)
g_clear_pointer(&s->command_buffer, g_byte_array_unref);
g_clear_pointer(&s->response_buffer, g_byte_array_unref);
+
+ if (s->migration_blocker) {
+ migrate_del_blocker(&s->migration_blocker);
+ }
}
static void tpm_crb_class_init(ObjectClass *klass, const void *data)