* [PATCH v7 0/6] hw/tpm: CRB chunking capability to handle PQC
@ 2026-05-06 7:58 Arun Menon
2026-05-06 7:58 ` [PATCH v7 1/6] hw/tpm: Add TPM CRB chunking fields Arun Menon
` (5 more replies)
0 siblings, 6 replies; 7+ messages in thread
From: Arun Menon @ 2026-05-06 7:58 UTC (permalink / raw)
To: qemu-devel
Cc: Zhao Liu, Marcel Apfelbaum, Ani Sinha, Fabiano Rosas,
marcandre.lureau, Stefan Berger, Philippe Mathieu-Daudé,
Yanan Wang, Paolo Bonzini, Laurent Vivier, Michael S. Tsirkin,
Igor Mammedov, Arun Menon
The move to Post Quantum Cryptography (PQC) changes how we manage
memory buffers. Unlike classic crypto algorithms like RSA or ECC which
used small keys and signatures, PQC algorithms require larger buffers.
The new version of TCG TPM v185 (currently under review [1]) supports
sending data/commands in chunks for the CRB (Command Response Buffer)
interface. This is in line with the initiative to support PQC algorithms.
This series implements the logic to send and receive data from the
linux guest to the TPM backend in chunks, thereby allowing the
guest to send larger data buffers. We introduce 2 new control registers
called nextChunk and crbRspRetry that will control the START. We also
add the CRB Interface Identifier called CapCRBChunk that is set to 1
indicating that the device supports chunking. The default maximum
chunk/buffer size is 3968 (4096 - 128) bytes.
During a send operation, the guest driver places data in the CRB buffer
and signals nextChunk for each segment until the final chunk is reached.
Upon receiving the START signal, QEMU appends the final chunk to its
internal buffer and dispatches the complete command to the TPM backend.
For responses, the backend's output is buffered. The guest consumes the
first chunk once the START bit is cleared. Subsequent chunks are
retrieved by the guest toggling the nextChunk bit, which advances the
internal buffer offset and populates the CRB data window.
For this to work, the linux guest tpm driver will also have to
a) probe if CRB chunking is supported
b) send data in chunks if the command length exceeds the chunk size.
c) receive data in chunks by sending a nextChunk signal and accumulate.
These patches are posted upstream:
https://lore.kernel.org/lkml/20260324181244.17741-1-armenon@redhat.com/
Dependencies:
This series has a hard dependency on the following patches currently on
the mailing list. They must be applied first for this series to function
correctly:
1. [PATCH 1/2] migration/vmstate: Add VMState support for GByteArray
Link: https://lore.kernel.org/all/20260422082214.10390-2-armenon@redhat.com/
2. [PATCH for-11.1] hw: add compat machines for 11.1
Link: https://lore.kernel.org/all/20260331140347.653404-1-cohuck@redhat.com/
[1] https://trustedcomputinggroup.org/wp-content/uploads/PC-Client-Specific-Platform-TPM-Profile-for-TPM-2p0-v1p07_rc1_121225.pdf
v7
--
- Removed error_free() after calling migrate_del_blocker
- Typo fix be_bufer_size to be_buffer_size, and removed a redundant
check.
v6
--
- Removed Stefan Berger's patches to make this series CRB-only. The
complex TIS changes will be posted later.
- Added a comment in the post_load hook.
v5
--
- Expose cap-chunk only if the binary is run with the new machine type
(>11.1). Remove migrate-buffers as this property is not needed.
- Add x-allow-chunk-migration internal property that will help in
blocking migration from a source with 11.1 binary and pre 11.1 machine
type to pre 11.1 binary and pre 11.1 machine type.
In this case, the source supports cap-chunk, but the destination binary
is unaware of the new buffers.
- Add post_load_errp hook, to validate the buffers before the VM is
started at the destination.
- Check if cap-chunk is true before processing nextChunk and crbRspRetry
from the guest. Patches 01, 04 and 06 have undergone changes.
v4
--
- Add migration blocker to prevent data loss and new hw_compat property
called cap_chunk. The chunking feature is now only visible to machine
type 11.1 and higher.
- Rename invoke to Start, to comply with the TCG TPM specification.
- Use g_clear_pointer for safety.
v3
--
Patches 1-6
- Fix the issue with subsequent nextChunk signal from the guest while
the TPM backend is not done processing the previous request.
- Add tpm_crb_unrealize() to clear buffers
- Update hw_compat to 11.1.
- Use newly introduced GByteArray VMStateInfo for migration.
Patches 7-10
- Add Stefan Berger's patches for swtpm profile support, TPM TIS
migration support with extended buffer and related tests.
NOTE: I have removed the "WIP" prefix and the "TODO" regarding dynamic
allocation from Stefan's final patch, as the static 8192-byte limit is
sufficient for the current requirements and passes all local testing.
v2
--
- Add the VM migration support.
- Increase the TIS TPM interface max buffer size to 8192.
Based-on: <20260331140347.653404-1-cohuck@redhat.com>
Based-on: <20260422082214.10390-2-armenon@redhat.com>
Arun Menon (6):
hw/tpm: Add TPM CRB chunking fields
hw/tpm: Refactor CRB_CTRL_START register access
hw/tpm: Add internal buffer state for chunking
hw/tpm: Implement TPM CRB chunking logic
test/qtest: Add test for tpm crb chunking
hw/tpm: Add support for VM migration with TPM CRB chunking
hw/core/machine.c | 5 +-
hw/tpm/tpm_crb.c | 253 ++++++++++++++++++++++++++++---
include/hw/acpi/tpm.h | 5 +-
tests/qtest/tpm-crb-swtpm-test.c | 10 ++
tests/qtest/tpm-util.c | 109 +++++++++++--
tests/qtest/tpm-util.h | 5 +
6 files changed, 351 insertions(+), 36 deletions(-)
--
2.54.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v7 1/6] hw/tpm: Add TPM CRB chunking fields
2026-05-06 7:58 [PATCH v7 0/6] hw/tpm: CRB chunking capability to handle PQC Arun Menon
@ 2026-05-06 7:58 ` Arun Menon
2026-05-06 7:58 ` [PATCH v7 2/6] hw/tpm: Refactor CRB_CTRL_START register access Arun Menon
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Arun Menon @ 2026-05-06 7:58 UTC (permalink / raw)
To: qemu-devel
Cc: Zhao Liu, Marcel Apfelbaum, Ani Sinha, Fabiano Rosas,
marcandre.lureau, Stefan Berger, Philippe Mathieu-Daudé,
Yanan Wang, Paolo Bonzini, Laurent Vivier, Michael S. Tsirkin,
Igor Mammedov, Arun Menon, Stefan Berger
From: Arun Menon <armenon@redhat.com>
- Add new fields to the CRB Interface Identifier and the CRB
Control Start registers.
- CRB_CTRL_START now has 2 new settings, that can be toggled using the
nextChunk and crbRspRetry bits.
- CapCRBChunk bit (10) was Reserved1 previously. The field is reused in
this revision of the specification. Refer to section 6.4.2.2 of [1]
- Add hw_compat global property called cap-chunk because the chunking
feature is only supported for machine type 11.1 and higher.
[1] https://trustedcomputinggroup.org/wp-content/uploads/PC-Client-Specific-Platform-TPM-Profile-for-TPM-2p0-v1p07_Pub.pdf
Signed-off-by: Arun Menon <armenon@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
hw/core/machine.c | 4 +++-
hw/tpm/tpm_crb.c | 6 ++++++
include/hw/acpi/tpm.h | 5 ++++-
3 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 1b661fd36a..4e0a93e231 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -39,7 +39,9 @@
#include "hw/acpi/generic_event_device.h"
#include "qemu/audio.h"
-GlobalProperty hw_compat_11_0[] = {};
+GlobalProperty hw_compat_11_0[] = {
+ { "tpm-crb", "cap-chunk", "off" },
+};
const size_t hw_compat_11_0_len = G_N_ELEMENTS(hw_compat_11_0);
GlobalProperty hw_compat_10_2[] = {
diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
index 02701ab948..bfa09c04cf 100644
--- a/hw/tpm/tpm_crb.c
+++ b/hw/tpm/tpm_crb.c
@@ -44,6 +44,8 @@ struct CRBState {
size_t be_buffer_size;
TPMPPI ppi;
+
+ bool cap_chunk;
};
typedef struct CRBState CRBState;
@@ -58,6 +60,7 @@ DECLARE_INSTANCE_CHECKER(CRBState, CRB,
#define CRB_INTF_CAP_FIFO_NOT_SUPPORTED 0b0
#define CRB_INTF_CAP_CRB_SUPPORTED 0b1
#define CRB_INTF_IF_SELECTOR_CRB 0b1
+#define CRB_INTF_CAP_CRB_CHUNK 0b1
#define CRB_CTRL_CMD_SIZE (TPM_CRB_ADDR_SIZE - A_CRB_DATA_BUFFER)
@@ -227,6 +230,7 @@ static const VMStateDescription vmstate_tpm_crb = {
static const Property tpm_crb_properties[] = {
DEFINE_PROP_TPMBE("tpmdev", CRBState, tpmbe),
+ DEFINE_PROP_BOOL("cap-chunk", CRBState, cap_chunk, true),
};
static void tpm_crb_reset(void *dev)
@@ -258,6 +262,8 @@ static void tpm_crb_reset(void *dev)
CapCRB, CRB_INTF_CAP_CRB_SUPPORTED);
ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
InterfaceSelector, CRB_INTF_IF_SELECTOR_CRB);
+ ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
+ CapCRBChunk, s->cap_chunk ? CRB_INTF_CAP_CRB_CHUNK : 0);
ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
RID, 0b0000);
ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID2,
diff --git a/include/hw/acpi/tpm.h b/include/hw/acpi/tpm.h
index 2ab186a745..782dc8212c 100644
--- a/include/hw/acpi/tpm.h
+++ b/include/hw/acpi/tpm.h
@@ -150,7 +150,7 @@ REG32(CRB_INTF_ID, 0x30)
FIELD(CRB_INTF_ID, InterfaceVersion, 4, 4)
FIELD(CRB_INTF_ID, CapLocality, 8, 1)
FIELD(CRB_INTF_ID, CapCRBIdleBypass, 9, 1)
- FIELD(CRB_INTF_ID, Reserved1, 10, 1)
+ FIELD(CRB_INTF_ID, CapCRBChunk, 10, 1)
FIELD(CRB_INTF_ID, CapDataXferSizeSupport, 11, 2)
FIELD(CRB_INTF_ID, CapFIFO, 13, 1)
FIELD(CRB_INTF_ID, CapCRB, 14, 1)
@@ -169,6 +169,9 @@ REG32(CRB_CTRL_STS, 0x44)
FIELD(CRB_CTRL_STS, tpmIdle, 1, 1)
REG32(CRB_CTRL_CANCEL, 0x48)
REG32(CRB_CTRL_START, 0x4C)
+ FIELD(CRB_CTRL_START, Start, 0, 1)
+ FIELD(CRB_CTRL_START, crbRspRetry, 1, 1)
+ FIELD(CRB_CTRL_START, nextChunk, 2, 1)
REG32(CRB_INT_ENABLED, 0x50)
REG32(CRB_INT_STS, 0x54)
REG32(CRB_CTRL_CMD_SIZE, 0x58)
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v7 2/6] hw/tpm: Refactor CRB_CTRL_START register access
2026-05-06 7:58 [PATCH v7 0/6] hw/tpm: CRB chunking capability to handle PQC Arun Menon
2026-05-06 7:58 ` [PATCH v7 1/6] hw/tpm: Add TPM CRB chunking fields Arun Menon
@ 2026-05-06 7:58 ` Arun Menon
2026-05-06 7:58 ` [PATCH v7 3/6] hw/tpm: Add internal buffer state for chunking Arun Menon
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Arun Menon @ 2026-05-06 7:58 UTC (permalink / raw)
To: qemu-devel
Cc: Zhao Liu, Marcel Apfelbaum, Ani Sinha, Fabiano Rosas,
marcandre.lureau, Stefan Berger, Philippe Mathieu-Daudé,
Yanan Wang, Paolo Bonzini, Laurent Vivier, Michael S. Tsirkin,
Igor Mammedov, Arun Menon, Stefan Berger
From: Arun Menon <armenon@redhat.com>
Replace manual bitwise operations with ARRAY_FIELD_DP32 macros
No functional changes.
Signed-off-by: Arun Menon <armenon@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
hw/tpm/tpm_crb.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
index bfa09c04cf..a0f472652e 100644
--- a/hw/tpm/tpm_crb.c
+++ b/hw/tpm/tpm_crb.c
@@ -146,7 +146,7 @@ static void tpm_crb_mmio_write(void *opaque, hwaddr addr,
tpm_crb_get_active_locty(s) == locty) {
void *mem = memory_region_get_ram_ptr(&s->cmdmem);
- s->regs[R_CRB_CTRL_START] |= CRB_START_INVOKE;
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, Start, 1);
s->cmd = (TPMBackendCmd) {
.in = mem,
.in_len = MIN(tpm_cmd_get_size(mem), s->be_buffer_size),
@@ -195,7 +195,7 @@ static void tpm_crb_request_completed(TPMIf *ti, int ret)
{
CRBState *s = CRB(ti);
- s->regs[R_CRB_CTRL_START] &= ~CRB_START_INVOKE;
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, Start, 0);
if (ret != 0) {
ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS,
tpmSts, 1); /* fatal error */
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v7 3/6] hw/tpm: Add internal buffer state for chunking
2026-05-06 7:58 [PATCH v7 0/6] hw/tpm: CRB chunking capability to handle PQC Arun Menon
2026-05-06 7:58 ` [PATCH v7 1/6] hw/tpm: Add TPM CRB chunking fields Arun Menon
2026-05-06 7:58 ` [PATCH v7 2/6] hw/tpm: Refactor CRB_CTRL_START register access Arun Menon
@ 2026-05-06 7:58 ` Arun Menon
2026-05-06 7:58 ` [PATCH v7 4/6] hw/tpm: Implement TPM CRB chunking logic Arun Menon
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Arun Menon @ 2026-05-06 7:58 UTC (permalink / raw)
To: qemu-devel
Cc: Zhao Liu, Marcel Apfelbaum, Ani Sinha, Fabiano Rosas,
marcandre.lureau, Stefan Berger, Philippe Mathieu-Daudé,
Yanan Wang, Paolo Bonzini, Laurent Vivier, Michael S. Tsirkin,
Igor Mammedov, Arun Menon, Stefan Berger
From: Arun Menon <armenon@redhat.com>
- Introduce GByteArray buffers to hold the command request and response
data during chunked TPM CRB transactions.
- Add helper function to clean them.
Signed-off-by: Arun Menon <armenon@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
---
hw/tpm/tpm_crb.c | 33 +++++++++++++++++++++++++++++----
1 file changed, 29 insertions(+), 4 deletions(-)
diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
index a0f472652e..1c944d7ef2 100644
--- a/hw/tpm/tpm_crb.c
+++ b/hw/tpm/tpm_crb.c
@@ -38,10 +38,13 @@ struct CRBState {
TPMBackend *tpmbe;
TPMBackendCmd cmd;
uint32_t regs[TPM_CRB_R_MAX];
+ size_t be_buffer_size;
MemoryRegion mmio;
MemoryRegion cmdmem;
- size_t be_buffer_size;
+ GByteArray *command_buffer;
+ GByteArray *response_buffer;
+ uint32_t response_offset;
TPMPPI ppi;
@@ -86,6 +89,13 @@ enum crb_cancel {
#define TPM_CRB_NO_LOCALITY 0xff
+static void tpm_crb_clear_internal_buffers(CRBState *s)
+{
+ g_byte_array_set_size(s->response_buffer, 0);
+ g_byte_array_set_size(s->command_buffer, 0);
+ s->response_offset = 0;
+}
+
static uint64_t tpm_crb_mmio_read(void *opaque, hwaddr addr,
unsigned size)
{
@@ -135,9 +145,11 @@ static void tpm_crb_mmio_write(void *opaque, hwaddr addr,
}
break;
case A_CRB_CTRL_CANCEL:
- if (val == CRB_CANCEL_INVOKE &&
- s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) {
- tpm_backend_cancel_cmd(s->tpmbe);
+ if (val == CRB_CANCEL_INVOKE) {
+ if (s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) {
+ tpm_backend_cancel_cmd(s->tpmbe);
+ }
+ tpm_crb_clear_internal_buffers(s);
}
break;
case A_CRB_CTRL_START:
@@ -239,6 +251,7 @@ static void tpm_crb_reset(void *dev)
tpm_ppi_reset(&s->ppi);
tpm_backend_reset(s->tpmbe);
+ tpm_crb_clear_internal_buffers(s);
memset(s->regs, 0, sizeof(s->regs));
@@ -305,6 +318,9 @@ static void tpm_crb_realize(DeviceState *dev, Error **errp)
memory_region_add_subregion(get_system_memory(),
TPM_CRB_ADDR_BASE + sizeof(s->regs), &s->cmdmem);
+ s->command_buffer = g_byte_array_new();
+ s->response_buffer = g_byte_array_new();
+
tpm_ppi_init(&s->ppi, get_system_memory(),
TPM_PPI_ADDR_BASE, OBJECT(s));
@@ -315,12 +331,21 @@ static void tpm_crb_realize(DeviceState *dev, Error **errp)
}
}
+static void tpm_crb_unrealize(DeviceState *dev)
+{
+ CRBState *s = CRB(dev);
+
+ g_clear_pointer(&s->command_buffer, g_byte_array_unref);
+ g_clear_pointer(&s->response_buffer, g_byte_array_unref);
+}
+
static void tpm_crb_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
TPMIfClass *tc = TPM_IF_CLASS(klass);
dc->realize = tpm_crb_realize;
+ dc->unrealize = tpm_crb_unrealize;
device_class_set_props(dc, tpm_crb_properties);
dc->vmsd = &vmstate_tpm_crb;
dc->user_creatable = true;
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v7 4/6] hw/tpm: Implement TPM CRB chunking logic
2026-05-06 7:58 [PATCH v7 0/6] hw/tpm: CRB chunking capability to handle PQC Arun Menon
` (2 preceding siblings ...)
2026-05-06 7:58 ` [PATCH v7 3/6] hw/tpm: Add internal buffer state for chunking Arun Menon
@ 2026-05-06 7:58 ` Arun Menon
2026-05-06 7:58 ` [PATCH v7 5/6] test/qtest: Add test for tpm crb chunking Arun Menon
2026-05-06 7:58 ` [PATCH v7 6/6] hw/tpm: Add support for VM migration with TPM CRB chunking Arun Menon
5 siblings, 0 replies; 7+ messages in thread
From: Arun Menon @ 2026-05-06 7:58 UTC (permalink / raw)
To: qemu-devel
Cc: Zhao Liu, Marcel Apfelbaum, Ani Sinha, Fabiano Rosas,
marcandre.lureau, Stefan Berger, Philippe Mathieu-Daudé,
Yanan Wang, Paolo Bonzini, Laurent Vivier, Michael S. Tsirkin,
Igor Mammedov, Arun Menon, Stefan Berger
From: Arun Menon <armenon@redhat.com>
- Add logic to populate internal TPM command request and response
buffers and to toggle the control registers after each operation.
- The chunk size is limited to CRB_CTRL_CMD_SIZE which is
(TPM_CRB_ADDR_SIZE - A_CRB_DATA_BUFFER). This comes out as 3968 bytes
(4096 - 128 or 0x1000 - 0x80), because 128 bytes are reserved for
control and status registers. In other words, only 3968 bytes are
available for the TPM data.
- With this feature, guests can send commands larger than 3968 bytes.
- Refer section 6.5.3.9 of [1] for implementation details.
[1] https://trustedcomputinggroup.org/wp-content/uploads/PC-Client-Specific-Platform-TPM-Profile-for-TPM-2p0-v1p07_Pub.pdf
Signed-off-by: Arun Menon <armenon@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
hw/tpm/tpm_crb.c | 143 ++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 130 insertions(+), 13 deletions(-)
diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
index 1c944d7ef2..f85df08185 100644
--- a/hw/tpm/tpm_crb.c
+++ b/hw/tpm/tpm_crb.c
@@ -17,6 +17,7 @@
#include "qemu/osdep.h"
#include "qemu/module.h"
+#include "qemu/error-report.h"
#include "qapi/error.h"
#include "system/address-spaces.h"
#include "hw/core/qdev-properties.h"
@@ -66,6 +67,7 @@ DECLARE_INSTANCE_CHECKER(CRBState, CRB,
#define CRB_INTF_CAP_CRB_CHUNK 0b1
#define CRB_CTRL_CMD_SIZE (TPM_CRB_ADDR_SIZE - A_CRB_DATA_BUFFER)
+#define TPM_HEADER_SIZE 10
enum crb_loc_ctrl {
CRB_LOC_CTRL_REQUEST_ACCESS = BIT(0),
@@ -81,6 +83,8 @@ enum crb_ctrl_req {
enum crb_start {
CRB_START_INVOKE = BIT(0),
+ CRB_START_RSP_RETRY = BIT(1),
+ CRB_START_NEXT_CHUNK = BIT(2),
};
enum crb_cancel {
@@ -123,6 +127,69 @@ static uint8_t tpm_crb_get_active_locty(CRBState *s)
return ARRAY_FIELD_EX32(s->regs, CRB_LOC_STATE, activeLocality);
}
+static bool tpm_crb_append_command_request(CRBState *s)
+{
+ /*
+ * The linux guest writes the TPM command to the MMIO region in chunks.
+ * This function appends a chunk from the MMIO region to internal
+ * command_buffer.
+ */
+ void *mem = memory_region_get_ram_ptr(&s->cmdmem);
+ uint32_t to_copy = 0;
+ uint32_t total_request_size = 0;
+
+ /*
+ * The initial call extracts the total TPM command size
+ * from its header. For the subsequent calls, the data already
+ * appended in the command_buffer is used to calculate the total
+ * size, as its header stays the same.
+ */
+ if (s->command_buffer->len == 0) {
+ total_request_size = tpm_cmd_get_size(mem);
+ if (total_request_size < TPM_HEADER_SIZE) {
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS, tpmSts, 1);
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, Start, 0);
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, nextChunk, 0);
+ tpm_crb_clear_internal_buffers(s);
+ error_report("Command size %" PRIu32 " less than "
+ "TPM header size %" PRIu32,
+ total_request_size, (uint32_t)TPM_HEADER_SIZE);
+ return false;
+ }
+ } else {
+ total_request_size = tpm_cmd_get_size(s->command_buffer->data);
+ }
+ total_request_size = MIN(total_request_size, s->be_buffer_size);
+
+ if (total_request_size > s->command_buffer->len) {
+ uint32_t remaining = total_request_size - s->command_buffer->len;
+ to_copy = MIN(remaining, CRB_CTRL_CMD_SIZE);
+ g_byte_array_append(s->command_buffer, (guint8 *)mem, to_copy);
+ }
+ return true;
+}
+
+static void tpm_crb_fill_command_response(CRBState *s)
+{
+ /*
+ * Response from the tpm backend will be stored in the internal
+ * response_buffer. This function will serve that accumulated response
+ * to the linux guest in chunks by writing it back to MMIO region.
+ */
+ void *mem = memory_region_get_ram_ptr(&s->cmdmem);
+ uint32_t remaining = s->response_buffer->len - s->response_offset;
+ uint32_t to_copy = MIN(CRB_CTRL_CMD_SIZE, remaining);
+
+ memcpy(mem, s->response_buffer->data + s->response_offset, to_copy);
+
+ if (to_copy < CRB_CTRL_CMD_SIZE) {
+ memset((guint8 *)mem + to_copy, 0, CRB_CTRL_CMD_SIZE - to_copy);
+ }
+
+ s->response_offset += to_copy;
+ memory_region_set_dirty(&s->cmdmem, 0, CRB_CTRL_CMD_SIZE);
+}
+
static void tpm_crb_mmio_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
@@ -153,20 +220,58 @@ static void tpm_crb_mmio_write(void *opaque, hwaddr addr,
}
break;
case A_CRB_CTRL_START:
- if (val == CRB_START_INVOKE &&
- !(s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) &&
- tpm_crb_get_active_locty(s) == locty) {
- void *mem = memory_region_get_ram_ptr(&s->cmdmem);
-
+ if (tpm_crb_get_active_locty(s) != locty) {
+ break;
+ }
+ if (s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) {
+ /*
+ * Backend TPM is busy processing a request.
+ */
+ break;
+ }
+ if (val & CRB_START_INVOKE) {
+ if (!tpm_crb_append_command_request(s)) {
+ break;
+ }
ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, Start, 1);
+ g_byte_array_set_size(s->response_buffer, s->be_buffer_size);
s->cmd = (TPMBackendCmd) {
- .in = mem,
- .in_len = MIN(tpm_cmd_get_size(mem), s->be_buffer_size),
- .out = mem,
- .out_len = s->be_buffer_size,
+ .in = s->command_buffer->data,
+ .in_len = s->command_buffer->len,
+ .out = s->response_buffer->data,
+ .out_len = s->response_buffer->len,
};
-
tpm_backend_deliver_request(s->tpmbe, &s->cmd);
+ } else if (val & CRB_START_NEXT_CHUNK) {
+ if (!s->cap_chunk) {
+ break;
+ }
+ /*
+ * nextChunk is used both while sending and receiving data.
+ * To distinguish between the two, response_buffer is checked.
+ * If it does not have data, then that means we have not yet
+ * sent the command to the tpm backend, and therefore call
+ * tpm_crb_append_command_request().
+ */
+ if (s->response_buffer->len > 0 &&
+ s->response_offset < s->response_buffer->len) {
+ tpm_crb_fill_command_response(s);
+ } else {
+ if (!tpm_crb_append_command_request(s)) {
+ break;
+ }
+ }
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, nextChunk, 0);
+ } else if (val & CRB_START_RSP_RETRY) {
+ if (!s->cap_chunk) {
+ break;
+ }
+ if (s->response_buffer->len > 0) {
+ s->response_offset = 0;
+ tpm_crb_fill_command_response(s);
+ }
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, crbRspRetry, 0);
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, nextChunk, 0);
}
break;
case A_CRB_LOC_CTRL:
@@ -211,8 +316,21 @@ static void tpm_crb_request_completed(TPMIf *ti, int ret)
if (ret != 0) {
ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS,
tpmSts, 1); /* fatal error */
+ tpm_crb_clear_internal_buffers(s);
+ } else {
+ uint32_t actual_resp_size = tpm_cmd_get_size(s->response_buffer->data);
+ uint32_t total_resp_size = MIN(actual_resp_size, s->be_buffer_size);
+ g_byte_array_set_size(s->response_buffer, total_resp_size);
+ s->response_offset = 0;
}
- memory_region_set_dirty(&s->cmdmem, 0, CRB_CTRL_CMD_SIZE);
+ /*
+ * Send the first chunk. Subsequent chunks will be sent
+ * on receiving nextChunk from the guest
+ */
+ tpm_crb_fill_command_response(s);
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, nextChunk, 0);
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, crbRspRetry, 0);
+ g_byte_array_set_size(s->command_buffer, 0);
}
static enum TPMVersion tpm_crb_get_version(TPMIf *ti)
@@ -287,8 +405,7 @@ static void tpm_crb_reset(void *dev)
s->regs[R_CRB_CTRL_RSP_SIZE] = CRB_CTRL_CMD_SIZE;
s->regs[R_CRB_CTRL_RSP_ADDR] = TPM_CRB_ADDR_BASE + A_CRB_DATA_BUFFER;
- s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->tpmbe),
- CRB_CTRL_CMD_SIZE);
+ s->be_buffer_size = tpm_backend_get_buffer_size(s->tpmbe);
if (tpm_backend_startup_tpm(s->tpmbe, s->be_buffer_size) < 0) {
exit(1);
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v7 5/6] test/qtest: Add test for tpm crb chunking
2026-05-06 7:58 [PATCH v7 0/6] hw/tpm: CRB chunking capability to handle PQC Arun Menon
` (3 preceding siblings ...)
2026-05-06 7:58 ` [PATCH v7 4/6] hw/tpm: Implement TPM CRB chunking logic Arun Menon
@ 2026-05-06 7:58 ` Arun Menon
2026-05-06 7:58 ` [PATCH v7 6/6] hw/tpm: Add support for VM migration with TPM CRB chunking Arun Menon
5 siblings, 0 replies; 7+ messages in thread
From: Arun Menon @ 2026-05-06 7:58 UTC (permalink / raw)
To: qemu-devel
Cc: Zhao Liu, Marcel Apfelbaum, Ani Sinha, Fabiano Rosas,
marcandre.lureau, Stefan Berger, Philippe Mathieu-Daudé,
Yanan Wang, Paolo Bonzini, Laurent Vivier, Michael S. Tsirkin,
Igor Mammedov, Arun Menon, Stefan Berger
From: Arun Menon <armenon@redhat.com>
- New test case added to the swtpm test. Data is written and read from
the buffer in chunks.
- The chunk size is dynamically calculated by reading the
CRB_CTRL_CMD_SIZE address. This can be changed manually to test.
- Add a helper function tpm_wait_till_bit_clear()
- Note that this commit does not yet exercise the chunked read/write
logic, as current transfer sizes remain small. Testing for large
transfers is introduced in a subsequent patch: 'tests: Use ML-DSA-87
operations to cause large TPM transfers with CRB'
Signed-off-by: Arun Menon <armenon@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
---
tests/qtest/tpm-crb-swtpm-test.c | 10 +++
tests/qtest/tpm-util.c | 109 ++++++++++++++++++++++++++-----
tests/qtest/tpm-util.h | 5 ++
3 files changed, 109 insertions(+), 15 deletions(-)
diff --git a/tests/qtest/tpm-crb-swtpm-test.c b/tests/qtest/tpm-crb-swtpm-test.c
index ffeb1c396b..050c7b0c1f 100644
--- a/tests/qtest/tpm-crb-swtpm-test.c
+++ b/tests/qtest/tpm-crb-swtpm-test.c
@@ -33,6 +33,14 @@ static void tpm_crb_swtpm_test(const void *data)
"tpm-crb", NULL);
}
+static void tpm_crb_chunk_swtpm_test(const void *data)
+{
+ const TestState *ts = data;
+
+ tpm_test_swtpm_test(ts->src_tpm_path, tpm_util_crb_chunk_transfer,
+ "tpm-crb", NULL);
+}
+
static void tpm_crb_swtpm_migration_test(const void *data)
{
const TestState *ts = data;
@@ -54,6 +62,8 @@ int main(int argc, char **argv)
g_test_init(&argc, &argv, NULL);
qtest_add_data_func("/tpm/crb-swtpm/test", &ts, tpm_crb_swtpm_test);
+ qtest_add_data_func("/tpm/crb-chunk-swtpm/test", &ts,
+ tpm_crb_chunk_swtpm_test);
qtest_add_data_func("/tpm/crb-swtpm-migration/test", &ts,
tpm_crb_swtpm_migration_test);
ret = g_test_run();
diff --git a/tests/qtest/tpm-util.c b/tests/qtest/tpm-util.c
index 2cb2dd4796..b0763d86b3 100644
--- a/tests/qtest/tpm-util.c
+++ b/tests/qtest/tpm-util.c
@@ -14,16 +14,44 @@
#include "qemu/osdep.h"
#include <glib/gstdio.h>
+#include "qemu/bswap.h"
#include "hw/acpi/tpm.h"
#include "libqtest.h"
#include "tpm-util.h"
#include "qobject/qdict.h"
+#define CRB_ADDR_START (TPM_CRB_ADDR_BASE + A_CRB_CTRL_START)
+#define CRB_ADDR_CTRL_STS (TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS)
+#define CRB_ADDR_CTRL_CMD_SIZE \
+ (TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_SIZE)
+
+#define CRB_START_INVOKE (1 << 0)
+#define CRB_START_RSP_RETRY (1 << 1)
+#define CRB_START_NEXT_CHUNK (1 << 2)
+
+void tpm_wait_till_bit_clear(QTestState *s, uint64_t addr, uint32_t mask)
+{
+ uint32_t sts;
+ uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
+
+ while (true) {
+ sts = qtest_readl(s, addr);
+ if ((sts & mask) == 0) {
+ break;
+ }
+ if (g_get_monotonic_time() >= end_time) {
+ g_assert_cmphex(sts & mask, ==, 0);
+ break;
+ }
+ }
+}
+
void tpm_util_crb_transfer(QTestState *s,
const unsigned char *req, size_t req_size,
unsigned char *rsp, size_t rsp_size)
{
+ uint32_t tpm_sts;
uint64_t caddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR);
uint64_t raddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR);
@@ -31,24 +59,75 @@ void tpm_util_crb_transfer(QTestState *s,
qtest_memwrite(s, caddr, req, req_size);
- uint32_t sts, start = 1;
- uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
- qtest_writel(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start);
- while (true) {
- start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START);
- if ((start & 1) == 0) {
- break;
+ qtest_writel(s, CRB_ADDR_START, CRB_START_INVOKE);
+ tpm_wait_till_bit_clear(s, CRB_ADDR_START, CRB_START_INVOKE);
+
+ tpm_sts = qtest_readl(s, CRB_ADDR_CTRL_STS);
+ g_assert_cmpint(tpm_sts & 1, ==, 0);
+
+ qtest_memread(s, raddr, rsp, rsp_size);
+}
+
+void tpm_util_crb_chunk_transfer(QTestState *s,
+ const unsigned char *req, size_t req_size,
+ unsigned char *rsp, size_t rsp_size)
+{
+ uint32_t tpm_sts;
+ size_t chunk_size;
+ unsigned char header[10];
+ uint32_t actual_response_size = 0;
+
+ uint64_t caddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR);
+ uint64_t raddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR);
+ uint32_t crb_ctrl_cmd_size = qtest_readl(s, CRB_ADDR_CTRL_CMD_SIZE);
+
+ chunk_size = crb_ctrl_cmd_size;
+
+ qtest_writeb(s, TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 1);
+
+ for (size_t i = 0; i < req_size; i += chunk_size) {
+ bool last_chunk = false;
+ size_t current_chunk_size = chunk_size;
+
+ if (i + chunk_size > req_size) {
+ last_chunk = true;
+ current_chunk_size = req_size - i;
}
- if (g_get_monotonic_time() >= end_time) {
- break;
+
+ qtest_memwrite(s, caddr, req + i, current_chunk_size);
+
+ if (last_chunk) {
+ qtest_writel(s, CRB_ADDR_START, CRB_START_INVOKE);
+ tpm_wait_till_bit_clear(s, CRB_ADDR_START, CRB_START_INVOKE);
+ } else {
+ qtest_writel(s, CRB_ADDR_START, CRB_START_NEXT_CHUNK);
+ tpm_wait_till_bit_clear(s, CRB_ADDR_START, CRB_START_NEXT_CHUNK);
}
- };
- start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START);
- g_assert_cmpint(start & 1, ==, 0);
- sts = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS);
- g_assert_cmpint(sts & 1, ==, 0);
+ }
+ tpm_sts = qtest_readl(s, CRB_ADDR_CTRL_STS);
+ g_assert_cmpint(tpm_sts & 1, ==, 0);
- qtest_memread(s, raddr, rsp, rsp_size);
+ /*
+ * Read response in chunks
+ */
+
+ qtest_memread(s, raddr, header, sizeof(header));
+ actual_response_size = ldl_be_p(&header[2]);
+
+ if (actual_response_size > rsp_size) {
+ actual_response_size = rsp_size;
+ }
+
+ for (size_t i = 0; i < actual_response_size; i += chunk_size) {
+ size_t to_read = i + chunk_size > actual_response_size
+ ? actual_response_size - i
+ : chunk_size;
+ if (i > 0) {
+ qtest_writel(s, CRB_ADDR_START, CRB_START_NEXT_CHUNK);
+ tpm_wait_till_bit_clear(s, CRB_ADDR_START, CRB_START_NEXT_CHUNK);
+ }
+ qtest_memread(s, raddr, rsp + i, to_read);
+ }
}
void tpm_util_startup(QTestState *s, tx_func *tx)
diff --git a/tests/qtest/tpm-util.h b/tests/qtest/tpm-util.h
index 0cb28dd6e5..681544e7d8 100644
--- a/tests/qtest/tpm-util.h
+++ b/tests/qtest/tpm-util.h
@@ -24,10 +24,15 @@ typedef void (tx_func)(QTestState *s,
const unsigned char *req, size_t req_size,
unsigned char *rsp, size_t rsp_size);
+void tpm_wait_till_bit_clear(QTestState *s, uint64_t addr, uint32_t mask);
void tpm_util_crb_transfer(QTestState *s,
const unsigned char *req, size_t req_size,
unsigned char *rsp, size_t rsp_size);
+void tpm_util_crb_chunk_transfer(QTestState *s,
+ const unsigned char *req, size_t req_size,
+ unsigned char *rsp, size_t rsp_size);
+
void tpm_util_startup(QTestState *s, tx_func *tx);
void tpm_util_pcrextend(QTestState *s, tx_func *tx);
void tpm_util_pcrread(QTestState *s, tx_func *tx,
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v7 6/6] hw/tpm: Add support for VM migration with TPM CRB chunking
2026-05-06 7:58 [PATCH v7 0/6] hw/tpm: CRB chunking capability to handle PQC Arun Menon
` (4 preceding siblings ...)
2026-05-06 7:58 ` [PATCH v7 5/6] test/qtest: Add test for tpm crb chunking Arun Menon
@ 2026-05-06 7:58 ` Arun Menon
5 siblings, 0 replies; 7+ messages in thread
From: Arun Menon @ 2026-05-06 7:58 UTC (permalink / raw)
To: qemu-devel
Cc: Zhao Liu, Marcel Apfelbaum, Ani Sinha, Fabiano Rosas,
marcandre.lureau, Stefan Berger, Philippe Mathieu-Daudé,
Yanan Wang, Paolo Bonzini, Laurent Vivier, Michael S. Tsirkin,
Igor Mammedov, Arun Menon, Stefan Berger
From: Arun Menon <armenon@redhat.com>
- 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>
---
hw/core/machine.c | 1 +
hw/tpm/tpm_crb.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 68 insertions(+)
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 4e0a93e231..30464b21ac 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -41,6 +41,7 @@
GlobalProperty hw_compat_11_0[] = {
{ "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)
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-05-06 7:59 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-06 7:58 [PATCH v7 0/6] hw/tpm: CRB chunking capability to handle PQC Arun Menon
2026-05-06 7:58 ` [PATCH v7 1/6] hw/tpm: Add TPM CRB chunking fields Arun Menon
2026-05-06 7:58 ` [PATCH v7 2/6] hw/tpm: Refactor CRB_CTRL_START register access Arun Menon
2026-05-06 7:58 ` [PATCH v7 3/6] hw/tpm: Add internal buffer state for chunking Arun Menon
2026-05-06 7:58 ` [PATCH v7 4/6] hw/tpm: Implement TPM CRB chunking logic Arun Menon
2026-05-06 7:58 ` [PATCH v7 5/6] test/qtest: Add test for tpm crb chunking Arun Menon
2026-05-06 7:58 ` [PATCH v7 6/6] hw/tpm: Add support for VM migration with TPM CRB chunking Arun Menon
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.