@samitouri / QOSamiQemu / commits / 866e457a66

test/qtest: Add test for tpm crb chunking

- New test case added to the swtpm test. Data is written and read from the buffer in chunks. - The chunk size is dynamically calculated by reading the CRB_CTRL_CMD_SIZE address. This can be changed manually to test. - Add a helper function tpm_wait_till_bit_clear() - Note that this commit does not yet exercise the chunked read/write logic, as current transfer sizes remain small. Testing for large transfers is introduced in a subsequent patch: 'tests: Use ML-DSA-87 operations to cause large TPM transfers with CRB' Signed-off-by: Arun Menon <armenon@redhat.com> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> Link: https://lore.kernel.org/qemu-devel/20260506075813.120781-6-armenon@redhat.com Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>

Arun Menon committed May 6, 2026 at 13:28 UTC 866e457a66243629be9c4421448b05ed12d62244
3 files changed +109 -15
tests/qtest/tpm-crb-swtpm-test.c
+10
@@ -33,6 +33,14 @@ static void tpm_crb_swtpm_test(const void *data)
33 "tpm-crb", NULL);
34 }
35
36 +static void tpm_crb_chunk_swtpm_test(const void *data)
37 +{
38 + const TestState *ts = data;
39 +
40 + tpm_test_swtpm_test(ts->src_tpm_path, tpm_util_crb_chunk_transfer,
41 + "tpm-crb", NULL);
42 +}
43 +
44 static void tpm_crb_swtpm_migration_test(const void *data)
45 {
46 const TestState *ts = data;
@@ -54,6 +62,8 @@ int main(int argc, char **argv)
62 g_test_init(&argc, &argv, NULL);
63
64 qtest_add_data_func("/tpm/crb-swtpm/test", &ts, tpm_crb_swtpm_test);
65 + qtest_add_data_func("/tpm/crb-chunk-swtpm/test", &ts,
66 + tpm_crb_chunk_swtpm_test);
67 qtest_add_data_func("/tpm/crb-swtpm-migration/test", &ts,
68 tpm_crb_swtpm_migration_test);
69 ret = g_test_run();
tests/qtest/tpm-util.c
+94 -15
@@ -14,16 +14,44 @@
14
15 #include "qemu/osdep.h"
16 #include <glib/gstdio.h>
17 +#include "qemu/bswap.h"
18
19 #include "hw/acpi/tpm.h"
20 #include "libqtest.h"
21 #include "tpm-util.h"
22 #include "qobject/qdict.h"
23
24 +#define CRB_ADDR_START (TPM_CRB_ADDR_BASE + A_CRB_CTRL_START)
25 +#define CRB_ADDR_CTRL_STS (TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS)
26 +#define CRB_ADDR_CTRL_CMD_SIZE \
27 + (TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_SIZE)
28 +
29 +#define CRB_START_INVOKE (1 << 0)
30 +#define CRB_START_RSP_RETRY (1 << 1)
31 +#define CRB_START_NEXT_CHUNK (1 << 2)
32 +
33 +void tpm_wait_till_bit_clear(QTestState *s, uint64_t addr, uint32_t mask)
34 +{
35 + uint32_t sts;
36 + uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
37 +
38 + while (true) {
39 + sts = qtest_readl(s, addr);
40 + if ((sts & mask) == 0) {
41 + break;
42 + }
43 + if (g_get_monotonic_time() >= end_time) {
44 + g_assert_cmphex(sts & mask, ==, 0);
45 + break;
46 + }
47 + }
48 +}
49 +
50 void tpm_util_crb_transfer(QTestState *s,
51 const unsigned char *req, size_t req_size,
52 unsigned char *rsp, size_t rsp_size)
53 {
54 + uint32_t tpm_sts;
55 uint64_t caddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR);
56 uint64_t raddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR);
57
@@ -31,24 +59,75 @@ void tpm_util_crb_transfer(QTestState *s,
59
60 qtest_memwrite(s, caddr, req, req_size);
61
34 - uint32_t sts, start = 1;
35 - uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
36 - qtest_writel(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start);
37 - while (true) {
38 - start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START);
39 - if ((start & 1) == 0) {
40 - break;
62 + qtest_writel(s, CRB_ADDR_START, CRB_START_INVOKE);
63 + tpm_wait_till_bit_clear(s, CRB_ADDR_START, CRB_START_INVOKE);
64 +
65 + tpm_sts = qtest_readl(s, CRB_ADDR_CTRL_STS);
66 + g_assert_cmpint(tpm_sts & 1, ==, 0);
67 +
68 + qtest_memread(s, raddr, rsp, rsp_size);
69 +}
70 +
71 +void tpm_util_crb_chunk_transfer(QTestState *s,
72 + const unsigned char *req, size_t req_size,
73 + unsigned char *rsp, size_t rsp_size)
74 +{
75 + uint32_t tpm_sts;
76 + size_t chunk_size;
77 + unsigned char header[10];
78 + uint32_t actual_response_size = 0;
79 +
80 + uint64_t caddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR);
81 + uint64_t raddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR);
82 + uint32_t crb_ctrl_cmd_size = qtest_readl(s, CRB_ADDR_CTRL_CMD_SIZE);
83 +
84 + chunk_size = crb_ctrl_cmd_size;
85 +
86 + qtest_writeb(s, TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 1);
87 +
88 + for (size_t i = 0; i < req_size; i += chunk_size) {
89 + bool last_chunk = false;
90 + size_t current_chunk_size = chunk_size;
91 +
92 + if (i + chunk_size > req_size) {
93 + last_chunk = true;
94 + current_chunk_size = req_size - i;
95 }
42 - if (g_get_monotonic_time() >= end_time) {
43 - break;
96 +
97 + qtest_memwrite(s, caddr, req + i, current_chunk_size);
98 +
99 + if (last_chunk) {
100 + qtest_writel(s, CRB_ADDR_START, CRB_START_INVOKE);
101 + tpm_wait_till_bit_clear(s, CRB_ADDR_START, CRB_START_INVOKE);
102 + } else {
103 + qtest_writel(s, CRB_ADDR_START, CRB_START_NEXT_CHUNK);
104 + tpm_wait_till_bit_clear(s, CRB_ADDR_START, CRB_START_NEXT_CHUNK);
105 }
45 - };
46 - start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START);
47 - g_assert_cmpint(start & 1, ==, 0);
48 - sts = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS);
49 - g_assert_cmpint(sts & 1, ==, 0);
106 + }
107 + tpm_sts = qtest_readl(s, CRB_ADDR_CTRL_STS);
108 + g_assert_cmpint(tpm_sts & 1, ==, 0);
109
51 - qtest_memread(s, raddr, rsp, rsp_size);
110 + /*
111 + * Read response in chunks
112 + */
113 +
114 + qtest_memread(s, raddr, header, sizeof(header));
115 + actual_response_size = ldl_be_p(&header[2]);
116 +
117 + if (actual_response_size > rsp_size) {
118 + actual_response_size = rsp_size;
119 + }
120 +
121 + for (size_t i = 0; i < actual_response_size; i += chunk_size) {
122 + size_t to_read = i + chunk_size > actual_response_size
123 + ? actual_response_size - i
124 + : chunk_size;
125 + if (i > 0) {
126 + qtest_writel(s, CRB_ADDR_START, CRB_START_NEXT_CHUNK);
127 + tpm_wait_till_bit_clear(s, CRB_ADDR_START, CRB_START_NEXT_CHUNK);
128 + }
129 + qtest_memread(s, raddr, rsp + i, to_read);
130 + }
131 }
132
133 void tpm_util_startup(QTestState *s, tx_func *tx)
tests/qtest/tpm-util.h
+5
@@ -24,10 +24,15 @@ typedef void (tx_func)(QTestState *s,
24 const unsigned char *req, size_t req_size,
25 unsigned char *rsp, size_t rsp_size);
26
27 +void tpm_wait_till_bit_clear(QTestState *s, uint64_t addr, uint32_t mask);
28 void tpm_util_crb_transfer(QTestState *s,
29 const unsigned char *req, size_t req_size,
30 unsigned char *rsp, size_t rsp_size);
31
32 +void tpm_util_crb_chunk_transfer(QTestState *s,
33 + const unsigned char *req, size_t req_size,
34 + unsigned char *rsp, size_t rsp_size);
35 +
36 void tpm_util_startup(QTestState *s, tx_func *tx);
37 void tpm_util_pcrextend(QTestState *s, tx_func *tx);
38 void tpm_util_pcrread(QTestState *s, tx_func *tx,