All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 00/10] hw/tpm: CRB chunking capability to handle PQC
@ 2026-04-06 14:17 Arun Menon
  2026-04-06 14:17 ` [PATCH v3 01/10] hw/tpm: Add TPM CRB chunking fields Arun Menon
                   ` (9 more replies)
  0 siblings, 10 replies; 22+ messages in thread
From: Arun Menon @ 2026-04-06 14:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Ani Sinha, Laurent Vivier, Zhao Liu, Stefan Berger,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas, marcandre.lureau,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé, 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/20260406115247.4879-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

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: <20260406115247.4879-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

Stefan Berger (4):
  qtests: Enable starting swtpm with a given profile
  tests: Use ML-DSA-87 operations to caused large TPM transfers with CRB
  tpm: Extend TPM TIS buffer size to 8192 bytes
  tests: Use ML-DSA-87 operations to caused large TPM transfers with TIS

 hw/core/machine.c                |   4 +-
 hw/tpm/tpm_crb.c                 | 218 +++++++++++++++++++++++++++----
 hw/tpm/tpm_tis.h                 |   2 +
 hw/tpm/tpm_tis_common.c          |  23 ++++
 hw/tpm/tpm_tis_i2c.c             |  24 +++-
 hw/tpm/tpm_tis_isa.c             |  24 +++-
 hw/tpm/tpm_tis_sysbus.c          |  24 +++-
 include/hw/acpi/tpm.h            |   5 +-
 tests/qtest/tpm-crb-swtpm-test.c |  11 ++
 tests/qtest/tpm-tests.c          | 102 ++++++++++++++-
 tests/qtest/tpm-tests.h          |   4 +
 tests/qtest/tpm-tis-swtpm-test.c |  11 ++
 tests/qtest/tpm-util.c           | 154 +++++++++++++++++++---
 tests/qtest/tpm-util.h           |  10 +-
 14 files changed, 569 insertions(+), 47 deletions(-)

-- 
2.53.0



^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v3 01/10] hw/tpm: Add TPM CRB chunking fields
  2026-04-06 14:17 [PATCH v3 00/10] hw/tpm: CRB chunking capability to handle PQC Arun Menon
@ 2026-04-06 14:17 ` Arun Menon
  2026-04-07  7:49   ` Marc-André Lureau
  2026-04-06 14:17 ` [PATCH v3 02/10] hw/tpm: Refactor CRB_CTRL_START register access Arun Menon
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 22+ messages in thread
From: Arun Menon @ 2026-04-06 14:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Ani Sinha, Laurent Vivier, Zhao Liu, Stefan Berger,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas, marcandre.lureau,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé, 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]

[1] https://trustedcomputinggroup.org/wp-content/uploads/PC-Client-Specific-Platform-TPM-Profile-for-TPM-2p0-v1p07_rc1_121225.pdf

Signed-off-by: Arun Menon <armenon@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
---
 hw/tpm/tpm_crb.c      | 3 +++
 include/hw/acpi/tpm.h | 5 ++++-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
index 8723536f93..0a1c7ecdc6 100644
--- a/hw/tpm/tpm_crb.c
+++ b/hw/tpm/tpm_crb.c
@@ -59,6 +59,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)
 
@@ -262,6 +263,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, CRB_INTF_CAP_CRB_CHUNK);
     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 d2bf6637c5..03d452d2b5 100644
--- a/include/hw/acpi/tpm.h
+++ b/include/hw/acpi/tpm.h
@@ -149,7 +149,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)
@@ -168,6 +168,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, invoke, 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.53.0



^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v3 02/10] hw/tpm: Refactor CRB_CTRL_START register access
  2026-04-06 14:17 [PATCH v3 00/10] hw/tpm: CRB chunking capability to handle PQC Arun Menon
  2026-04-06 14:17 ` [PATCH v3 01/10] hw/tpm: Add TPM CRB chunking fields Arun Menon
@ 2026-04-06 14:17 ` Arun Menon
  2026-04-07  7:50   ` Marc-André Lureau
  2026-04-06 14:17 ` [PATCH v3 03/10] hw/tpm: Add internal buffer state for chunking Arun Menon
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 22+ messages in thread
From: Arun Menon @ 2026-04-06 14:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Ani Sinha, Laurent Vivier, Zhao Liu, Stefan Berger,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas, marcandre.lureau,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé, 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>
---
 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 0a1c7ecdc6..bc55908786 100644
--- a/hw/tpm/tpm_crb.c
+++ b/hw/tpm/tpm_crb.c
@@ -145,7 +145,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, invoke, 1);
             s->cmd = (TPMBackendCmd) {
                 .in = mem,
                 .in_len = MIN(tpm_cmd_get_size(mem), s->be_buffer_size),
@@ -194,7 +194,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, invoke, 0);
     if (ret != 0) {
         ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS,
                          tpmSts, 1); /* fatal error */
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v3 03/10] hw/tpm: Add internal buffer state for chunking
  2026-04-06 14:17 [PATCH v3 00/10] hw/tpm: CRB chunking capability to handle PQC Arun Menon
  2026-04-06 14:17 ` [PATCH v3 01/10] hw/tpm: Add TPM CRB chunking fields Arun Menon
  2026-04-06 14:17 ` [PATCH v3 02/10] hw/tpm: Refactor CRB_CTRL_START register access Arun Menon
@ 2026-04-06 14:17 ` Arun Menon
  2026-04-07  7:56   ` Marc-André Lureau
  2026-04-06 14:17 ` [PATCH v3 04/10] hw/tpm: Implement TPM CRB chunking logic Arun Menon
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 22+ messages in thread
From: Arun Menon @ 2026-04-06 14:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Ani Sinha, Laurent Vivier, Zhao Liu, Stefan Berger,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas, marcandre.lureau,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé, Arun Menon

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>
---
 hw/tpm/tpm_crb.c | 34 ++++++++++++++++++++++++++++++----
 1 file changed, 30 insertions(+), 4 deletions(-)

diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
index bc55908786..d65b3e2cc1 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;
 
     bool ppi_enabled;
     TPMPPI ppi;
@@ -85,6 +88,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)
 {
@@ -134,9 +144,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:
@@ -240,6 +252,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));
 
@@ -306,6 +319,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();
+
     if (s->ppi_enabled) {
         tpm_ppi_init(&s->ppi, get_system_memory(),
                      TPM_PPI_ADDR_BASE, OBJECT(s));
@@ -318,12 +334,22 @@ static void tpm_crb_realize(DeviceState *dev, Error **errp)
     }
 }
 
+static void tpm_crb_unrealize(DeviceState *dev)
+{
+    CRBState *s = CRB(dev);
+
+    g_byte_array_unref(s->command_buffer);
+    g_byte_array_unref(s->response_buffer);
+
+}
+
 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.53.0



^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v3 04/10] hw/tpm: Implement TPM CRB chunking logic
  2026-04-06 14:17 [PATCH v3 00/10] hw/tpm: CRB chunking capability to handle PQC Arun Menon
                   ` (2 preceding siblings ...)
  2026-04-06 14:17 ` [PATCH v3 03/10] hw/tpm: Add internal buffer state for chunking Arun Menon
@ 2026-04-06 14:17 ` Arun Menon
  2026-04-07  8:21   ` Marc-André Lureau
  2026-04-06 14:17 ` [PATCH v3 05/10] test/qtest: Add test for tpm crb chunking Arun Menon
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 22+ messages in thread
From: Arun Menon @ 2026-04-06 14:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Ani Sinha, Laurent Vivier, Zhao Liu, Stefan Berger,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas, marcandre.lureau,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé, 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_rc1_121225.pdf

Signed-off-by: Arun Menon <armenon@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
---
 hw/tpm/tpm_crb.c | 148 +++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 131 insertions(+), 17 deletions(-)

diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
index d65b3e2cc1..b9f295db7a 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"
@@ -65,6 +66,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),
@@ -80,6 +82,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 {
@@ -122,6 +126,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, invoke, 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)
 {
@@ -152,20 +219,55 @@ 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);
-
-            ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, invoke, 1);
-            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,
-            };
-
-            tpm_backend_deliver_request(s->tpmbe, &s->cmd);
+        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 (!(s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE)) {
+                if (!tpm_crb_append_command_request(s)) {
+                    break;
+                }
+                ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, invoke, 1);
+                g_byte_array_set_size(s->response_buffer, s->be_buffer_size);
+                s->cmd = (TPMBackendCmd) {
+                    .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) {
+            /*
+             * 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->response_buffer->len > 0) {
+                trace_tpm_crb_mmio_write(addr, size, val);
+                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:
@@ -210,8 +312,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)
@@ -288,8 +403,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.53.0



^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v3 05/10] test/qtest: Add test for tpm crb chunking
  2026-04-06 14:17 [PATCH v3 00/10] hw/tpm: CRB chunking capability to handle PQC Arun Menon
                   ` (3 preceding siblings ...)
  2026-04-06 14:17 ` [PATCH v3 04/10] hw/tpm: Implement TPM CRB chunking logic Arun Menon
@ 2026-04-06 14:17 ` Arun Menon
  2026-04-07 10:40   ` Marc-André Lureau
  2026-04-06 14:17 ` [PATCH v3 06/10] hw/tpm: Add support for VM migration with TPM CRB chunking Arun Menon
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 22+ messages in thread
From: Arun Menon @ 2026-04-06 14:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Ani Sinha, Laurent Vivier, Zhao Liu, Stefan Berger,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas, marcandre.lureau,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé, Arun Menon

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()

Signed-off-by: Arun Menon <armenon@redhat.com>
---
 tests/qtest/tpm-crb-swtpm-test.c |  10 +++
 tests/qtest/tpm-util.c           | 108 ++++++++++++++++++++++++++-----
 tests/qtest/tpm-util.h           |   5 ++
 3 files changed, 108 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..603c5a825d 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,74 @@ 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;
+
+    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);
+
+    size_t 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
+     */
+
+    unsigned char header[10];
+    qtest_memread(s, raddr, header, sizeof(header));
+
+    uint32_t 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.53.0



^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v3 06/10] hw/tpm: Add support for VM migration with TPM CRB chunking
  2026-04-06 14:17 [PATCH v3 00/10] hw/tpm: CRB chunking capability to handle PQC Arun Menon
                   ` (4 preceding siblings ...)
  2026-04-06 14:17 ` [PATCH v3 05/10] test/qtest: Add test for tpm crb chunking Arun Menon
@ 2026-04-06 14:17 ` Arun Menon
  2026-04-07 11:00   ` Marc-André Lureau
  2026-04-06 14:17 ` [PATCH v3 07/10] qtests: Enable starting swtpm with a given profile Arun Menon
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 22+ messages in thread
From: Arun Menon @ 2026-04-06 14:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Ani Sinha, Laurent Vivier, Zhao Liu, Stefan Berger,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas, marcandre.lureau,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé, Arun Menon

From: Arun Menon <armenon@redhat.com>

- Add subsection in VMState for TPM CRB with the newly introduced
  command and response buffers, along with a needed callback, so that
  newer QEMU only sends the buffers if it is necessary.
- Add hw_compat blocker because the feature is only supported for
  machine type 11.1 and higher.
- If the VM has no pending chunked TPM commands in the internal buffers
  during a VM migration, or if the machine type does not support newly
  introduced buffers, then the needed callback will return false, as it
  checks the hw_compat blocker and thus the subsection will not be sent
  to the destination host.

Signed-off-by: Arun Menon <armenon@redhat.com>
---
 hw/core/machine.c |  4 +++-
 hw/tpm/tpm_crb.c  | 31 +++++++++++++++++++++++++++++++
 2 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/hw/core/machine.c b/hw/core/machine.c
index 1abc8ae737..fb290c6c53 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -38,7 +38,9 @@
 #include "hw/acpi/generic_event_device.h"
 #include "qemu/audio.h"
 
-GlobalProperty hw_compat_11_0[] = {};
+GlobalProperty hw_compat_11_0[] = {
+    { "tpm-crb", "migrate-buffers", "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 b9f295db7a..81471dd9f8 100644
--- a/hw/tpm/tpm_crb.c
+++ b/hw/tpm/tpm_crb.c
@@ -49,6 +49,8 @@ struct CRBState {
 
     bool ppi_enabled;
     TPMPPI ppi;
+
+    bool migrate_buffers;
 };
 typedef struct CRBState CRBState;
 
@@ -345,18 +347,47 @@ static int tpm_crb_pre_save(void *opaque)
     return 0;
 }
 
+static bool tpm_crb_chunk_needed(void *opaque)
+{
+    CRBState *s = opaque;
+
+    if (!s->migrate_buffers) {
+        return false;
+    }
+
+    return ((s->command_buffer && s->command_buffer->len > 0) ||
+            (s->response_buffer && s->response_buffer->len > 0));
+}
+
+static const VMStateDescription vmstate_tpm_crb_chunk = {
+    .name = "tpm-crb/chunk",
+    .version_id = 0,
+    .needed = tpm_crb_chunk_needed,
+    .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("ppi", CRBState, ppi_enabled, true),
+    DEFINE_PROP_BOOL("migrate-buffers", CRBState, migrate_buffers, true),
 };
 
 static void tpm_crb_reset(void *dev)
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v3 07/10] qtests: Enable starting swtpm with a given profile
  2026-04-06 14:17 [PATCH v3 00/10] hw/tpm: CRB chunking capability to handle PQC Arun Menon
                   ` (5 preceding siblings ...)
  2026-04-06 14:17 ` [PATCH v3 06/10] hw/tpm: Add support for VM migration with TPM CRB chunking Arun Menon
@ 2026-04-06 14:17 ` Arun Menon
  2026-04-06 14:17 ` [PATCH v3 08/10] tests: Use ML-DSA-87 operations to caused large TPM transfers with CRB Arun Menon
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 22+ messages in thread
From: Arun Menon @ 2026-04-06 14:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Ani Sinha, Laurent Vivier, Zhao Liu, Stefan Berger,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas, marcandre.lureau,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé, Arun Menon, Stefan Berger

From: Stefan Berger <stefanb@linux.ibm.com>

Enable swtpm to start with a given profile by passing it to swtpm on the
command line using

   --profile name=<profile name>

Remove any existing TPM 2 state file since applying a new profile to
existing state would be refused by swtpm.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 tests/qtest/tpm-tests.c | 6 +++---
 tests/qtest/tpm-util.c  | 9 ++++++++-
 tests/qtest/tpm-util.h  | 3 ++-
 3 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/tests/qtest/tpm-tests.c b/tests/qtest/tpm-tests.c
index 197714f8d9..f71d882990 100644
--- a/tests/qtest/tpm-tests.c
+++ b/tests/qtest/tpm-tests.c
@@ -43,7 +43,7 @@ void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx,
         return;
     }
 
-    succ = tpm_util_swtpm_start(src_tpm_path, &swtpm_pid, &addr, &error);
+    succ = tpm_util_swtpm_start(src_tpm_path, &swtpm_pid, &addr, NULL, &error);
     g_assert_true(succ);
 
     args = g_strdup_printf(
@@ -91,11 +91,11 @@ void tpm_test_swtpm_migration_test(const char *src_tpm_path,
     }
 
     succ = tpm_util_swtpm_start(src_tpm_path, &src_tpm_pid,
-                                &src_tpm_addr, &error);
+                                &src_tpm_addr, NULL, &error);
     g_assert_true(succ);
 
     succ = tpm_util_swtpm_start(dst_tpm_path, &dst_tpm_pid,
-                                &dst_tpm_addr, &error);
+                                &dst_tpm_addr, NULL, &error);
     g_assert_true(succ);
 
     tpm_util_migration_start_qemu(&src_qemu, &dst_qemu,
diff --git a/tests/qtest/tpm-util.c b/tests/qtest/tpm-util.c
index 603c5a825d..24243065c1 100644
--- a/tests/qtest/tpm-util.c
+++ b/tests/qtest/tpm-util.c
@@ -210,7 +210,8 @@ bool tpm_util_swtpm_has_tpm2(void)
 }
 
 gboolean tpm_util_swtpm_start(const char *path, GPid *pid,
-                              SocketAddress **addr, GError **error)
+                              SocketAddress **addr, const char *profilename,
+                              GError **error)
 {
     char *swtpm_argv_tpmstate = g_strdup_printf("dir=%s", path);
     char *swtpm_argv_ctrl = g_strdup_printf("type=unixio,path=%s/sock",
@@ -220,11 +221,17 @@ gboolean tpm_util_swtpm_start(const char *path, GPid *pid,
         g_strdup("--tpmstate"), swtpm_argv_tpmstate,
         g_strdup("--ctrl"), swtpm_argv_ctrl,
         g_strdup("--tpm2"),
+        profilename ? g_strdup("--profile") : NULL,
+        profilename ? g_strdup_printf("name=%s", profilename) : NULL,
         NULL
     };
+    g_autofree char *swtpm_state_file;
     gboolean succ;
     unsigned i;
 
+    swtpm_state_file = g_strdup_printf("%s/tpm2-00.permall", path);
+    g_unlink(swtpm_state_file);
+
     *addr = g_new0(SocketAddress, 1);
     (*addr)->type = SOCKET_ADDRESS_TYPE_UNIX;
     (*addr)->u.q_unix.path = g_build_filename(path, "sock", NULL);
diff --git a/tests/qtest/tpm-util.h b/tests/qtest/tpm-util.h
index 681544e7d8..ca2d7d173f 100644
--- a/tests/qtest/tpm-util.h
+++ b/tests/qtest/tpm-util.h
@@ -41,7 +41,8 @@ void tpm_util_pcrread(QTestState *s, tx_func *tx,
 bool tpm_util_swtpm_has_tpm2(void);
 
 gboolean tpm_util_swtpm_start(const char *path, GPid *pid,
-                              SocketAddress **addr, GError **error);
+                              SocketAddress **addr, const char *profilename,
+                              GError **error);
 void tpm_util_swtpm_kill(GPid pid);
 
 void tpm_util_migrate(QTestState *who, const char *uri);
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v3 08/10] tests: Use ML-DSA-87 operations to caused large TPM transfers with CRB
  2026-04-06 14:17 [PATCH v3 00/10] hw/tpm: CRB chunking capability to handle PQC Arun Menon
                   ` (6 preceding siblings ...)
  2026-04-06 14:17 ` [PATCH v3 07/10] qtests: Enable starting swtpm with a given profile Arun Menon
@ 2026-04-06 14:17 ` Arun Menon
  2026-04-06 14:17 ` [PATCH v3 09/10] tpm: Extend TPM TIS buffer size to 8192 bytes Arun Menon
  2026-04-06 14:17 ` [PATCH v3 10/10] tests: Use ML-DSA-87 operations to caused large TPM transfers with TIS Arun Menon
  9 siblings, 0 replies; 22+ messages in thread
From: Arun Menon @ 2026-04-06 14:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Ani Sinha, Laurent Vivier, Zhao Liu, Stefan Berger,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas, marcandre.lureau,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé, Arun Menon, Stefan Berger

From: Stefan Berger <stefanb@linux.ibm.com>

To test large data transfers (receiving and sending) that make us of a CRB
chunked transfer, create an ML-DSA-87 key and sign some data with it and
receive the 4627 bytes signature. After this send the signature back to the
TPM to have the TPM verify the signature.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 tests/qtest/tpm-crb-swtpm-test.c |  5 +-
 tests/qtest/tpm-tests.c          | 96 ++++++++++++++++++++++++++++++++
 tests/qtest/tpm-tests.h          |  4 ++
 tests/qtest/tpm-util.c           | 37 ++++++++++++
 tests/qtest/tpm-util.h           |  2 +
 5 files changed, 142 insertions(+), 2 deletions(-)

diff --git a/tests/qtest/tpm-crb-swtpm-test.c b/tests/qtest/tpm-crb-swtpm-test.c
index 050c7b0c1f..541fd58133 100644
--- a/tests/qtest/tpm-crb-swtpm-test.c
+++ b/tests/qtest/tpm-crb-swtpm-test.c
@@ -37,8 +37,9 @@ 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);
+    tpm_test_swtpm_large_tx_test(ts->src_tpm_path,
+                                 tpm_util_crb_chunk_transfer,
+                                 "tpm-crb", NULL);
 }
 
 static void tpm_crb_swtpm_migration_test(const void *data)
diff --git a/tests/qtest/tpm-tests.c b/tests/qtest/tpm-tests.c
index f71d882990..21811f3a2e 100644
--- a/tests/qtest/tpm-tests.c
+++ b/tests/qtest/tpm-tests.c
@@ -13,6 +13,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "system/tpm_util.h"
 #include <glib/gstdio.h>
 
 #include "libqtest-single.h"
@@ -130,3 +131,98 @@ void tpm_test_swtpm_migration_test(const char *src_tpm_path,
     g_unlink(src_tpm_addr->u.q_unix.path);
     qapi_free_SocketAddress(src_tpm_addr);
 }
+
+void tpm_test_swtpm_large_tx_test(const char *src_tpm_path, tx_func *tx,
+                                  const char *ifmodel,
+                                  const char *machine_options)
+{
+    unsigned char signature[2 + 2 + 4627]; /* TPMT_SIGNATURE */
+    unsigned char response[8192];
+    unsigned char request[8192];
+    SocketAddress *addr = NULL;
+    GError *error = NULL;
+    char *args = NULL;
+    GPid swtpm_pid;
+    QTestState *s;
+    gboolean succ;
+
+    if (tpm_test_swtpm_skip()) {
+        return;
+    }
+
+    /* Large transfers based on ML-DSA operations required default-v2 profile */
+    if (!tpm_util_swtpm_has_profile("default-v2", "ml-dsa")) {
+        return;
+    }
+
+    succ = tpm_util_swtpm_start(src_tpm_path, &swtpm_pid, &addr, "default-v2",
+                                &error);
+    g_assert_true(succ);
+
+    args = g_strdup_printf(
+        "%s "
+        "-chardev socket,id=chr,path=%s "
+        "-tpmdev emulator,id=dev,chardev=chr "
+        "-device %s,tpmdev=dev",
+        machine_options ? : "", addr->u.q_unix.path, ifmodel);
+
+    s = qtest_start(args);
+    g_free(args);
+
+    tpm_util_startup(s, tx);
+
+    static const unsigned char tpm_createprimary_mldsa[] =
+        "\x80\x02\x00\x00\x00\x38\x00\x00\x01\x31\x40\x00\x00\x07\x00\x00"
+        "\x00\x09\x40\x00\x00\x09\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00"
+        "\x00\x00\x0f\x00\xa1\x00\x0b\x00\x04\x04\x72\x00\x00\x00\x03\x00"
+        "\x00\x00\x00\x00\x00\x00\x00\x00";
+    tx(s, tpm_createprimary_mldsa, sizeof(tpm_createprimary_mldsa),
+       response, sizeof(response));
+    g_assert_cmpint(tpm_cmd_get_errcode(response), ==, 0);
+    g_assert_cmpint(tpm_cmd_get_size(response), ==, 2831);
+
+    static const unsigned char tpm_signsequencestart[] =
+        "\x80\x01\x00\x00\x00\x12\x00\x00\x01\xaa\x80\x00\x00\x00\x00\x00"
+        "\x00\x00";
+    tx(s, tpm_signsequencestart, sizeof(tpm_signsequencestart),
+       response, sizeof(response));
+    g_assert_cmpint(tpm_cmd_get_errcode(response), ==, 0);
+    g_assert_cmpint(tpm_cmd_get_size(response), ==, 14);
+
+    /* Complete sequence and get signature */
+    static const unsigned char tpm_signsequencecomplete[] =
+        "\x80\x02\x00\x00\x00\x2a\x00\x00\x01\xa4\x80\x00\x00\x01\x80\x00"
+        "\x00\x00\x00\x00\x00\x12\x40\x00\x00\x09\x00\x00\x00\x00\x00\x40"
+        "\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00";
+    tx(s, tpm_signsequencecomplete, sizeof(tpm_signsequencecomplete),
+       response, sizeof(response));
+    g_assert_cmpint(tpm_cmd_get_errcode(response), ==, 0);
+    g_assert_cmpint(tpm_cmd_get_size(response), ==, 4655);
+
+    /* TPMT_SIGNATURE found at offset 14 */
+    memcpy(signature, &response[14], sizeof(signature));
+
+    static const unsigned char tpm_verifysequencestart[] =
+        "\x80\x01\x00\x00\x00\x14\x00\x00\x01\xa9\x80\x00\x00\x00\x00\x00"
+        "\x00\x00\x00\x00";
+    tx(s, tpm_verifysequencestart, sizeof(tpm_verifysequencestart),
+       response, sizeof(response));
+    g_assert_cmpint(tpm_cmd_get_errcode(response), ==, 0);
+    g_assert_cmpint(tpm_cmd_get_size(response), ==, 14);
+
+    /* TPM2_VerifySequenceComplete */
+    memcpy(request,
+           "\x80\x02\x00\x00\x12\x36\x00\x00\x01\xa3\x80\x00\x00\x01\x80\x00"
+           "\x00\x00\x00\x00\x00\x09\x40\x00\x00\x09\x00\x00\x00\x00\x00",
+           31);
+    memcpy(&request[31], signature, sizeof(signature));
+    tx(s, request, 31 + sizeof(signature), response, sizeof(response));
+    g_assert_cmpint(tpm_cmd_get_errcode(response), ==, 0);
+    g_assert_cmpint(tpm_cmd_get_size(response), ==, 27);
+
+    qtest_end();
+    tpm_util_swtpm_kill(swtpm_pid);
+
+    g_unlink(addr->u.q_unix.path);
+    qapi_free_SocketAddress(addr);
+}
diff --git a/tests/qtest/tpm-tests.h b/tests/qtest/tpm-tests.h
index 07ba60d26e..6993ce40dc 100644
--- a/tests/qtest/tpm-tests.h
+++ b/tests/qtest/tpm-tests.h
@@ -24,4 +24,8 @@ void tpm_test_swtpm_migration_test(const char *src_tpm_path,
                                    const char *ifmodel,
                                    const char *machine_options);
 
+void tpm_test_swtpm_large_tx_test(const char *src_tpm_path, tx_func *tx,
+                                  const char *ifmodel,
+                                  const char *machine_options);
+
 #endif /* TESTS_TPM_TESTS_H */
diff --git a/tests/qtest/tpm-util.c b/tests/qtest/tpm-util.c
index 24243065c1..6163f5a533 100644
--- a/tests/qtest/tpm-util.c
+++ b/tests/qtest/tpm-util.c
@@ -209,6 +209,43 @@ bool tpm_util_swtpm_has_tpm2(void)
     return has_tpm2;
 }
 
+bool tpm_util_swtpm_has_profile(const char *profilename,
+                                const char *content)
+{
+    bool has_profile = false;
+    char *out = NULL;
+    static const char *argv[] = {
+        "swtpm", "socket", "--tpm2", "--print-profiles", NULL
+    };
+
+    if (!tpm_util_swtpm_has_tpm2()) {
+        return false;
+    }
+
+    if (!g_spawn_sync(NULL /* working_dir */,
+                      (char **)argv,
+                      NULL /* envp */,
+                      G_SPAWN_SEARCH_PATH,
+                      NULL /* child_setup */,
+                      NULL /* user_data */,
+                      &out,
+                      NULL /* err */,
+                      NULL /* exit_status */,
+                      NULL)) {
+        return false;
+    }
+
+    if (strstr(out, profilename)) {
+        has_profile = true;
+    }
+    if (has_profile && content && strstr(out, content) == NULL) {
+        has_profile = false;
+    }
+
+    g_free(out);
+    return has_profile;
+}
+
 gboolean tpm_util_swtpm_start(const char *path, GPid *pid,
                               SocketAddress **addr, const char *profilename,
                               GError **error)
diff --git a/tests/qtest/tpm-util.h b/tests/qtest/tpm-util.h
index ca2d7d173f..90790f30db 100644
--- a/tests/qtest/tpm-util.h
+++ b/tests/qtest/tpm-util.h
@@ -39,6 +39,8 @@ void tpm_util_pcrread(QTestState *s, tx_func *tx,
                       const unsigned char *exp_resp, size_t exp_resp_size);
 
 bool tpm_util_swtpm_has_tpm2(void);
+bool tpm_util_swtpm_has_profile(const char *profilename,
+                                const char *content);
 
 gboolean tpm_util_swtpm_start(const char *path, GPid *pid,
                               SocketAddress **addr, const char *profilename,
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v3 09/10] tpm: Extend TPM TIS buffer size to 8192 bytes
  2026-04-06 14:17 [PATCH v3 00/10] hw/tpm: CRB chunking capability to handle PQC Arun Menon
                   ` (7 preceding siblings ...)
  2026-04-06 14:17 ` [PATCH v3 08/10] tests: Use ML-DSA-87 operations to caused large TPM transfers with CRB Arun Menon
@ 2026-04-06 14:17 ` Arun Menon
  2026-04-06 14:17 ` [PATCH v3 10/10] tests: Use ML-DSA-87 operations to caused large TPM transfers with TIS Arun Menon
  9 siblings, 0 replies; 22+ messages in thread
From: Arun Menon @ 2026-04-06 14:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Ani Sinha, Laurent Vivier, Zhao Liu, Stefan Berger,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas, marcandre.lureau,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé, Arun Menon, Stefan Berger

From: Stefan Berger <stefanb@linux.ibm.com>

Extend the TIS buffer size to 8192 bytes and store the first 4096 bytes
using VMSTATE_PARTIAL_BUFFER and the rest using VMSTATE_BUFFER_START_MIDDLE
when necessary. It is necessary to store the buffer beyond original 4096
bytes when:

 - the user has written more than 4096 bytes to the buffer
 - the TPM 2 response is larger than 4096 bytes

Use the .needed function of the VMStateDescription interface to check
whether the bytes in the buffer beyond 4096 bytes needed to be saved.
The .pre_save function is called before the .needed function so that we
can be sure to have received any response packet from the TPM.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
[ Arun Menon - Removed WIP tag and TODO for submission ]
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 hw/tpm/tpm_tis.h        |  2 ++
 hw/tpm/tpm_tis_common.c | 23 +++++++++++++++++++++++
 hw/tpm/tpm_tis_i2c.c    | 24 +++++++++++++++++++++++-
 hw/tpm/tpm_tis_isa.c    | 24 +++++++++++++++++++++++-
 hw/tpm/tpm_tis_sysbus.c | 24 +++++++++++++++++++++++-
 5 files changed, 94 insertions(+), 3 deletions(-)

diff --git a/hw/tpm/tpm_tis.h b/hw/tpm/tpm_tis.h
index 184632ff66..d35c332287 100644
--- a/hw/tpm/tpm_tis.h
+++ b/hw/tpm/tpm_tis.h
@@ -90,4 +90,6 @@ uint32_t tpm_tis_read_data(TPMState *s, hwaddr addr, unsigned size);
 void tpm_tis_write_data(TPMState *s, hwaddr addr, uint64_t val, uint32_t size);
 uint16_t tpm_tis_get_checksum(TPMState *s);
 
+bool tpm_tis_ext_buffer_migration_needed(struct TPMState *s);
+
 #endif /* TPM_TPM_TIS_H */
diff --git a/hw/tpm/tpm_tis_common.c b/hw/tpm/tpm_tis_common.c
index f594b15b8a..dffb0a411e 100644
--- a/hw/tpm/tpm_tis_common.c
+++ b/hw/tpm/tpm_tis_common.c
@@ -890,3 +890,26 @@ const VMStateDescription vmstate_locty = {
     }
 };
 
+bool tpm_tis_ext_buffer_migration_needed(struct TPMState *s)
+{
+    if (!TPM_TIS_IS_VALID_LOCTY(s->active_locty)) {
+        return false;
+    }
+
+    switch (s->loc[s->active_locty].state) {
+    case TPM_TIS_STATE_IDLE:
+    case TPM_TIS_STATE_READY:
+        return false;
+    case TPM_TIS_STATE_RECEPTION:
+        return s->rw_offset >= 4096;
+    case TPM_TIS_STATE_EXECUTION:
+        /*
+         * TPM is executing: we cannot know the size of TPM response.
+         * .pre_save must have been called before (should never get here).
+         */
+        return false;
+    case TPM_TIS_STATE_COMPLETION:
+        return (tpm_cmd_get_size(&s->buffer) >= 4096);
+    }
+    return false;
+}
diff --git a/hw/tpm/tpm_tis_i2c.c b/hw/tpm/tpm_tis_i2c.c
index 9f13e0ec12..a01df7e5a0 100644
--- a/hw/tpm/tpm_tis_i2c.c
+++ b/hw/tpm/tpm_tis_i2c.c
@@ -110,13 +110,31 @@ static int tpm_tis_i2c_post_load(void *opaque, int version_id)
     return 0;
 }
 
+static bool tpm_tis_ext_buffer_migration_needed_i2c(void *opaque)
+{
+    TPMStateI2C *i2cst = opaque;
+
+    return tpm_tis_ext_buffer_migration_needed(&i2cst->state);
+}
+
+static const VMStateDescription vmstate_tpm_tis_ext_buffer_i2c = {
+    .name = "tpm-tis/ext_buffer",
+    .version_id = 0,
+    .needed = tpm_tis_ext_buffer_migration_needed_i2c,
+    .pre_save = tpm_tis_i2c_pre_save,
+    .fields = (const VMStateField[]) {
+        VMSTATE_BUFFER_START_MIDDLE(state.buffer, TPMStateI2C, 4096),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static const VMStateDescription vmstate_tpm_tis_i2c = {
     .name = "tpm-tis-i2c",
     .version_id = 0,
     .pre_save  = tpm_tis_i2c_pre_save,
     .post_load  = tpm_tis_i2c_post_load,
     .fields = (const VMStateField[]) {
-        VMSTATE_BUFFER(state.buffer, TPMStateI2C),
+        VMSTATE_PARTIAL_BUFFER(state.buffer, TPMStateI2C, 4096),
         VMSTATE_UINT16(state.rw_offset, TPMStateI2C),
         VMSTATE_UINT8(state.active_locty, TPMStateI2C),
         VMSTATE_UINT8(state.aborting_locty, TPMStateI2C),
@@ -133,6 +151,10 @@ static const VMStateDescription vmstate_tpm_tis_i2c = {
         VMSTATE_UINT8(csum_enable, TPMStateI2C),
 
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (const VMStateDescription *const[]) {
+        &vmstate_tpm_tis_ext_buffer_i2c,
+        NULL,
     }
 };
 
diff --git a/hw/tpm/tpm_tis_isa.c b/hw/tpm/tpm_tis_isa.c
index 61e95434f5..73038f1a10 100644
--- a/hw/tpm/tpm_tis_isa.c
+++ b/hw/tpm/tpm_tis_isa.c
@@ -49,12 +49,30 @@ static int tpm_tis_pre_save_isa(void *opaque)
     return tpm_tis_pre_save(&isadev->state);
 }
 
+static bool tpm_tis_ext_buffer_migration_needed_isa(void *opaque)
+{
+    TPMStateISA *isadev = opaque;
+
+    return tpm_tis_ext_buffer_migration_needed(&isadev->state);
+}
+
+static const VMStateDescription vmstate_tpm_tis_ext_buffer_isa = {
+    .name = "tpm-tis/ext_buffer",
+    .version_id = 0,
+    .needed = tpm_tis_ext_buffer_migration_needed_isa,
+    .pre_save = tpm_tis_pre_save_isa,
+    .fields = (const VMStateField[]) {
+        VMSTATE_BUFFER_START_MIDDLE(state.buffer, TPMStateISA, 4096),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static const VMStateDescription vmstate_tpm_tis_isa = {
     .name = "tpm-tis",
     .version_id = 0,
     .pre_save  = tpm_tis_pre_save_isa,
     .fields = (const VMStateField[]) {
-        VMSTATE_BUFFER(state.buffer, TPMStateISA),
+        VMSTATE_PARTIAL_BUFFER(state.buffer, TPMStateISA, 4096),
         VMSTATE_UINT16(state.rw_offset, TPMStateISA),
         VMSTATE_UINT8(state.active_locty, TPMStateISA),
         VMSTATE_UINT8(state.aborting_locty, TPMStateISA),
@@ -64,6 +82,10 @@ static const VMStateDescription vmstate_tpm_tis_isa = {
                              vmstate_locty, TPMLocality),
 
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (const VMStateDescription *const[]) {
+        &vmstate_tpm_tis_ext_buffer_isa,
+        NULL,
     }
 };
 
diff --git a/hw/tpm/tpm_tis_sysbus.c b/hw/tpm/tpm_tis_sysbus.c
index e9372e7316..86fc5a592c 100644
--- a/hw/tpm/tpm_tis_sysbus.c
+++ b/hw/tpm/tpm_tis_sysbus.c
@@ -48,12 +48,30 @@ static int tpm_tis_pre_save_sysbus(void *opaque)
     return tpm_tis_pre_save(&sbdev->state);
 }
 
+static bool tpm_tis_ext_buffer_migration_needed_sysbus(void *opaque)
+{
+    TPMStateSysBus *sbdev = opaque;
+
+    return tpm_tis_ext_buffer_migration_needed(&sbdev->state);
+}
+
+static const VMStateDescription vmstate_tpm_tis_ext_buffer_sysbus = {
+    .name = "tpm-tis/ext_buffer",
+    .version_id = 0,
+    .needed = tpm_tis_ext_buffer_migration_needed_sysbus,
+    .pre_save  = tpm_tis_pre_save_sysbus,
+    .fields = (const VMStateField[]) {
+        VMSTATE_BUFFER_START_MIDDLE(state.buffer, TPMStateSysBus, 4096),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static const VMStateDescription vmstate_tpm_tis_sysbus = {
     .name = "tpm-tis",
     .version_id = 0,
     .pre_save  = tpm_tis_pre_save_sysbus,
     .fields = (const VMStateField[]) {
-        VMSTATE_BUFFER(state.buffer, TPMStateSysBus),
+        VMSTATE_PARTIAL_BUFFER(state.buffer, TPMStateSysBus, 4096),
         VMSTATE_UINT16(state.rw_offset, TPMStateSysBus),
         VMSTATE_UINT8(state.active_locty, TPMStateSysBus),
         VMSTATE_UINT8(state.aborting_locty, TPMStateSysBus),
@@ -63,6 +81,10 @@ static const VMStateDescription vmstate_tpm_tis_sysbus = {
                              0, vmstate_locty, TPMLocality),
 
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (const VMStateDescription *const[]) {
+        &vmstate_tpm_tis_ext_buffer_sysbus,
+        NULL,
     }
 };
 
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v3 10/10] tests: Use ML-DSA-87 operations to caused large TPM transfers with TIS
  2026-04-06 14:17 [PATCH v3 00/10] hw/tpm: CRB chunking capability to handle PQC Arun Menon
                   ` (8 preceding siblings ...)
  2026-04-06 14:17 ` [PATCH v3 09/10] tpm: Extend TPM TIS buffer size to 8192 bytes Arun Menon
@ 2026-04-06 14:17 ` Arun Menon
  9 siblings, 0 replies; 22+ messages in thread
From: Arun Menon @ 2026-04-06 14:17 UTC (permalink / raw)
  To: qemu-devel
  Cc: Ani Sinha, Laurent Vivier, Zhao Liu, Stefan Berger,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas, marcandre.lureau,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé, Arun Menon, Stefan Berger

From: Stefan Berger <stefanb@linux.ibm.com>

Test a large data transfer with the TIS. To do this, first create an
ML-DSA-87 key and sign some data with it. Then receive the 4627 bytes
signature and then send the signature back to have the TPM verify it.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 tests/qtest/tpm-tis-swtpm-test.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/tests/qtest/tpm-tis-swtpm-test.c b/tests/qtest/tpm-tis-swtpm-test.c
index 105e42e21d..56d2a340c9 100644
--- a/tests/qtest/tpm-tis-swtpm-test.c
+++ b/tests/qtest/tpm-tis-swtpm-test.c
@@ -36,6 +36,15 @@ static void tpm_tis_swtpm_test(const void *data)
                         "tpm-tis", NULL);
 }
 
+static void tpm_tis_large_tx_swtpm_test(const void *data)
+{
+    const TestState *ts = data;
+
+    tpm_test_swtpm_large_tx_test(ts->src_tpm_path,
+                                 tpm_tis_transfer,
+                                 "tpm-tis", NULL);
+}
+
 static void tpm_tis_swtpm_migration_test(const void *data)
 {
     const TestState *ts = data;
@@ -57,6 +66,8 @@ int main(int argc, char **argv)
     g_test_init(&argc, &argv, NULL);
 
     qtest_add_data_func("/tpm/tis-swtpm/test", &ts, tpm_tis_swtpm_test);
+    qtest_add_data_func("/tpm/tis-larget-tx-swtpm/test", &ts,
+                        tpm_tis_large_tx_swtpm_test);
     qtest_add_data_func("/tpm/tis-swtpm-migration/test", &ts,
                         tpm_tis_swtpm_migration_test);
     ret = g_test_run();
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 22+ messages in thread

* Re: [PATCH v3 01/10] hw/tpm: Add TPM CRB chunking fields
  2026-04-06 14:17 ` [PATCH v3 01/10] hw/tpm: Add TPM CRB chunking fields Arun Menon
@ 2026-04-07  7:49   ` Marc-André Lureau
  2026-04-15 13:35     ` Arun Menon
  0 siblings, 1 reply; 22+ messages in thread
From: Marc-André Lureau @ 2026-04-07  7:49 UTC (permalink / raw)
  To: Arun Menon
  Cc: qemu-devel, Ani Sinha, Laurent Vivier, Zhao Liu, Stefan Berger,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé, Stefan Berger

Hi

On Mon, Apr 6, 2026 at 6:18 PM Arun Menon <armenon@redhat.com> wrote:
>
> 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]
>
> [1] https://trustedcomputinggroup.org/wp-content/uploads/PC-Client-Specific-Platform-TPM-Profile-for-TPM-2p0-v1p07_rc1_121225.pdf

You may update the link to the released version
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      | 3 +++
>  include/hw/acpi/tpm.h | 5 ++++-
>  2 files changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
> index 8723536f93..0a1c7ecdc6 100644
> --- a/hw/tpm/tpm_crb.c
> +++ b/hw/tpm/tpm_crb.c
> @@ -59,6 +59,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)
>
> @@ -262,6 +263,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, CRB_INTF_CAP_CRB_CHUNK);
>      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 d2bf6637c5..03d452d2b5 100644
> --- a/include/hw/acpi/tpm.h
> +++ b/include/hw/acpi/tpm.h
> @@ -149,7 +149,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)
> @@ -168,6 +168,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, invoke, 0, 1)

Any reason to diverge from the spec "Start" naming here?

> +  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.53.0
>



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v3 02/10] hw/tpm: Refactor CRB_CTRL_START register access
  2026-04-06 14:17 ` [PATCH v3 02/10] hw/tpm: Refactor CRB_CTRL_START register access Arun Menon
@ 2026-04-07  7:50   ` Marc-André Lureau
  0 siblings, 0 replies; 22+ messages in thread
From: Marc-André Lureau @ 2026-04-07  7:50 UTC (permalink / raw)
  To: Arun Menon
  Cc: qemu-devel, Ani Sinha, Laurent Vivier, Zhao Liu, Stefan Berger,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé, Stefan Berger

On Mon, Apr 6, 2026 at 6:18 PM Arun Menon <armenon@redhat.com> wrote:
>
> 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>

(even if we decide to rename invoke->Start)

> ---
>  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 0a1c7ecdc6..bc55908786 100644
> --- a/hw/tpm/tpm_crb.c
> +++ b/hw/tpm/tpm_crb.c
> @@ -145,7 +145,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, invoke, 1);
>              s->cmd = (TPMBackendCmd) {
>                  .in = mem,
>                  .in_len = MIN(tpm_cmd_get_size(mem), s->be_buffer_size),
> @@ -194,7 +194,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, invoke, 0);
>      if (ret != 0) {
>          ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS,
>                           tpmSts, 1); /* fatal error */
> --
> 2.53.0
>



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v3 03/10] hw/tpm: Add internal buffer state for chunking
  2026-04-06 14:17 ` [PATCH v3 03/10] hw/tpm: Add internal buffer state for chunking Arun Menon
@ 2026-04-07  7:56   ` Marc-André Lureau
  2026-04-15 13:34     ` Arun Menon
  0 siblings, 1 reply; 22+ messages in thread
From: Marc-André Lureau @ 2026-04-07  7:56 UTC (permalink / raw)
  To: Arun Menon
  Cc: qemu-devel, Ani Sinha, Laurent Vivier, Zhao Liu, Stefan Berger,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé

On Mon, Apr 6, 2026 at 6:18 PM Arun Menon <armenon@redhat.com> wrote:
>
> 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>

> ---
>  hw/tpm/tpm_crb.c | 34 ++++++++++++++++++++++++++++++----
>  1 file changed, 30 insertions(+), 4 deletions(-)
>
> diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
> index bc55908786..d65b3e2cc1 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;
>
>      bool ppi_enabled;
>      TPMPPI ppi;
> @@ -85,6 +88,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)
>  {
> @@ -134,9 +144,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:
> @@ -240,6 +252,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));
>
> @@ -306,6 +319,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();
> +
>      if (s->ppi_enabled) {
>          tpm_ppi_init(&s->ppi, get_system_memory(),
>                       TPM_PPI_ADDR_BASE, OBJECT(s));
> @@ -318,12 +334,22 @@ static void tpm_crb_realize(DeviceState *dev, Error **errp)
>      }
>  }
>
> +static void tpm_crb_unrealize(DeviceState *dev)
> +{
> +    CRBState *s = CRB(dev);
> +
> +    g_byte_array_unref(s->command_buffer);
> +    g_byte_array_unref(s->response_buffer);

I have a preference for g_clear_pointer(&ptr, g_byte_array_unref), it
is safer, but unrealize() should only be called once after realize()
so ok.

> +
> +}
> +
>  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.53.0
>



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v3 04/10] hw/tpm: Implement TPM CRB chunking logic
  2026-04-06 14:17 ` [PATCH v3 04/10] hw/tpm: Implement TPM CRB chunking logic Arun Menon
@ 2026-04-07  8:21   ` Marc-André Lureau
  0 siblings, 0 replies; 22+ messages in thread
From: Marc-André Lureau @ 2026-04-07  8:21 UTC (permalink / raw)
  To: Arun Menon
  Cc: qemu-devel, Ani Sinha, Laurent Vivier, Zhao Liu, Stefan Berger,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé, Stefan Berger

Hi

On Mon, Apr 6, 2026 at 6:18 PM Arun Menon <armenon@redhat.com> wrote:
>
> 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_rc1_121225.pdf

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 | 148 +++++++++++++++++++++++++++++++++++++++++------
>  1 file changed, 131 insertions(+), 17 deletions(-)
>
> diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
> index d65b3e2cc1..b9f295db7a 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"
> @@ -65,6 +66,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),
> @@ -80,6 +82,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 {
> @@ -122,6 +126,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, invoke, 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)
>  {
> @@ -152,20 +219,55 @@ 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);
> -
> -            ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, invoke, 1);
> -            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,
> -            };
> -
> -            tpm_backend_deliver_request(s->tpmbe, &s->cmd);
> +        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 (!(s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE)) {
> +                if (!tpm_crb_append_command_request(s)) {
> +                    break;
> +                }
> +                ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, invoke, 1);
> +                g_byte_array_set_size(s->response_buffer, s->be_buffer_size);
> +                s->cmd = (TPMBackendCmd) {
> +                    .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) {
> +            /*
> +             * 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->response_buffer->len > 0) {
> +                trace_tpm_crb_mmio_write(addr, size, val);
> +                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:
> @@ -210,8 +312,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)
> @@ -288,8 +403,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.53.0
>



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v3 05/10] test/qtest: Add test for tpm crb chunking
  2026-04-06 14:17 ` [PATCH v3 05/10] test/qtest: Add test for tpm crb chunking Arun Menon
@ 2026-04-07 10:40   ` Marc-André Lureau
  2026-04-15 13:34     ` Arun Menon
  0 siblings, 1 reply; 22+ messages in thread
From: Marc-André Lureau @ 2026-04-07 10:40 UTC (permalink / raw)
  To: Arun Menon
  Cc: qemu-devel, Ani Sinha, Laurent Vivier, Zhao Liu, Stefan Berger,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé

Hi

On Mon, Apr 6, 2026 at 6:18 PM Arun Menon <armenon@redhat.com> wrote:
>
> 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()
>
> Signed-off-by: Arun Menon <armenon@redhat.com>

This alone doesn't yet test the chunked write/read, as the transfers
aren't larger. The following patch "tests: Use ML-DSA-87 operations to
caused large TPM transfers with CRB" will. It may be worth to say in
the commit message

with other minor style nitpicks below

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

> ---
>  tests/qtest/tpm-crb-swtpm-test.c |  10 +++
>  tests/qtest/tpm-util.c           | 108 ++++++++++++++++++++++++++-----
>  tests/qtest/tpm-util.h           |   5 ++
>  3 files changed, 108 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..603c5a825d 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,74 @@ 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;
> +
> +    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);
> +
> +    size_t 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) {

extra space before ;

> +        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);

extra space after START

> +            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
> +     */
> +
> +    unsigned char header[10];
> +    qtest_memread(s, raddr, header, sizeof(header));
> +
> +    uint32_t actual_response_size = ldl_be_p(&header[2]);

avoid mixing code & declaration per qemu coding style.

> +
> +    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.53.0
>



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v3 06/10] hw/tpm: Add support for VM migration with TPM CRB chunking
  2026-04-06 14:17 ` [PATCH v3 06/10] hw/tpm: Add support for VM migration with TPM CRB chunking Arun Menon
@ 2026-04-07 11:00   ` Marc-André Lureau
  2026-04-10 14:39     ` Arun Menon
  0 siblings, 1 reply; 22+ messages in thread
From: Marc-André Lureau @ 2026-04-07 11:00 UTC (permalink / raw)
  To: Arun Menon, Stefan Berger
  Cc: qemu-devel, Ani Sinha, Laurent Vivier, Zhao Liu, Marcel Apfelbaum,
	Paolo Bonzini, Fabiano Rosas, Michael S. Tsirkin, Yanan Wang,
	Igor Mammedov, Philippe Mathieu-Daudé

Hi

On Mon, Apr 6, 2026 at 6:18 PM Arun Menon <armenon@redhat.com> wrote:
>
> From: Arun Menon <armenon@redhat.com>
>
> - Add subsection in VMState for TPM CRB with the newly introduced
>   command and response buffers, along with a needed callback, so that
>   newer QEMU only sends the buffers if it is necessary.
> - Add hw_compat blocker because the feature is only supported for
>   machine type 11.1 and higher.
> - If the VM has no pending chunked TPM commands in the internal buffers
>   during a VM migration, or if the machine type does not support newly
>   introduced buffers, then the needed callback will return false, as it
>   checks the hw_compat blocker and thus the subsection will not be sent
>   to the destination host.
>
> Signed-off-by: Arun Menon <armenon@redhat.com>
> ---
>  hw/core/machine.c |  4 +++-
>  hw/tpm/tpm_crb.c  | 31 +++++++++++++++++++++++++++++++
>  2 files changed, 34 insertions(+), 1 deletion(-)
>
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index 1abc8ae737..fb290c6c53 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -38,7 +38,9 @@
>  #include "hw/acpi/generic_event_device.h"
>  #include "qemu/audio.h"
>
> -GlobalProperty hw_compat_11_0[] = {};
> +GlobalProperty hw_compat_11_0[] = {
> +    { "tpm-crb", "migrate-buffers", "off"},

If this is not intended to be user visible, we should use x- prefix

The problem is that the previous code changes expose CapChunk to the
guest unconditionally.

If running with <=11.0 we should not enable chunk transfer by default
- or we need to prevent/block migration... otherwise we may lose
VM/device state, expose a differente device etc.

What about having a "cap-chunk" property? Enable it by default with >=11.1.

If enabled when <=11.0, then also disable migration.

> +};
>  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 b9f295db7a..81471dd9f8 100644
> --- a/hw/tpm/tpm_crb.c
> +++ b/hw/tpm/tpm_crb.c
> @@ -49,6 +49,8 @@ struct CRBState {
>
>      bool ppi_enabled;
>      TPMPPI ppi;
> +
> +    bool migrate_buffers;
>  };
>  typedef struct CRBState CRBState;
>
> @@ -345,18 +347,47 @@ static int tpm_crb_pre_save(void *opaque)
>      return 0;
>  }
>
> +static bool tpm_crb_chunk_needed(void *opaque)
> +{
> +    CRBState *s = opaque;
> +
> +    if (!s->migrate_buffers) {
> +        return false;
> +    }
> +
> +    return ((s->command_buffer && s->command_buffer->len > 0) ||
> +            (s->response_buffer && s->response_buffer->len > 0));
> +}
> +
> +static const VMStateDescription vmstate_tpm_crb_chunk = {
> +    .name = "tpm-crb/chunk",
> +    .version_id = 0,
> +    .needed = tpm_crb_chunk_needed,
> +    .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("ppi", CRBState, ppi_enabled, true),
> +    DEFINE_PROP_BOOL("migrate-buffers", CRBState, migrate_buffers, true),
>  };
>
>  static void tpm_crb_reset(void *dev)
> --
> 2.53.0
>



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v3 06/10] hw/tpm: Add support for VM migration with TPM CRB chunking
  2026-04-07 11:00   ` Marc-André Lureau
@ 2026-04-10 14:39     ` Arun Menon
  2026-04-10 16:39       ` Marc-André Lureau
  0 siblings, 1 reply; 22+ messages in thread
From: Arun Menon @ 2026-04-10 14:39 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: Stefan Berger, qemu-devel, Ani Sinha, Laurent Vivier, Zhao Liu,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé

On Tue, Apr 07, 2026 at 03:00:27PM +0400, Marc-André Lureau wrote:
> Hi
> 
> On Mon, Apr 6, 2026 at 6:18 PM Arun Menon <armenon@redhat.com> wrote:
> >
> > From: Arun Menon <armenon@redhat.com>
> >
> > - Add subsection in VMState for TPM CRB with the newly introduced
> >   command and response buffers, along with a needed callback, so that
> >   newer QEMU only sends the buffers if it is necessary.
> > - Add hw_compat blocker because the feature is only supported for
> >   machine type 11.1 and higher.
> > - If the VM has no pending chunked TPM commands in the internal buffers
> >   during a VM migration, or if the machine type does not support newly
> >   introduced buffers, then the needed callback will return false, as it
> >   checks the hw_compat blocker and thus the subsection will not be sent
> >   to the destination host.
> >
> > Signed-off-by: Arun Menon <armenon@redhat.com>
> > ---
> >  hw/core/machine.c |  4 +++-
> >  hw/tpm/tpm_crb.c  | 31 +++++++++++++++++++++++++++++++
> >  2 files changed, 34 insertions(+), 1 deletion(-)
> >
> > diff --git a/hw/core/machine.c b/hw/core/machine.c
> > index 1abc8ae737..fb290c6c53 100644
> > --- a/hw/core/machine.c
> > +++ b/hw/core/machine.c
> > @@ -38,7 +38,9 @@
> >  #include "hw/acpi/generic_event_device.h"
> >  #include "qemu/audio.h"
> >
> > -GlobalProperty hw_compat_11_0[] = {};
> > +GlobalProperty hw_compat_11_0[] = {
> > +    { "tpm-crb", "migrate-buffers", "off"},
> 
> If this is not intended to be user visible, we should use x- prefix

Yes, I will add x- prefix.

> 
> The problem is that the previous code changes expose CapChunk to the
> guest unconditionally.
> 
> If running with <=11.0 we should not enable chunk transfer by default
> - or we need to prevent/block migration... otherwise we may lose
> VM/device state, expose a differente device etc.
> 
> What about having a "cap-chunk" property? Enable it by default with >=11.1.
> 
> If enabled when <=11.0, then also disable migration.

This is really good insight. Similar to migrate-buffers, I need to make
sure that the CapCRBChunk bit is set only when we are running the newer
machine type.

I shall introduce a new entry in hw_compat array called x-cap-chunk.
That means, we should not unconditionally set the CapCRBChunk bit to 1.
We should set the bit based on cap_chunk property.

However, I am a bit confused on the order of precedence of this variable.
From what I understand, the device initialization happens first ->
followed by the compat properties

So,
"DEFINE_PROP_BOOL("migrate-buffers", CRBState, migrate_buffers, true)"
gets executed first setting it to true by default and then, the compat
property is applied,
"GlobalProperty hw_compat_11_0[] = {
    { "tpm-crb", "migrate-buffers", "off"},
}"
setting it to false.

When is the user provided command line argument parsed?
For example launching qemu using:
    -machine pc-11.0 
    -device tpm-crb,tpmdev=tpm0,cap-chunk=on

Is it set in the end? In the above example, the user explicitly set
cap-chunk to true whereas the migrate-buffers default value is false.

Therefore we end up with the following scenario.
Since chunking is supported, the guest writes a large command in the
buffer. The user then decides to migrate the VM. QEMU sees that
migrate-buffer is off, so it does not migrate the buffers. When the VM
resumes on the destination host, it will find the buffers empty.

Therefore, we need to stop migration if cap-chunk is enabled but
migrate-buffers is disabled. Please guide.

> 
> > +};
> >  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 b9f295db7a..81471dd9f8 100644
> > --- a/hw/tpm/tpm_crb.c
> > +++ b/hw/tpm/tpm_crb.c
> > @@ -49,6 +49,8 @@ struct CRBState {
> >
> >      bool ppi_enabled;
> >      TPMPPI ppi;
> > +
> > +    bool migrate_buffers;
> >  };
> >  typedef struct CRBState CRBState;
> >
> > @@ -345,18 +347,47 @@ static int tpm_crb_pre_save(void *opaque)
> >      return 0;
> >  }
> >
> > +static bool tpm_crb_chunk_needed(void *opaque)
> > +{
> > +    CRBState *s = opaque;
> > +
> > +    if (!s->migrate_buffers) {
> > +        return false;
> > +    }
> > +
> > +    return ((s->command_buffer && s->command_buffer->len > 0) ||
> > +            (s->response_buffer && s->response_buffer->len > 0));
> > +}
> > +
> > +static const VMStateDescription vmstate_tpm_crb_chunk = {
> > +    .name = "tpm-crb/chunk",
> > +    .version_id = 0,
> > +    .needed = tpm_crb_chunk_needed,
> > +    .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("ppi", CRBState, ppi_enabled, true),
> > +    DEFINE_PROP_BOOL("migrate-buffers", CRBState, migrate_buffers, true),
> >  };
> >
> >  static void tpm_crb_reset(void *dev)
> > --
> > 2.53.0
> >
>

Regards,
Arun Menon



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v3 06/10] hw/tpm: Add support for VM migration with TPM CRB chunking
  2026-04-10 14:39     ` Arun Menon
@ 2026-04-10 16:39       ` Marc-André Lureau
  0 siblings, 0 replies; 22+ messages in thread
From: Marc-André Lureau @ 2026-04-10 16:39 UTC (permalink / raw)
  To: Arun Menon
  Cc: Stefan Berger, qemu-devel, Ani Sinha, Laurent Vivier, Zhao Liu,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé

Hi

On Fri, Apr 10, 2026 at 6:41 PM Arun Menon <armenon@redhat.com> wrote:
>
> However, I am a bit confused on the order of precedence of this variable.
> From what I understand, the device initialization happens first ->
> followed by the compat properties
>
> So,
> "DEFINE_PROP_BOOL("migrate-buffers", CRBState, migrate_buffers, true)"
> gets executed first setting it to true by default and then, the compat
> property is applied,
> "GlobalProperty hw_compat_11_0[] = {
>     { "tpm-crb", "migrate-buffers", "off"},
> }"
> setting it to false.
>
> When is the user provided command line argument parsed?
> For example launching qemu using:
>     -machine pc-11.0
>     -device tpm-crb,tpmdev=tpm0,cap-chunk=on
>
> Is it set in the end? In the above example, the user explicitly set
> cap-chunk to true whereas the migrate-buffers default value is false.

Iirc, user specified property takes precedence over compat properties.

> Therefore we end up with the following scenario.
> Since chunking is supported, the guest writes a large command in the
> buffer. The user then decides to migrate the VM. QEMU sees that
> migrate-buffer is off, so it does not migrate the buffers. When the VM
> resumes on the destination host, it will find the buffers empty.

>
> Therefore, we need to stop migration if cap-chunk is enabled but
> migrate-buffers is disabled. Please guide.

Yes, if cap-chunk is enabled but migrate-buffer is off (compat 11.0),
there should be a migration blocker.


-- 
Marc-André Lureau


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v3 05/10] test/qtest: Add test for tpm crb chunking
  2026-04-07 10:40   ` Marc-André Lureau
@ 2026-04-15 13:34     ` Arun Menon
  0 siblings, 0 replies; 22+ messages in thread
From: Arun Menon @ 2026-04-15 13:34 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Ani Sinha, Laurent Vivier, Zhao Liu, Stefan Berger,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé

On Tue, Apr 07, 2026 at 02:40:51PM +0400, Marc-André Lureau wrote:
> Hi
>
> On Mon, Apr 6, 2026 at 6:18 PM Arun Menon <armenon@redhat.com> wrote:
> >
> > 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()
> >
> > Signed-off-by: Arun Menon <armenon@redhat.com>
>
> This alone doesn't yet test the chunked write/read, as the transfers
> aren't larger. The following patch "tests: Use ML-DSA-87 operations to
> caused large TPM transfers with CRB" will. It may be worth to say in
> the commit message
>
> with other minor style nitpicks below

Thanks for the review, I have incorporated the feedback in the next
revision.

>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> > ---
> >  tests/qtest/tpm-crb-swtpm-test.c |  10 +++
> >  tests/qtest/tpm-util.c           | 108 ++++++++++++++++++++++++++-----
> >  tests/qtest/tpm-util.h           |   5 ++
> >  3 files changed, 108 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..603c5a825d 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,74 @@ 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;
> > +
> > +    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);
> > +
> > +    size_t 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) {
>
> extra space before ;
>
> > +        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);
>
> extra space after START
>
> > +            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
> > +     */
> > +
> > +    unsigned char header[10];
> > +    qtest_memread(s, raddr, header, sizeof(header));
> > +
> > +    uint32_t actual_response_size = ldl_be_p(&header[2]);
>
> avoid mixing code & declaration per qemu coding style.
>
> > +
> > +    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.53.0
> >
>

Regards,
Arun Menon



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v3 03/10] hw/tpm: Add internal buffer state for chunking
  2026-04-07  7:56   ` Marc-André Lureau
@ 2026-04-15 13:34     ` Arun Menon
  0 siblings, 0 replies; 22+ messages in thread
From: Arun Menon @ 2026-04-15 13:34 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Ani Sinha, Laurent Vivier, Zhao Liu, Stefan Berger,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé

On Tue, Apr 07, 2026 at 11:56:58AM +0400, Marc-André Lureau wrote:
> On Mon, Apr 6, 2026 at 6:18 PM Arun Menon <armenon@redhat.com> wrote:
> >
> > 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>
>
> > ---
> >  hw/tpm/tpm_crb.c | 34 ++++++++++++++++++++++++++++++----
> >  1 file changed, 30 insertions(+), 4 deletions(-)
> >
> > diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
> > index bc55908786..d65b3e2cc1 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;
> >
> >      bool ppi_enabled;
> >      TPMPPI ppi;
> > @@ -85,6 +88,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)
> >  {
> > @@ -134,9 +144,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:
> > @@ -240,6 +252,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));
> >
> > @@ -306,6 +319,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();
> > +
> >      if (s->ppi_enabled) {
> >          tpm_ppi_init(&s->ppi, get_system_memory(),
> >                       TPM_PPI_ADDR_BASE, OBJECT(s));
> > @@ -318,12 +334,22 @@ static void tpm_crb_realize(DeviceState *dev, Error **errp)
> >      }
> >  }
> >
> > +static void tpm_crb_unrealize(DeviceState *dev)
> > +{
> > +    CRBState *s = CRB(dev);
> > +
> > +    g_byte_array_unref(s->command_buffer);
> > +    g_byte_array_unref(s->response_buffer);
>
> I have a preference for g_clear_pointer(&ptr, g_byte_array_unref), it
> is safer, but unrealize() should only be called once after realize()
> so ok.

Thanks for the tip. I have updated it to use g_clear_pointer in the next
revision.

>
> > +
> > +}
> > +
> >  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.53.0
> >
>

Regards,
Arun Menon



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v3 01/10] hw/tpm: Add TPM CRB chunking fields
  2026-04-07  7:49   ` Marc-André Lureau
@ 2026-04-15 13:35     ` Arun Menon
  0 siblings, 0 replies; 22+ messages in thread
From: Arun Menon @ 2026-04-15 13:35 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Ani Sinha, Laurent Vivier, Zhao Liu, Stefan Berger,
	Marcel Apfelbaum, Paolo Bonzini, Fabiano Rosas,
	Michael S. Tsirkin, Yanan Wang, Igor Mammedov,
	Philippe Mathieu-Daudé, Stefan Berger

On Tue, Apr 07, 2026 at 11:49:54AM +0400, Marc-André Lureau wrote:
> Hi
>
> On Mon, Apr 6, 2026 at 6:18 PM Arun Menon <armenon@redhat.com> wrote:
> >
> > 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]
> >
> > [1] https://trustedcomputinggroup.org/wp-content/uploads/PC-Client-Specific-Platform-TPM-Profile-for-TPM-2p0-v1p07_rc1_121225.pdf
>
> You may update the link to the released version
> 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      | 3 +++
> >  include/hw/acpi/tpm.h | 5 ++++-
> >  2 files changed, 7 insertions(+), 1 deletion(-)
> >
> > diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
> > index 8723536f93..0a1c7ecdc6 100644
> > --- a/hw/tpm/tpm_crb.c
> > +++ b/hw/tpm/tpm_crb.c
> > @@ -59,6 +59,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)
> >
> > @@ -262,6 +263,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, CRB_INTF_CAP_CRB_CHUNK);
> >      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 d2bf6637c5..03d452d2b5 100644
> > --- a/include/hw/acpi/tpm.h
> > +++ b/include/hw/acpi/tpm.h
> > @@ -149,7 +149,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)
> > @@ -168,6 +168,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, invoke, 0, 1)
>
> Any reason to diverge from the spec "Start" naming here?

No reason; I think I should stick to spec. I saw cancel invoke and I
thought start should also have an invoke.
I will change it to 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.53.0
> >
>

Regards,
Arun Menon



^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2026-04-15 13:36 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-06 14:17 [PATCH v3 00/10] hw/tpm: CRB chunking capability to handle PQC Arun Menon
2026-04-06 14:17 ` [PATCH v3 01/10] hw/tpm: Add TPM CRB chunking fields Arun Menon
2026-04-07  7:49   ` Marc-André Lureau
2026-04-15 13:35     ` Arun Menon
2026-04-06 14:17 ` [PATCH v3 02/10] hw/tpm: Refactor CRB_CTRL_START register access Arun Menon
2026-04-07  7:50   ` Marc-André Lureau
2026-04-06 14:17 ` [PATCH v3 03/10] hw/tpm: Add internal buffer state for chunking Arun Menon
2026-04-07  7:56   ` Marc-André Lureau
2026-04-15 13:34     ` Arun Menon
2026-04-06 14:17 ` [PATCH v3 04/10] hw/tpm: Implement TPM CRB chunking logic Arun Menon
2026-04-07  8:21   ` Marc-André Lureau
2026-04-06 14:17 ` [PATCH v3 05/10] test/qtest: Add test for tpm crb chunking Arun Menon
2026-04-07 10:40   ` Marc-André Lureau
2026-04-15 13:34     ` Arun Menon
2026-04-06 14:17 ` [PATCH v3 06/10] hw/tpm: Add support for VM migration with TPM CRB chunking Arun Menon
2026-04-07 11:00   ` Marc-André Lureau
2026-04-10 14:39     ` Arun Menon
2026-04-10 16:39       ` Marc-André Lureau
2026-04-06 14:17 ` [PATCH v3 07/10] qtests: Enable starting swtpm with a given profile Arun Menon
2026-04-06 14:17 ` [PATCH v3 08/10] tests: Use ML-DSA-87 operations to caused large TPM transfers with CRB Arun Menon
2026-04-06 14:17 ` [PATCH v3 09/10] tpm: Extend TPM TIS buffer size to 8192 bytes Arun Menon
2026-04-06 14:17 ` [PATCH v3 10/10] tests: Use ML-DSA-87 operations to caused large TPM transfers with TIS 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.