* [PULL v1 01/16] tests: Move TPM I2C bus read/write functions to common files
2026-06-01 21:53 [PULL v1 00/16] Merge tpm 2026/06/01 v1 Stefan Berger
@ 2026-06-01 21:53 ` Stefan Berger
2026-06-01 21:53 ` [PULL v1 02/16] tests: Have TPM I2C read/write functions take QTestState as first parameter Stefan Berger
` (15 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2026-06-01 21:53 UTC (permalink / raw)
To: qemu-devel; +Cc: peter.maydell, Stefan Berger, Arun Menon
Move functions for reading from and writing to the Aspeed I2C device into
a file so they can be reused by other functions.
Reviewed-by: Arun Menon <armenon@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260429121743.1346635-2-stefanb@linux.ibm.com
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
tests/qtest/meson.build | 2 +-
tests/qtest/tpm-tis-i2c-test.c | 50 +-------------------------
tests/qtest/tpm-tis-i2c-util.c | 64 ++++++++++++++++++++++++++++++++++
tests/qtest/tpm-tis-i2c-util.h | 30 ++++++++++++++++
4 files changed, 96 insertions(+), 50 deletions(-)
create mode 100644 tests/qtest/tpm-tis-i2c-util.c
create mode 100644 tests/qtest/tpm-tis-i2c-util.h
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 728dde54b3..57512c6e56 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -401,7 +401,7 @@ qtests = {
'tpm-crb-test': [io, tpmemu_files],
'tpm-tis-swtpm-test': [io, tpmemu_files, 'tpm-tis-util.c'],
'tpm-tis-test': [io, tpmemu_files, 'tpm-tis-util.c'],
- 'tpm-tis-i2c-test': [io, tpmemu_files, 'qtest_aspeed.c'],
+ 'tpm-tis-i2c-test': [io, tpmemu_files, 'tpm-tis-i2c-util.c', 'qtest_aspeed.c'],
'tpm-tis-device-swtpm-test': [io, tpmemu_files, 'tpm-tis-util.c'],
'tpm-tis-device-test': [io, tpmemu_files, 'tpm-tis-util.c'],
'virtio-net-failover': test_migration_files,
diff --git a/tests/qtest/tpm-tis-i2c-test.c b/tests/qtest/tpm-tis-i2c-test.c
index 3a1af026f2..02ddf76c2c 100644
--- a/tests/qtest/tpm-tis-i2c-test.c
+++ b/tests/qtest/tpm-tis-i2c-test.c
@@ -20,6 +20,7 @@
#include "hw/pci/pci_ids.h"
#include "qtest_aspeed.h"
#include "tpm-emu.h"
+#include "tpm-tis-i2c-util.h"
#define DEBUG_TIS_TEST 0
@@ -36,58 +37,9 @@
#define DPRINTF_STS \
DPRINTF("%s: %d: sts = 0x%08x\n", __func__, __LINE__, sts)
-#define I2C_SLAVE_ADDR 0x2e
-#define I2C_DEV_BUS_NUM 10
-
static const uint8_t TPM_CMD[12] =
"\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00";
-static uint32_t aspeed_bus_addr;
-
-static uint8_t cur_locty = 0xff;
-
-static void tpm_tis_i2c_set_locty(uint8_t locty)
-{
- if (cur_locty != locty) {
- cur_locty = locty;
- aspeed_i2c_writeb(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR,
- TPM_I2C_REG_LOC_SEL, locty);
- }
-}
-
-static uint8_t tpm_tis_i2c_readb(uint8_t locty, uint8_t reg)
-{
- tpm_tis_i2c_set_locty(locty);
- return aspeed_i2c_readb(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, reg);
-}
-
-static uint16_t tpm_tis_i2c_readw(uint8_t locty, uint8_t reg)
-{
- tpm_tis_i2c_set_locty(locty);
- return aspeed_i2c_readw(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, reg);
-}
-
-static uint32_t tpm_tis_i2c_readl(uint8_t locty, uint8_t reg)
-{
- tpm_tis_i2c_set_locty(locty);
- return aspeed_i2c_readl(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, reg);
-}
-
-static void tpm_tis_i2c_writeb(uint8_t locty, uint8_t reg, uint8_t v)
-{
- if (reg != TPM_I2C_REG_LOC_SEL) {
- tpm_tis_i2c_set_locty(locty);
- }
- aspeed_i2c_writeb(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, reg, v);
-}
-
-static void tpm_tis_i2c_writel(uint8_t locty, uint8_t reg, uint32_t v)
-{
- if (reg != TPM_I2C_REG_LOC_SEL) {
- tpm_tis_i2c_set_locty(locty);
- }
- aspeed_i2c_writel(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, reg, v);
-}
static void tpm_tis_i2c_test_basic(const void *data)
{
diff --git a/tests/qtest/tpm-tis-i2c-util.c b/tests/qtest/tpm-tis-i2c-util.c
new file mode 100644
index 0000000000..07b1eeba69
--- /dev/null
+++ b/tests/qtest/tpm-tis-i2c-util.c
@@ -0,0 +1,64 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * QTest utilities for TPM TIS over I2C
+ *
+ * Copyright (c) 2018, 2026 IBM Corporation
+ *
+ * Authors:
+ * Stefan Berger <stefanb@linux.ibm.com>
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "hw/acpi/tpm.h"
+#include "libqtest-single.h"
+#include "qtest_aspeed.h"
+#include "tpm-tis-i2c-util.h"
+
+uint32_t aspeed_bus_addr;
+
+static uint8_t cur_locty = 0xff;
+
+static void tpm_tis_i2c_set_locty(uint8_t locty)
+{
+ if (cur_locty != locty) {
+ cur_locty = locty;
+ aspeed_i2c_writeb(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR,
+ TPM_I2C_REG_LOC_SEL, locty);
+ }
+}
+
+uint8_t tpm_tis_i2c_readb(uint8_t locty, uint8_t reg)
+{
+ tpm_tis_i2c_set_locty(locty);
+ return aspeed_i2c_readb(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, reg);
+}
+
+uint16_t tpm_tis_i2c_readw(uint8_t locty, uint8_t reg)
+{
+ tpm_tis_i2c_set_locty(locty);
+ return aspeed_i2c_readw(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, reg);
+}
+
+uint32_t tpm_tis_i2c_readl(uint8_t locty, uint8_t reg)
+{
+ tpm_tis_i2c_set_locty(locty);
+ return aspeed_i2c_readl(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, reg);
+}
+
+void tpm_tis_i2c_writeb(uint8_t locty, uint8_t reg, uint8_t v)
+{
+ if (reg != TPM_I2C_REG_LOC_SEL) {
+ tpm_tis_i2c_set_locty(locty);
+ }
+ aspeed_i2c_writeb(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, reg, v);
+}
+
+void tpm_tis_i2c_writel(uint8_t locty, uint8_t reg, uint32_t v)
+{
+ if (reg != TPM_I2C_REG_LOC_SEL) {
+ tpm_tis_i2c_set_locty(locty);
+ }
+ aspeed_i2c_writel(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, reg, v);
+}
diff --git a/tests/qtest/tpm-tis-i2c-util.h b/tests/qtest/tpm-tis-i2c-util.h
new file mode 100644
index 0000000000..dfe626b43d
--- /dev/null
+++ b/tests/qtest/tpm-tis-i2c-util.h
@@ -0,0 +1,30 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * QTest TPM TIS I2C: Common test functions used for TPM I2C on Aspeed bus
+ *
+ * Copyright (c) 2026 IBM Corporation
+ *
+ * Authors:
+ * Stefan Berger <stefanb@linux.ibm.com>
+ *
+ */
+
+#ifndef TESTS_TPM_TIS_I2C_UTIL_H
+#define TESTS_TPM_TIS_I2C_UTIL_H
+
+#include "qemu/osdep.h"
+
+extern uint32_t aspeed_bus_addr;
+
+#define I2C_SLAVE_ADDR 0x2e
+#define I2C_DEV_BUS_NUM 10
+
+uint8_t tpm_tis_i2c_readb(uint8_t locty, uint8_t reg);
+uint16_t tpm_tis_i2c_readw(uint8_t locty, uint8_t reg);
+uint32_t tpm_tis_i2c_readl(uint8_t locty, uint8_t reg);
+
+void tpm_tis_i2c_writeb(uint8_t locty, uint8_t reg, uint8_t v);
+void tpm_tis_i2c_writel(uint8_t locty, uint8_t reg, uint32_t v);
+
+#endif /* TESTS_TPM_TIS_I2C_UTIL_H */
--
2.54.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PULL v1 02/16] tests: Have TPM I2C read/write functions take QTestState as first parameter
2026-06-01 21:53 [PULL v1 00/16] Merge tpm 2026/06/01 v1 Stefan Berger
2026-06-01 21:53 ` [PULL v1 01/16] tests: Move TPM I2C bus read/write functions to common files Stefan Berger
@ 2026-06-01 21:53 ` Stefan Berger
2026-06-01 21:53 ` [PULL v1 03/16] tests: Convert string arrays to byte arrays Stefan Berger
` (14 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2026-06-01 21:53 UTC (permalink / raw)
To: qemu-devel; +Cc: peter.maydell, Stefan Berger, Arun Menon
Pass the QTestState as first parameter to the TPM I2C functions. Use
global_qtest in existing test cases.
Reviewed-by: Arun Menon <armenon@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260429121743.1346635-3-stefanb@linux.ibm.com
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
tests/qtest/tpm-tis-i2c-test.c | 171 +++++++++++++++++----------------
tests/qtest/tpm-tis-i2c-util.c | 32 +++---
tests/qtest/tpm-tis-i2c-util.h | 10 +-
3 files changed, 110 insertions(+), 103 deletions(-)
diff --git a/tests/qtest/tpm-tis-i2c-test.c b/tests/qtest/tpm-tis-i2c-test.c
index 02ddf76c2c..f614f888f3 100644
--- a/tests/qtest/tpm-tis-i2c-test.c
+++ b/tests/qtest/tpm-tis-i2c-test.c
@@ -50,62 +50,64 @@ static void tpm_tis_i2c_test_basic(const void *data)
* All register accesses below must work without locality 0 being the
* active locality. Therefore, ensure access is released.
*/
- tpm_tis_i2c_writeb(0, TPM_I2C_REG_ACCESS,
+ tpm_tis_i2c_writeb(global_qtest, 0, TPM_I2C_REG_ACCESS,
TPM_TIS_ACCESS_ACTIVE_LOCALITY);
- access = tpm_tis_i2c_readb(0, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, 0, TPM_I2C_REG_ACCESS);
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
/* read interrupt capability -- none are supported */
- v = tpm_tis_i2c_readl(0, TPM_I2C_REG_INT_CAPABILITY);
+ v = tpm_tis_i2c_readl(global_qtest, 0, TPM_I2C_REG_INT_CAPABILITY);
g_assert_cmpint(v, ==, 0);
/* try to enable all interrupts */
- tpm_tis_i2c_writel(0, TPM_I2C_REG_INT_ENABLE, 0xffffffff);
- v = tpm_tis_i2c_readl(0, TPM_I2C_REG_INT_ENABLE);
+ tpm_tis_i2c_writel(global_qtest, 0, TPM_I2C_REG_INT_ENABLE, 0xffffffff);
+ v = tpm_tis_i2c_readl(global_qtest, 0, TPM_I2C_REG_INT_ENABLE);
/* none could be enabled */
g_assert_cmpint(v, ==, 0);
/* enable csum */
- tpm_tis_i2c_writeb(0, TPM_I2C_REG_DATA_CSUM_ENABLE, TPM_DATA_CSUM_ENABLED);
+ tpm_tis_i2c_writeb(global_qtest, 0, TPM_I2C_REG_DATA_CSUM_ENABLE,
+ TPM_DATA_CSUM_ENABLED);
/* check csum enable register has bit 0 set */
- v = tpm_tis_i2c_readb(0, TPM_I2C_REG_DATA_CSUM_ENABLE);
+ v = tpm_tis_i2c_readb(global_qtest, 0, TPM_I2C_REG_DATA_CSUM_ENABLE);
g_assert_cmpint(v, ==, TPM_DATA_CSUM_ENABLED);
/* reading it as 32bit register returns same result */
- v = tpm_tis_i2c_readl(0, TPM_I2C_REG_DATA_CSUM_ENABLE);
+ v = tpm_tis_i2c_readl(global_qtest, 0, TPM_I2C_REG_DATA_CSUM_ENABLE);
g_assert_cmpint(v, ==, TPM_DATA_CSUM_ENABLED);
/* disable csum */
- tpm_tis_i2c_writeb(0, TPM_I2C_REG_DATA_CSUM_ENABLE, 0);
+ tpm_tis_i2c_writeb(global_qtest, 0, TPM_I2C_REG_DATA_CSUM_ENABLE, 0);
/* check csum enable register has bit 0 clear */
- v = tpm_tis_i2c_readb(0, TPM_I2C_REG_DATA_CSUM_ENABLE);
+ v = tpm_tis_i2c_readb(global_qtest, 0, TPM_I2C_REG_DATA_CSUM_ENABLE);
g_assert_cmpint(v, ==, 0);
/* write to unsupported register '1' */
- tpm_tis_i2c_writel(0, 1, 0x12345678);
- v = tpm_tis_i2c_readl(0, 1);
+ tpm_tis_i2c_writel(global_qtest, 0, 1, 0x12345678);
+ v = tpm_tis_i2c_readl(global_qtest, 0, 1);
g_assert_cmpint(v, ==, 0xffffffff);
/* request use of locality */
- tpm_tis_i2c_writeb(0, TPM_I2C_REG_ACCESS, TPM_TIS_ACCESS_REQUEST_USE);
+ tpm_tis_i2c_writeb(global_qtest, 0, TPM_I2C_REG_ACCESS,
+ TPM_TIS_ACCESS_REQUEST_USE);
/* read byte from STS + 3 */
- v = tpm_tis_i2c_readb(0, TPM_I2C_REG_STS + 3);
+ v = tpm_tis_i2c_readb(global_qtest, 0, TPM_I2C_REG_STS + 3);
g_assert_cmpint(v, ==, 0);
/* check STS after writing to STS + 3 */
- v = tpm_tis_i2c_readl(0, TPM_I2C_REG_STS);
- tpm_tis_i2c_writeb(0, TPM_I2C_REG_STS + 3, 0xf);
- v2 = tpm_tis_i2c_readl(0, TPM_I2C_REG_STS);
+ v = tpm_tis_i2c_readl(global_qtest, 0, TPM_I2C_REG_STS);
+ tpm_tis_i2c_writeb(global_qtest, 0, TPM_I2C_REG_STS + 3, 0xf);
+ v2 = tpm_tis_i2c_readl(global_qtest, 0, TPM_I2C_REG_STS);
g_assert_cmpint(v, ==, v2);
/* release access */
- tpm_tis_i2c_writeb(0, TPM_I2C_REG_ACCESS,
+ tpm_tis_i2c_writeb(global_qtest, 0, TPM_I2C_REG_ACCESS,
TPM_TIS_ACCESS_ACTIVE_LOCALITY);
/* select locality 5 -- must not be possible */
- tpm_tis_i2c_writeb(0, TPM_I2C_REG_LOC_SEL, 5);
- v = tpm_tis_i2c_readb(0, TPM_I2C_REG_LOC_SEL);
+ tpm_tis_i2c_writeb(global_qtest, 0, TPM_I2C_REG_LOC_SEL, 5);
+ v = tpm_tis_i2c_readb(global_qtest, 0, TPM_I2C_REG_LOC_SEL);
g_assert_cmpint(v, ==, 0);
}
@@ -118,11 +120,12 @@ static void tpm_tis_i2c_test_check_localities(const void *data)
uint32_t rid;
for (locty = 0; locty < TPM_TIS_NUM_LOCALITIES; locty++) {
- access = tpm_tis_i2c_readb(locty, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, locty, TPM_I2C_REG_ACCESS);
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
- capability = tpm_tis_i2c_readl(locty, TPM_I2C_REG_INTF_CAPABILITY);
+ capability = tpm_tis_i2c_readl(global_qtest, locty,
+ TPM_I2C_REG_INTF_CAPABILITY);
i2c_cap = (TPM_I2C_CAP_INTERFACE_TYPE |
TPM_I2C_CAP_INTERFACE_VER |
TPM_I2C_CAP_TPM2_FAMILY |
@@ -131,15 +134,15 @@ static void tpm_tis_i2c_test_check_localities(const void *data)
TPM_I2C_CAP_DEV_ADDR_CHANGE);
g_assert_cmpint(capability, ==, i2c_cap);
- didvid = tpm_tis_i2c_readl(locty, TPM_I2C_REG_DID_VID);
+ didvid = tpm_tis_i2c_readl(global_qtest, locty, TPM_I2C_REG_DID_VID);
g_assert_cmpint(didvid, ==, (1 << 16) | PCI_VENDOR_ID_IBM);
- rid = tpm_tis_i2c_readl(locty, TPM_I2C_REG_RID);
+ rid = tpm_tis_i2c_readl(global_qtest, locty, TPM_I2C_REG_RID);
g_assert_cmpint(rid, !=, 0);
g_assert_cmpint(rid, !=, 0xffffffff);
/* locality selection must be at locty */
- l = tpm_tis_i2c_readb(locty, TPM_I2C_REG_LOC_SEL);
+ l = tpm_tis_i2c_readb(global_qtest, locty, TPM_I2C_REG_LOC_SEL);
g_assert_cmpint(l, ==, locty);
}
}
@@ -151,23 +154,23 @@ static void tpm_tis_i2c_test_check_access_reg(const void *data)
/* do not test locality 4 (hw only) */
for (locty = 0; locty < TPM_TIS_NUM_LOCALITIES - 1; locty++) {
- access = tpm_tis_i2c_readb(locty, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, locty, TPM_I2C_REG_ACCESS);
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
/* request use of locality */
- tpm_tis_i2c_writeb(locty, TPM_I2C_REG_ACCESS,
+ tpm_tis_i2c_writeb(global_qtest, locty, TPM_I2C_REG_ACCESS,
TPM_TIS_ACCESS_REQUEST_USE);
- access = tpm_tis_i2c_readb(locty, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, locty, TPM_I2C_REG_ACCESS);
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_ACTIVE_LOCALITY |
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
/* release access */
- tpm_tis_i2c_writeb(locty, TPM_I2C_REG_ACCESS,
+ tpm_tis_i2c_writeb(global_qtest, locty, TPM_I2C_REG_ACCESS,
TPM_TIS_ACCESS_ACTIVE_LOCALITY);
- access = tpm_tis_i2c_readb(locty, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, locty, TPM_I2C_REG_ACCESS);
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
}
@@ -186,14 +189,14 @@ static void tpm_tis_i2c_test_check_access_reg_seize(const void *data)
for (locty = 0; locty < TPM_TIS_NUM_LOCALITIES - 1; locty++) {
pending_request_flag = 0;
- access = tpm_tis_i2c_readb(locty, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, locty, TPM_I2C_REG_ACCESS);
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
/* request use of locality */
- tpm_tis_i2c_writeb(locty,
+ tpm_tis_i2c_writeb(global_qtest, locty,
TPM_I2C_REG_ACCESS, TPM_TIS_ACCESS_REQUEST_USE);
- access = tpm_tis_i2c_readb(locty, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, locty, TPM_I2C_REG_ACCESS);
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_ACTIVE_LOCALITY |
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
@@ -201,14 +204,14 @@ static void tpm_tis_i2c_test_check_access_reg_seize(const void *data)
/* lower localities cannot seize access */
for (l = 0; l < locty; l++) {
/* lower locality is not active */
- access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, l, TPM_I2C_REG_ACCESS);
DPRINTF_ACCESS;
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
pending_request_flag |
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
/* try to request use from 'l' */
- tpm_tis_i2c_writeb(l,
+ tpm_tis_i2c_writeb(global_qtest, l,
TPM_I2C_REG_ACCESS,
TPM_TIS_ACCESS_REQUEST_USE);
@@ -216,7 +219,7 @@ static void tpm_tis_i2c_test_check_access_reg_seize(const void *data)
* requesting use from 'l' was not possible;
* we must see REQUEST_USE and possibly PENDING_REQUEST
*/
- access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, l, TPM_I2C_REG_ACCESS);
DPRINTF_ACCESS;
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_REQUEST_USE |
@@ -227,17 +230,17 @@ static void tpm_tis_i2c_test_check_access_reg_seize(const void *data)
* locality 'locty' must be unchanged;
* we must see PENDING_REQUEST
*/
- access = tpm_tis_i2c_readb(locty, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, locty, TPM_I2C_REG_ACCESS);
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_ACTIVE_LOCALITY |
TPM_TIS_ACCESS_PENDING_REQUEST |
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
/* try to seize from 'l' */
- tpm_tis_i2c_writeb(l,
+ tpm_tis_i2c_writeb(global_qtest, l,
TPM_I2C_REG_ACCESS, TPM_TIS_ACCESS_SEIZE);
/* seize from 'l' was not possible */
- access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, l, TPM_I2C_REG_ACCESS);
DPRINTF_ACCESS;
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_REQUEST_USE |
@@ -245,7 +248,7 @@ static void tpm_tis_i2c_test_check_access_reg_seize(const void *data)
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
/* locality 'locty' must be unchanged */
- access = tpm_tis_i2c_readb(locty, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, locty, TPM_I2C_REG_ACCESS);
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_ACTIVE_LOCALITY |
TPM_TIS_ACCESS_PENDING_REQUEST |
@@ -264,14 +267,14 @@ static void tpm_tis_i2c_test_check_access_reg_seize(const void *data)
*/
for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES - 1; l++) {
/* try to 'request use' from 'l' */
- tpm_tis_i2c_writeb(l, TPM_I2C_REG_ACCESS,
+ tpm_tis_i2c_writeb(global_qtest, l, TPM_I2C_REG_ACCESS,
TPM_TIS_ACCESS_REQUEST_USE);
/*
* requesting use from 'l' was not possible; we should see
* REQUEST_USE and may see PENDING_REQUEST
*/
- access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, l, TPM_I2C_REG_ACCESS);
DPRINTF_ACCESS;
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_REQUEST_USE |
@@ -282,7 +285,7 @@ static void tpm_tis_i2c_test_check_access_reg_seize(const void *data)
* locality 'l-1' must be unchanged; we should always
* see PENDING_REQUEST from 'l' requesting access
*/
- access = tpm_tis_i2c_readb(l - 1, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, l - 1, TPM_I2C_REG_ACCESS);
DPRINTF_ACCESS;
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_ACTIVE_LOCALITY |
@@ -290,10 +293,11 @@ static void tpm_tis_i2c_test_check_access_reg_seize(const void *data)
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
/* try to seize from 'l' */
- tpm_tis_i2c_writeb(l, TPM_I2C_REG_ACCESS, TPM_TIS_ACCESS_SEIZE);
+ tpm_tis_i2c_writeb(global_qtest, l, TPM_I2C_REG_ACCESS,
+ TPM_TIS_ACCESS_SEIZE);
/* seize from 'l' was possible */
- access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, l, TPM_I2C_REG_ACCESS);
DPRINTF_ACCESS;
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_ACTIVE_LOCALITY |
@@ -301,7 +305,7 @@ static void tpm_tis_i2c_test_check_access_reg_seize(const void *data)
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
/* l - 1 should show that it has BEEN_SEIZED */
- access = tpm_tis_i2c_readb(l - 1, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, l - 1, TPM_I2C_REG_ACCESS);
DPRINTF_ACCESS;
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_BEEN_SEIZED |
@@ -309,10 +313,10 @@ static void tpm_tis_i2c_test_check_access_reg_seize(const void *data)
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
/* clear the BEEN_SEIZED flag and make sure it's gone */
- tpm_tis_i2c_writeb(l - 1, TPM_I2C_REG_ACCESS,
+ tpm_tis_i2c_writeb(global_qtest, l - 1, TPM_I2C_REG_ACCESS,
TPM_TIS_ACCESS_BEEN_SEIZED);
- access = tpm_tis_i2c_readb(l - 1, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, l - 1, TPM_I2C_REG_ACCESS);
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
pending_request_flag |
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
@@ -330,22 +334,22 @@ static void tpm_tis_i2c_test_check_access_reg_seize(const void *data)
/* release access from l - 1; this activates locty - 1 */
l--;
- access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, l, TPM_I2C_REG_ACCESS);
DPRINTF_ACCESS;
DPRINTF("%s: %d: relinquishing control on l = %d\n",
__func__, __LINE__, l);
- tpm_tis_i2c_writeb(l, TPM_I2C_REG_ACCESS,
+ tpm_tis_i2c_writeb(global_qtest, l, TPM_I2C_REG_ACCESS,
TPM_TIS_ACCESS_ACTIVE_LOCALITY);
- access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, l, TPM_I2C_REG_ACCESS);
DPRINTF_ACCESS;
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
pending_request_flag |
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
for (l = locty - 1; l >= 0; l--) {
- access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, l, TPM_I2C_REG_ACCESS);
DPRINTF_ACCESS;
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_ACTIVE_LOCALITY |
@@ -353,7 +357,7 @@ static void tpm_tis_i2c_test_check_access_reg_seize(const void *data)
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
/* release this locality */
- tpm_tis_i2c_writeb(l, TPM_I2C_REG_ACCESS,
+ tpm_tis_i2c_writeb(global_qtest, l, TPM_I2C_REG_ACCESS,
TPM_TIS_ACCESS_ACTIVE_LOCALITY);
if (l == 1) {
@@ -363,7 +367,7 @@ static void tpm_tis_i2c_test_check_access_reg_seize(const void *data)
/* no locality may be active now */
for (l = 0; l < TPM_TIS_NUM_LOCALITIES - 1; l++) {
- access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, l, TPM_I2C_REG_ACCESS);
DPRINTF_ACCESS;
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
@@ -384,14 +388,14 @@ static void tpm_tis_i2c_test_check_access_reg_release(const void *data)
for (locty = TPM_TIS_NUM_LOCALITIES - 2; locty >= 0; locty--) {
pending_request_flag = 0;
- access = tpm_tis_i2c_readb(locty, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, locty, TPM_I2C_REG_ACCESS);
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
/* request use of locality */
- tpm_tis_i2c_writeb(locty, TPM_I2C_REG_ACCESS,
+ tpm_tis_i2c_writeb(global_qtest, locty, TPM_I2C_REG_ACCESS,
TPM_TIS_ACCESS_REQUEST_USE);
- access = tpm_tis_i2c_readb(locty, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, locty, TPM_I2C_REG_ACCESS);
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_ACTIVE_LOCALITY |
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
@@ -405,9 +409,9 @@ static void tpm_tis_i2c_test_check_access_reg_release(const void *data)
* request use of locality 'l' -- we MUST see REQUEST USE and
* may see PENDING_REQUEST
*/
- tpm_tis_i2c_writeb(l, TPM_I2C_REG_ACCESS,
+ tpm_tis_i2c_writeb(global_qtest, l, TPM_I2C_REG_ACCESS,
TPM_TIS_ACCESS_REQUEST_USE);
- access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, l, TPM_I2C_REG_ACCESS);
DPRINTF_ACCESS;
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_REQUEST_USE |
@@ -416,7 +420,7 @@ static void tpm_tis_i2c_test_check_access_reg_release(const void *data)
pending_request_flag = TPM_TIS_ACCESS_PENDING_REQUEST;
}
/* release locality 'locty' */
- tpm_tis_i2c_writeb(locty, TPM_I2C_REG_ACCESS,
+ tpm_tis_i2c_writeb(global_qtest, locty, TPM_I2C_REG_ACCESS,
TPM_TIS_ACCESS_ACTIVE_LOCALITY);
/*
* highest locality should now be active; release it and make sure the
@@ -427,16 +431,16 @@ static void tpm_tis_i2c_test_check_access_reg_release(const void *data)
continue;
}
/* 'l' should be active now */
- access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, l, TPM_I2C_REG_ACCESS);
DPRINTF_ACCESS;
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_ACTIVE_LOCALITY |
pending_request_flag |
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
/* 'l' relinquishes access */
- tpm_tis_i2c_writeb(l, TPM_I2C_REG_ACCESS,
+ tpm_tis_i2c_writeb(global_qtest, l, TPM_I2C_REG_ACCESS,
TPM_TIS_ACCESS_ACTIVE_LOCALITY);
- access = tpm_tis_i2c_readb(l, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, l, TPM_I2C_REG_ACCESS);
DPRINTF_ACCESS;
if (l == 1 || (locty <= 1 && l == 2)) {
pending_request_flag = 0;
@@ -460,22 +464,24 @@ static void tpm_tis_i2c_test_check_transmit(const void *data)
size_t i;
/* enable csum */
- tpm_tis_i2c_writeb(0, TPM_I2C_REG_DATA_CSUM_ENABLE, TPM_DATA_CSUM_ENABLED);
+ tpm_tis_i2c_writeb(global_qtest, 0, TPM_I2C_REG_DATA_CSUM_ENABLE,
+ TPM_DATA_CSUM_ENABLED);
/* check csum enable register has bit 0 set */
- v = tpm_tis_i2c_readb(0, TPM_I2C_REG_DATA_CSUM_ENABLE);
+ v = tpm_tis_i2c_readb(global_qtest, 0, TPM_I2C_REG_DATA_CSUM_ENABLE);
g_assert_cmpint(v, ==, TPM_DATA_CSUM_ENABLED);
/* reading it as 32bit register returns same result */
- v = tpm_tis_i2c_readl(0, TPM_I2C_REG_DATA_CSUM_ENABLE);
+ v = tpm_tis_i2c_readl(global_qtest, 0, TPM_I2C_REG_DATA_CSUM_ENABLE);
g_assert_cmpint(v, ==, TPM_DATA_CSUM_ENABLED);
/* request use of locality 0 */
- tpm_tis_i2c_writeb(0, TPM_I2C_REG_ACCESS, TPM_TIS_ACCESS_REQUEST_USE);
- access = tpm_tis_i2c_readb(0, TPM_I2C_REG_ACCESS);
+ tpm_tis_i2c_writeb(global_qtest, 0, TPM_I2C_REG_ACCESS,
+ TPM_TIS_ACCESS_REQUEST_USE);
+ access = tpm_tis_i2c_readb(global_qtest, 0, TPM_I2C_REG_ACCESS);
g_assert_cmpint(access, ==, TPM_TIS_ACCESS_TPM_REG_VALID_STS |
TPM_TIS_ACCESS_ACTIVE_LOCALITY |
TPM_TIS_ACCESS_TPM_ESTABLISHMENT);
- sts = tpm_tis_i2c_readl(0, TPM_I2C_REG_STS);
+ sts = tpm_tis_i2c_readl(global_qtest, 0, TPM_I2C_REG_STS);
DPRINTF_STS;
g_assert_cmpint(sts & 0xff, ==, 0);
@@ -484,21 +490,22 @@ static void tpm_tis_i2c_test_check_transmit(const void *data)
g_assert_cmpint(bcount, >=, 128);
/* read bcount from STS + 1 must work also */
- bcount2 = tpm_tis_i2c_readw(0, TPM_I2C_REG_STS + 1);
+ bcount2 = tpm_tis_i2c_readw(global_qtest, 0, TPM_I2C_REG_STS + 1);
g_assert_cmpint(bcount, ==, bcount2);
/* ic2 must have bits 26-31 zero */
g_assert_cmpint(sts & (0x1f << 26), ==, 0);
- tpm_tis_i2c_writel(0, TPM_I2C_REG_STS, TPM_TIS_STS_COMMAND_READY);
- sts = tpm_tis_i2c_readl(0, TPM_I2C_REG_STS);
+ tpm_tis_i2c_writel(global_qtest, 0, TPM_I2C_REG_STS,
+ TPM_TIS_STS_COMMAND_READY);
+ sts = tpm_tis_i2c_readl(global_qtest, 0, TPM_I2C_REG_STS);
DPRINTF_STS;
g_assert_cmpint(sts & 0xff, ==, TPM_TIS_STS_COMMAND_READY);
/* transmit command */
for (i = 0; i < sizeof(TPM_CMD); i++) {
- tpm_tis_i2c_writeb(0, TPM_I2C_REG_DATA_FIFO, TPM_CMD[i]);
- sts = tpm_tis_i2c_readl(0, TPM_I2C_REG_STS);
+ tpm_tis_i2c_writeb(global_qtest, 0, TPM_I2C_REG_DATA_FIFO, TPM_CMD[i]);
+ sts = tpm_tis_i2c_readl(global_qtest, 0, TPM_I2C_REG_STS);
DPRINTF_STS;
if (i < sizeof(TPM_CMD) - 1) {
g_assert_cmpint(sts & 0xff, ==,
@@ -509,21 +516,21 @@ static void tpm_tis_i2c_test_check_transmit(const void *data)
g_assert_cmpint((sts >> 8) & 0xffff, ==, --bcount);
}
/* read the checksum */
- csum = tpm_tis_i2c_readw(0, TPM_I2C_REG_DATA_CSUM_GET);
+ csum = tpm_tis_i2c_readw(global_qtest, 0, TPM_I2C_REG_DATA_CSUM_GET);
g_assert_cmpint(csum, ==, 0x6733);
/* start processing */
- tpm_tis_i2c_writeb(0, TPM_I2C_REG_STS, TPM_TIS_STS_TPM_GO);
+ tpm_tis_i2c_writeb(global_qtest, 0, TPM_I2C_REG_STS, TPM_TIS_STS_TPM_GO);
uint64_t end_time = g_get_monotonic_time() + 50 * G_TIME_SPAN_SECOND;
do {
- sts = tpm_tis_i2c_readl(0, TPM_I2C_REG_STS);
+ sts = tpm_tis_i2c_readl(global_qtest, 0, TPM_I2C_REG_STS);
if ((sts & TPM_TIS_STS_DATA_AVAILABLE) != 0) {
break;
}
} while (g_get_monotonic_time() < end_time);
- sts = tpm_tis_i2c_readl(0, TPM_I2C_REG_STS);
+ sts = tpm_tis_i2c_readl(global_qtest, 0, TPM_I2C_REG_STS);
DPRINTF_STS;
g_assert_cmpint(sts & 0xff, == ,
TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE);
@@ -534,8 +541,8 @@ static void tpm_tis_i2c_test_check_transmit(const void *data)
g_assert_cmpint(sizeof(tpm_msg), ==, bcount);
for (i = 0; i < sizeof(tpm_msg); i++) {
- tpm_msg[i] = tpm_tis_i2c_readb(0, TPM_I2C_REG_DATA_FIFO);
- sts = tpm_tis_i2c_readl(0, TPM_I2C_REG_STS);
+ tpm_msg[i] = tpm_tis_i2c_readb(global_qtest, 0, TPM_I2C_REG_DATA_FIFO);
+ sts = tpm_tis_i2c_readl(global_qtest, 0, TPM_I2C_REG_STS);
DPRINTF_STS;
if (sts & TPM_TIS_STS_DATA_AVAILABLE) {
g_assert_cmpint((sts >> 8) & 0xffff, ==, --bcount);
@@ -544,9 +551,9 @@ static void tpm_tis_i2c_test_check_transmit(const void *data)
g_assert_cmpmem(tpm_msg, sizeof(tpm_msg), s->tpm_msg, sizeof(*s->tpm_msg));
/* relinquish use of locality 0 */
- tpm_tis_i2c_writeb(0,
+ tpm_tis_i2c_writeb(global_qtest, 0,
TPM_I2C_REG_ACCESS, TPM_TIS_ACCESS_ACTIVE_LOCALITY);
- access = tpm_tis_i2c_readb(0, TPM_I2C_REG_ACCESS);
+ access = tpm_tis_i2c_readb(global_qtest, 0, TPM_I2C_REG_ACCESS);
}
int main(int argc, char **argv)
diff --git a/tests/qtest/tpm-tis-i2c-util.c b/tests/qtest/tpm-tis-i2c-util.c
index 07b1eeba69..6e724a4a47 100644
--- a/tests/qtest/tpm-tis-i2c-util.c
+++ b/tests/qtest/tpm-tis-i2c-util.c
@@ -20,45 +20,45 @@ uint32_t aspeed_bus_addr;
static uint8_t cur_locty = 0xff;
-static void tpm_tis_i2c_set_locty(uint8_t locty)
+static void tpm_tis_i2c_set_locty(QTestState *s, uint8_t locty)
{
if (cur_locty != locty) {
cur_locty = locty;
- aspeed_i2c_writeb(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR,
+ aspeed_i2c_writeb(s, aspeed_bus_addr, I2C_SLAVE_ADDR,
TPM_I2C_REG_LOC_SEL, locty);
}
}
-uint8_t tpm_tis_i2c_readb(uint8_t locty, uint8_t reg)
+uint8_t tpm_tis_i2c_readb(QTestState *s, uint8_t locty, uint8_t reg)
{
- tpm_tis_i2c_set_locty(locty);
- return aspeed_i2c_readb(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, reg);
+ tpm_tis_i2c_set_locty(s, locty);
+ return aspeed_i2c_readb(s, aspeed_bus_addr, I2C_SLAVE_ADDR, reg);
}
-uint16_t tpm_tis_i2c_readw(uint8_t locty, uint8_t reg)
+uint16_t tpm_tis_i2c_readw(QTestState *s, uint8_t locty, uint8_t reg)
{
- tpm_tis_i2c_set_locty(locty);
+ tpm_tis_i2c_set_locty(s, locty);
return aspeed_i2c_readw(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, reg);
}
-uint32_t tpm_tis_i2c_readl(uint8_t locty, uint8_t reg)
+uint32_t tpm_tis_i2c_readl(QTestState *s, uint8_t locty, uint8_t reg)
{
- tpm_tis_i2c_set_locty(locty);
- return aspeed_i2c_readl(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, reg);
+ tpm_tis_i2c_set_locty(s, locty);
+ return aspeed_i2c_readl(s, aspeed_bus_addr, I2C_SLAVE_ADDR, reg);
}
-void tpm_tis_i2c_writeb(uint8_t locty, uint8_t reg, uint8_t v)
+void tpm_tis_i2c_writeb(QTestState *s, uint8_t locty, uint8_t reg, uint8_t v)
{
if (reg != TPM_I2C_REG_LOC_SEL) {
- tpm_tis_i2c_set_locty(locty);
+ tpm_tis_i2c_set_locty(s, locty);
}
- aspeed_i2c_writeb(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, reg, v);
+ aspeed_i2c_writeb(s, aspeed_bus_addr, I2C_SLAVE_ADDR, reg, v);
}
-void tpm_tis_i2c_writel(uint8_t locty, uint8_t reg, uint32_t v)
+void tpm_tis_i2c_writel(QTestState *s, uint8_t locty, uint8_t reg, uint32_t v)
{
if (reg != TPM_I2C_REG_LOC_SEL) {
- tpm_tis_i2c_set_locty(locty);
+ tpm_tis_i2c_set_locty(s, locty);
}
- aspeed_i2c_writel(global_qtest, aspeed_bus_addr, I2C_SLAVE_ADDR, reg, v);
+ aspeed_i2c_writel(s, aspeed_bus_addr, I2C_SLAVE_ADDR, reg, v);
}
diff --git a/tests/qtest/tpm-tis-i2c-util.h b/tests/qtest/tpm-tis-i2c-util.h
index dfe626b43d..3289545f61 100644
--- a/tests/qtest/tpm-tis-i2c-util.h
+++ b/tests/qtest/tpm-tis-i2c-util.h
@@ -20,11 +20,11 @@ extern uint32_t aspeed_bus_addr;
#define I2C_SLAVE_ADDR 0x2e
#define I2C_DEV_BUS_NUM 10
-uint8_t tpm_tis_i2c_readb(uint8_t locty, uint8_t reg);
-uint16_t tpm_tis_i2c_readw(uint8_t locty, uint8_t reg);
-uint32_t tpm_tis_i2c_readl(uint8_t locty, uint8_t reg);
+uint8_t tpm_tis_i2c_readb(QTestState *s, uint8_t locty, uint8_t reg);
+uint16_t tpm_tis_i2c_readw(QTestState *s, uint8_t locty, uint8_t reg);
+uint32_t tpm_tis_i2c_readl(QTestState *s, uint8_t locty, uint8_t reg);
-void tpm_tis_i2c_writeb(uint8_t locty, uint8_t reg, uint8_t v);
-void tpm_tis_i2c_writel(uint8_t locty, uint8_t reg, uint32_t v);
+void tpm_tis_i2c_writeb(QTestState *s, uint8_t locty, uint8_t reg, uint8_t v);
+void tpm_tis_i2c_writel(QTestState *s, uint8_t locty, uint8_t reg, uint32_t v);
#endif /* TESTS_TPM_TIS_I2C_UTIL_H */
--
2.54.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PULL v1 03/16] tests: Convert string arrays to byte arrays
2026-06-01 21:53 [PULL v1 00/16] Merge tpm 2026/06/01 v1 Stefan Berger
2026-06-01 21:53 ` [PULL v1 01/16] tests: Move TPM I2C bus read/write functions to common files Stefan Berger
2026-06-01 21:53 ` [PULL v1 02/16] tests: Have TPM I2C read/write functions take QTestState as first parameter Stefan Berger
@ 2026-06-01 21:53 ` Stefan Berger
2026-06-01 21:53 ` [PULL v1 04/16] tests: Rename id of tpmdev to tpm0 Stefan Berger
` (13 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2026-06-01 21:53 UTC (permalink / raw)
To: qemu-devel; +Cc: peter.maydell, Stefan Berger, Arun Menon
Convert the TPM command and response string arrays to byte arrays.
Reviewed-by: Arun Menon <armenon@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260429121743.1346635-4-stefanb@linux.ibm.com
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
tests/qtest/tpm-tests.c | 30 ++++++++++++++++++---------
tests/qtest/tpm-util.c | 45 ++++++++++++++++++++++++++---------------
2 files changed, 49 insertions(+), 26 deletions(-)
diff --git a/tests/qtest/tpm-tests.c b/tests/qtest/tpm-tests.c
index 197714f8d9..8bd3288f80 100644
--- a/tests/qtest/tpm-tests.c
+++ b/tests/qtest/tpm-tests.c
@@ -59,11 +59,16 @@ void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx,
tpm_util_startup(s, tx);
tpm_util_pcrextend(s, tx);
- static const unsigned char tpm_pcrread_resp[] =
- "\x80\x01\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00"
- "\x00\x01\x00\x0b\x03\x00\x04\x00\x00\x00\x00\x01\x00\x20\xf6\x85"
- "\x98\xe5\x86\x8d\xe6\x8b\x97\x29\x99\x60\xf2\x71\x7d\x17\x67\x89"
- "\xa4\x2f\x9a\xae\xa8\xc7\xb7\xaa\x79\xa8\x62\x56\xc1\xde";
+ static const unsigned char tpm_pcrread_resp[] = {
+ 0x80, 0x01, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x0b, 0x03, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x20, 0xf6, 0x85,
+ 0x98, 0xe5, 0x86, 0x8d, 0xe6, 0x8b, 0x97, 0x29,
+ 0x99, 0x60, 0xf2, 0x71, 0x7d, 0x17, 0x67, 0x89,
+ 0xa4, 0x2f, 0x9a, 0xae, 0xa8, 0xc7, 0xb7, 0xaa,
+ 0x79, 0xa8, 0x62, 0x56, 0xc1, 0xde
+ };
tpm_util_pcrread(s, tx, tpm_pcrread_resp,
sizeof(tpm_pcrread_resp));
@@ -105,11 +110,16 @@ void tpm_test_swtpm_migration_test(const char *src_tpm_path,
tpm_util_startup(src_qemu, tx);
tpm_util_pcrextend(src_qemu, tx);
- static const unsigned char tpm_pcrread_resp[] =
- "\x80\x01\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00"
- "\x00\x01\x00\x0b\x03\x00\x04\x00\x00\x00\x00\x01\x00\x20\xf6\x85"
- "\x98\xe5\x86\x8d\xe6\x8b\x97\x29\x99\x60\xf2\x71\x7d\x17\x67\x89"
- "\xa4\x2f\x9a\xae\xa8\xc7\xb7\xaa\x79\xa8\x62\x56\xc1\xde";
+ static const unsigned char tpm_pcrread_resp[] = {
+ 0x80, 0x01, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x0b, 0x03, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x20, 0xf6, 0x85,
+ 0x98, 0xe5, 0x86, 0x8d, 0xe6, 0x8b, 0x97, 0x29,
+ 0x99, 0x60, 0xf2, 0x71, 0x7d, 0x17, 0x67, 0x89,
+ 0xa4, 0x2f, 0x9a, 0xae, 0xa8, 0xc7, 0xb7, 0xaa,
+ 0x79, 0xa8, 0x62, 0x56, 0xc1, 0xde,
+ };
tpm_util_pcrread(src_qemu, tx, tpm_pcrread_resp,
sizeof(tpm_pcrread_resp));
diff --git a/tests/qtest/tpm-util.c b/tests/qtest/tpm-util.c
index 2cb2dd4796..63cd5b69db 100644
--- a/tests/qtest/tpm-util.c
+++ b/tests/qtest/tpm-util.c
@@ -54,10 +54,14 @@ void tpm_util_crb_transfer(QTestState *s,
void tpm_util_startup(QTestState *s, tx_func *tx)
{
unsigned char buffer[1024];
- static const unsigned char tpm_startup[] =
- "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00";
- static const unsigned char tpm_startup_resp[] =
- "\x80\x01\x00\x00\x00\x0a\x00\x00\x00\x00";
+ static const unsigned char tpm_startup[] = {
+ 0x80, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00,
+ 0x01, 0x44, 0x00, 0x00
+ };
+ static const unsigned char tpm_startup_resp[] = {
+ 0x80, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00,
+ 0x00, 0x00
+ };
tx(s, tpm_startup, sizeof(tpm_startup), buffer, sizeof(buffer));
@@ -68,16 +72,23 @@ void tpm_util_startup(QTestState *s, tx_func *tx)
void tpm_util_pcrextend(QTestState *s, tx_func *tx)
{
unsigned char buffer[1024];
- static const unsigned char tpm_pcrextend[] =
- "\x80\x02\x00\x00\x00\x41\x00\x00\x01\x82\x00\x00\x00\x0a\x00\x00"
- "\x00\x09\x40\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00"
- "\x0b\x74\x65\x73\x74\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x00";
+ static const unsigned char tpm_pcrextend[] = {
+ 0x80, 0x02, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00,
+ 0x01, 0x82, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00,
+ 0x00, 0x09, 0x40, 0x00, 0x00, 0x09, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x0b, 0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00
+ };
- static const unsigned char tpm_pcrextend_resp[] =
- "\x80\x02\x00\x00\x00\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x01\x00\x00";
+ static const unsigned char tpm_pcrextend_resp[] = {
+ 0x80, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00
+ };
tx(s, tpm_pcrextend, sizeof(tpm_pcrextend), buffer, sizeof(buffer));
@@ -89,9 +100,11 @@ void tpm_util_pcrread(QTestState *s, tx_func *tx,
const unsigned char *exp_resp, size_t exp_resp_size)
{
unsigned char buffer[1024];
- static const unsigned char tpm_pcrread[] =
- "\x80\x01\x00\x00\x00\x14\x00\x00\x01\x7e\x00\x00\x00\x01\x00\x0b"
- "\x03\x00\x04\x00";
+ static const unsigned char tpm_pcrread[] = {
+ 0x80, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00,
+ 0x01, 0x7e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b,
+ 0x03, 0x00, 0x04, 0x00
+ };
tx(s, tpm_pcrread, sizeof(tpm_pcrread), buffer, sizeof(buffer));
--
2.54.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PULL v1 04/16] tests: Rename id of tpmdev to tpm0
2026-06-01 21:53 [PULL v1 00/16] Merge tpm 2026/06/01 v1 Stefan Berger
` (2 preceding siblings ...)
2026-06-01 21:53 ` [PULL v1 03/16] tests: Convert string arrays to byte arrays Stefan Berger
@ 2026-06-01 21:53 ` Stefan Berger
2026-06-01 21:53 ` [PULL v1 05/16] tests: Check whether the I2C master flag is set Stefan Berger
` (12 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2026-06-01 21:53 UTC (permalink / raw)
To: qemu-devel; +Cc: peter.maydell, Stefan Berger, Arun Menon
Rename the id of the tpmdev from dev to tpm0 because this 'dev' cannot
be used when the tpm-tis-i2c device is used.
Reviewed-by: Arun Menon <armenon@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260429121743.1346635-5-stefanb@linux.ibm.com
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
tests/qtest/tpm-tests.c | 4 ++--
tests/qtest/tpm-util.c | 8 ++++----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/tests/qtest/tpm-tests.c b/tests/qtest/tpm-tests.c
index 8bd3288f80..1b4f932b0c 100644
--- a/tests/qtest/tpm-tests.c
+++ b/tests/qtest/tpm-tests.c
@@ -49,8 +49,8 @@ void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx,
args = g_strdup_printf(
"%s "
"-chardev socket,id=chr,path=%s "
- "-tpmdev emulator,id=dev,chardev=chr "
- "-device %s,tpmdev=dev",
+ "-tpmdev emulator,id=tpm0,chardev=chr "
+ "-device %s,tpmdev=tpm0",
machine_options ? : "", addr->u.q_unix.path, ifmodel);
s = qtest_start(args);
diff --git a/tests/qtest/tpm-util.c b/tests/qtest/tpm-util.c
index 63cd5b69db..0f9bef8971 100644
--- a/tests/qtest/tpm-util.c
+++ b/tests/qtest/tpm-util.c
@@ -240,8 +240,8 @@ void tpm_util_migration_start_qemu(QTestState **src_qemu,
src_qemu_args = g_strdup_printf(
"%s "
"-chardev socket,id=chr,path=%s "
- "-tpmdev emulator,id=dev,chardev=chr "
- "-device %s,tpmdev=dev ",
+ "-tpmdev emulator,id=tpm0,chardev=chr "
+ "-device %s,tpmdev=tpm0 ",
machine_options ? : "", src_tpm_addr->u.q_unix.path, ifmodel);
*src_qemu = qtest_init(src_qemu_args);
@@ -249,8 +249,8 @@ void tpm_util_migration_start_qemu(QTestState **src_qemu,
dst_qemu_args = g_strdup_printf(
"%s "
"-chardev socket,id=chr,path=%s "
- "-tpmdev emulator,id=dev,chardev=chr "
- "-device %s,tpmdev=dev "
+ "-tpmdev emulator,id=tpm0,chardev=chr "
+ "-device %s,tpmdev=tpm0 "
"-incoming %s",
machine_options ? : "",
dst_tpm_addr->u.q_unix.path,
--
2.54.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PULL v1 05/16] tests: Check whether the I2C master flag is set
2026-06-01 21:53 [PULL v1 00/16] Merge tpm 2026/06/01 v1 Stefan Berger
` (3 preceding siblings ...)
2026-06-01 21:53 ` [PULL v1 04/16] tests: Rename id of tpmdev to tpm0 Stefan Berger
@ 2026-06-01 21:53 ` Stefan Berger
2026-06-01 21:53 ` [PULL v1 06/16] tests: Add a TPM TIS I2C swtpm test Stefan Berger
` (11 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2026-06-01 21:53 UTC (permalink / raw)
To: qemu-devel; +Cc: peter.maydell, Stefan Berger, Arun Menon
Replace the 'once' variable with a check for whether the master flag is
set so that the flag can be set when needed.
Reviewed-by: Arun Menon <armenon@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260429121743.1346635-6-stefanb@linux.ibm.com
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
tests/qtest/qtest_aspeed.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/tests/qtest/qtest_aspeed.c b/tests/qtest/qtest_aspeed.c
index f6da9adea9..7bf5abb9be 100644
--- a/tests/qtest/qtest_aspeed.c
+++ b/tests/qtest/qtest_aspeed.c
@@ -15,18 +15,21 @@
#include "qtest_aspeed.h"
#include "hw/i2c/aspeed_i2c.h"
+static bool aspeed_i2c_is_master_enabled(QTestState *s, uint32_t baseaddr)
+{
+ return qtest_readl(s, baseaddr + A_I2CC_FUN_CTRL) & A_I2CD_MASTER_EN;
+}
+
static void aspeed_i2c_startup(QTestState *s, uint32_t baseaddr,
uint8_t slave_addr, uint8_t reg)
{
uint32_t v;
- static int once;
- if (!once) {
+ if (!aspeed_i2c_is_master_enabled(s, baseaddr)) {
/* one time: enable master */
qtest_writel(s, baseaddr + A_I2CC_FUN_CTRL, 0);
v = qtest_readl(s, baseaddr + A_I2CC_FUN_CTRL) | A_I2CD_MASTER_EN;
qtest_writel(s, baseaddr + A_I2CC_FUN_CTRL, v);
- once = 1;
}
/* select device */
--
2.54.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PULL v1 06/16] tests: Add a TPM TIS I2C swtpm test
2026-06-01 21:53 [PULL v1 00/16] Merge tpm 2026/06/01 v1 Stefan Berger
` (4 preceding siblings ...)
2026-06-01 21:53 ` [PULL v1 05/16] tests: Check whether the I2C master flag is set Stefan Berger
@ 2026-06-01 21:53 ` Stefan Berger
2026-06-01 21:54 ` [PULL v1 07/16] migration/vmstate: Add VMState support for GByteArray Stefan Berger
` (10 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2026-06-01 21:53 UTC (permalink / raw)
To: qemu-devel; +Cc: peter.maydell, Stefan Berger, Arun Menon
Add a test case testing the TPM TIS over I2C with swtpm.
Reviewed-by: Arun Menon <armenon@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260429121743.1346635-7-stefanb@linux.ibm.com
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
tests/qtest/meson.build | 5 +-
tests/qtest/tpm-tis-i2c-swtpm-test.c | 82 ++++++++++++++++++++++++++++
tests/qtest/tpm-tis-i2c-util.c | 38 +++++++++++++
tests/qtest/tpm-tis-i2c-util.h | 4 ++
4 files changed, 127 insertions(+), 2 deletions(-)
create mode 100644 tests/qtest/tpm-tis-i2c-swtpm-test.c
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 57512c6e56..45ea497fa5 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -244,7 +244,7 @@ qtests_arm = \
(config_all_devices.has_key('CONFIG_ASPEED_SOC') ? qtests_aspeed : []) + \
(config_all_devices.has_key('CONFIG_NPCM7XX') ? qtests_npcm7xx : []) + \
(config_all_devices.has_key('CONFIG_GENERIC_LOADER') ? ['hexloader-test'] : []) + \
- (config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test', 'tpm-tis-i2c-swtpm-test'] : []) + \
(config_all_devices.has_key('CONFIG_VEXPRESS') ? ['test-arm-mptimer'] : []) + \
(config_all_devices.has_key('CONFIG_MICROBIT') ? ['microbit-test'] : []) + \
(config_all_devices.has_key('CONFIG_STM32L4X5_SOC') ? qtests_stm32l4x5 : []) + \
@@ -263,7 +263,7 @@ qtests_aarch64 = \
(config_all_devices.has_key('CONFIG_XLNX_VERSAL') ? ['xlnx-canfd-test', 'xlnx-versal-trng-test'] : []) + \
(config_all_devices.has_key('CONFIG_RASPI') ? ['bcm2835-dma-test', 'bcm2835-i2c-test'] : []) + \
(config_all_accel.has_key('CONFIG_TCG') and \
- config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : []) + \
+ config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test', 'tpm-tis-i2c-swtpm-test'] : []) + \
(config_all_devices.has_key('CONFIG_ASPEED_SOC') ? qtests_aspeed64 : []) + \
(config_all_devices.has_key('CONFIG_NPCM8XX') ? qtests_npcm8xx : []) + \
(config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') and
@@ -402,6 +402,7 @@ qtests = {
'tpm-tis-swtpm-test': [io, tpmemu_files, 'tpm-tis-util.c'],
'tpm-tis-test': [io, tpmemu_files, 'tpm-tis-util.c'],
'tpm-tis-i2c-test': [io, tpmemu_files, 'tpm-tis-i2c-util.c', 'qtest_aspeed.c'],
+ 'tpm-tis-i2c-swtpm-test': [io, tpmemu_files, 'tpm-tis-i2c-util.c', 'qtest_aspeed.c'],
'tpm-tis-device-swtpm-test': [io, tpmemu_files, 'tpm-tis-util.c'],
'tpm-tis-device-test': [io, tpmemu_files, 'tpm-tis-util.c'],
'virtio-net-failover': test_migration_files,
diff --git a/tests/qtest/tpm-tis-i2c-swtpm-test.c b/tests/qtest/tpm-tis-i2c-swtpm-test.c
new file mode 100644
index 0000000000..50d32a960b
--- /dev/null
+++ b/tests/qtest/tpm-tis-i2c-swtpm-test.c
@@ -0,0 +1,82 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * QTest testcase for TPM TIS over I2C talking to external swtpm
+ *
+ * Copyright (c) 2018, 2026 IBM Corporation
+ * with parts borrowed from migration-test.c that is:
+ * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
+ *
+ * Authors:
+ * Stefan Berger <stefanb@linux.ibm.com>
+ *
+ */
+
+#include "qemu/osdep.h"
+
+#include "libqtest.h"
+#include "qemu/module.h"
+#include "tpm-tests.h"
+#include "tpm-tis-i2c-util.h"
+#include "qtest_aspeed.h"
+
+typedef struct TestState {
+ char *src_tpm_path;
+ char *dst_tpm_path;
+ char *uri;
+ const char *machine_options;
+ char *ifmodel;
+} TestState;
+
+static void tpm_tis_i2c_swtpm_test(const void *data)
+{
+ const TestState *ts = data;
+
+ tpm_test_swtpm_test(ts->src_tpm_path, tpm_tis_i2c_transfer,
+ ts->ifmodel, ts->machine_options);
+}
+
+static void tpm_tis_swtpm_migration_test(const void *data)
+{
+ const TestState *ts = data;
+
+ tpm_test_swtpm_migration_test(ts->src_tpm_path, ts->dst_tpm_path,
+ ts->uri, tpm_tis_i2c_transfer,
+ ts->ifmodel, ts->machine_options);
+}
+
+
+int main(int argc, char **argv)
+{
+ int ret;
+ TestState ts;
+
+ ts.src_tpm_path = g_dir_make_tmp("qemu-tpm-tis-i2c-swtpm-test.XXXXXX",
+ NULL);
+ ts.dst_tpm_path = g_dir_make_tmp("qemu-tpm-tis-i2c-swtpm-test.XXXXXX",
+ NULL);
+ ts.uri = g_strdup_printf("unix:%s/migsocket", ts.src_tpm_path);
+ ts.machine_options = "-machine rainier-bmc -accel tcg";
+ ts.ifmodel = g_strdup_printf(
+ "tpm-tis-i2c,bus=aspeed.i2c.bus.%d,address=0x%x",
+ I2C_DEV_BUS_NUM, I2C_SLAVE_ADDR);
+
+ module_call_init(MODULE_INIT_QOM);
+ g_test_init(&argc, &argv, NULL);
+
+ aspeed_bus_addr = ast2600_i2c_calc_bus_addr(I2C_DEV_BUS_NUM);
+
+ qtest_add_data_func("/tpm/tis-i2c-swtpm/test", &ts, tpm_tis_i2c_swtpm_test);
+ qtest_add_data_func("/tpm/tis-i2c-swtpm-migration/test", &ts,
+ tpm_tis_swtpm_migration_test);
+ ret = g_test_run();
+
+ tpm_util_rmdir(ts.dst_tpm_path);
+ g_free(ts.dst_tpm_path);
+ tpm_util_rmdir(ts.src_tpm_path);
+ g_free(ts.src_tpm_path);
+ g_free(ts.uri);
+ g_free(ts.ifmodel);
+
+ return ret;
+}
diff --git a/tests/qtest/tpm-tis-i2c-util.c b/tests/qtest/tpm-tis-i2c-util.c
index 6e724a4a47..c73420ec5b 100644
--- a/tests/qtest/tpm-tis-i2c-util.c
+++ b/tests/qtest/tpm-tis-i2c-util.c
@@ -15,6 +15,7 @@
#include "libqtest-single.h"
#include "qtest_aspeed.h"
#include "tpm-tis-i2c-util.h"
+#include "tpm-emu.h"
uint32_t aspeed_bus_addr;
@@ -62,3 +63,40 @@ void tpm_tis_i2c_writel(QTestState *s, uint8_t locty, uint8_t reg, uint32_t v)
}
aspeed_i2c_writel(s, aspeed_bus_addr, I2C_SLAVE_ADDR, reg, v);
}
+
+void tpm_tis_i2c_transfer(QTestState *s,
+ const unsigned char *req, size_t req_size,
+ unsigned char *rsp, size_t rsp_size)
+{
+ uint32_t sts;
+ size_t i;
+
+ /* request use of locality 0 */
+ tpm_tis_i2c_writeb(s, 0, TPM_I2C_REG_ACCESS, TPM_TIS_ACCESS_REQUEST_USE);
+
+ tpm_tis_i2c_writel(s, 0, TPM_I2C_REG_STS, TPM_TIS_STS_COMMAND_READY);
+
+ /* transmit command */
+ for (i = 0; i < req_size; i++) {
+ tpm_tis_i2c_writeb(s, 0, TPM_I2C_REG_DATA_FIFO, req[i]);
+ }
+
+ /* start processing */
+ tpm_tis_i2c_writeb(s, 0, TPM_I2C_REG_STS, TPM_TIS_STS_TPM_GO);
+
+ uint64_t end_time = g_get_monotonic_time() + 50 * G_TIME_SPAN_SECOND;
+ do {
+ sts = tpm_tis_i2c_readl(s, 0, TPM_I2C_REG_STS);
+ if ((sts & TPM_TIS_STS_DATA_AVAILABLE) != 0) {
+ break;
+ }
+ } while (g_get_monotonic_time() < end_time);
+
+ /* read response */
+ for (i = 0; i < rsp_size; i++) {
+ rsp[i] = tpm_tis_i2c_readb(s, 0, TPM_I2C_REG_DATA_FIFO);
+ }
+ /* relinquish use of locality 0 */
+ tpm_tis_i2c_writeb(s, 0,
+ TPM_I2C_REG_ACCESS, TPM_TIS_ACCESS_ACTIVE_LOCALITY);
+}
diff --git a/tests/qtest/tpm-tis-i2c-util.h b/tests/qtest/tpm-tis-i2c-util.h
index 3289545f61..499bf4964a 100644
--- a/tests/qtest/tpm-tis-i2c-util.h
+++ b/tests/qtest/tpm-tis-i2c-util.h
@@ -27,4 +27,8 @@ uint32_t tpm_tis_i2c_readl(QTestState *s, uint8_t locty, uint8_t reg);
void tpm_tis_i2c_writeb(QTestState *s, uint8_t locty, uint8_t reg, uint8_t v);
void tpm_tis_i2c_writel(QTestState *s, uint8_t locty, uint8_t reg, uint32_t v);
+void tpm_tis_i2c_transfer(QTestState *s,
+ const unsigned char *req, size_t req_size,
+ unsigned char *rsp, size_t rsp_size);
+
#endif /* TESTS_TPM_TIS_I2C_UTIL_H */
--
2.54.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PULL v1 07/16] migration/vmstate: Add VMState support for GByteArray
2026-06-01 21:53 [PULL v1 00/16] Merge tpm 2026/06/01 v1 Stefan Berger
` (5 preceding siblings ...)
2026-06-01 21:53 ` [PULL v1 06/16] tests: Add a TPM TIS I2C swtpm test Stefan Berger
@ 2026-06-01 21:54 ` Stefan Berger
2026-06-01 21:54 ` [PULL v1 08/16] ui/vdagent: Use VMSTATE_GBYTEARRAY to safely migrate outbuf Stefan Berger
` (9 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2026-06-01 21:54 UTC (permalink / raw)
To: qemu-devel
Cc: peter.maydell, Arun Menon, Marc-André Lureau, Peter Xu,
Stefan Berger
From: Arun Menon <armenon@redhat.com>
In GLib, GByteArray is an object managed by the library. Currently,
migrating a GByteArray requires treating it as a raw C struct and using
VMSTATE_VBUFFER_ALLOC_UINT32. For example, see vmstate_vdba in
ui/vdagent.c
QEMU cannot pretend that GByteArray is a C struct and simply use
VMS_ALLOC to g_malloc() the buffer. This is because, VMS_ALLOC blindly
overwrites the data pointer with a newly allocated buffer, thereby
leaking the previous memory. Besides, GLib tracks the array's capacity
in a hidden alloc field. Bypassing GLib APIs leave this capacity out of
sync with the newly allocated buffer, potentially leading to heap buffer
overflows during subsequent g_byte_array_append() calls.
This commit introduces VMSTATE_GBYTEARRAY which uses specific library
API calls (g_byte_array_set_size()) to safely resize and populate the
buffer.
Signed-off-by: Arun Menon <armenon@redhat.com>
Suggested-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Peter Xu <peterx@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260423105733.113046-2-armenon@redhat.com
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
include/migration/vmstate.h | 10 ++++++++++
migration/vmstate-types.c | 28 ++++++++++++++++++++++++++++
2 files changed, 38 insertions(+)
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 0a8a2e85a6..1b7f295417 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -308,6 +308,7 @@ extern const VMStateInfo vmstate_info_bitmap;
extern const VMStateInfo vmstate_info_qtailq;
extern const VMStateInfo vmstate_info_gtree;
extern const VMStateInfo vmstate_info_qlist;
+extern const VMStateInfo vmstate_info_g_byte_array;
#define type_check_2darray(t1,t2,n,m) ((t1(*)[n][m])0 - (t2*)0)
/*
@@ -957,6 +958,15 @@ extern const VMStateInfo vmstate_info_qlist;
.start = offsetof(_type, _next), \
}
+#define VMSTATE_GBYTEARRAY(_field, _state, _version) { \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .size = sizeof(GByteArray), \
+ .info = &vmstate_info_g_byte_array, \
+ .flags = VMS_SINGLE, \
+ .offset = vmstate_offset_pointer(_state, _field, GByteArray), \
+}
+
/* _f : field name
_f_n : num of elements field_name
_n : num of elements
diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c
index ae465c5c2c..8c01215c25 100644
--- a/migration/vmstate-types.c
+++ b/migration/vmstate-types.c
@@ -924,3 +924,31 @@ const VMStateInfo vmstate_info_qlist = {
.load = load_qlist,
.save = save_qlist,
};
+
+static int get_g_byte_array(QEMUFile *f, void *pv, size_t size,
+ const VMStateField *field)
+{
+ GByteArray *byte_array = *(GByteArray **)pv;
+ uint32_t len = qemu_get_be32(f);
+
+ g_byte_array_set_size(byte_array, len);
+ qemu_get_buffer(f, byte_array->data, len);
+ return 0;
+}
+
+static int put_g_byte_array(QEMUFile *f, void *pv, size_t size,
+ const VMStateField *field, JSONWriter *vmdesc)
+{
+ GByteArray *byte_array = *(GByteArray **)pv;
+
+ qemu_put_be32(f, byte_array->len);
+ qemu_put_buffer(f, byte_array->data, byte_array->len);
+
+ return 0;
+}
+
+const VMStateInfo vmstate_info_g_byte_array = {
+ .name = "GByteArray",
+ .get = get_g_byte_array,
+ .put = put_g_byte_array,
+};
--
2.54.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PULL v1 08/16] ui/vdagent: Use VMSTATE_GBYTEARRAY to safely migrate outbuf
2026-06-01 21:53 [PULL v1 00/16] Merge tpm 2026/06/01 v1 Stefan Berger
` (6 preceding siblings ...)
2026-06-01 21:54 ` [PULL v1 07/16] migration/vmstate: Add VMState support for GByteArray Stefan Berger
@ 2026-06-01 21:54 ` Stefan Berger
2026-06-01 21:54 ` [PULL v1 09/16] hw/tpm: Add TPM CRB chunking fields Stefan Berger
` (8 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2026-06-01 21:54 UTC (permalink / raw)
To: qemu-devel
Cc: peter.maydell, Arun Menon, Marc-André Lureau, Peter Xu,
Stefan Berger
From: Arun Menon <armenon@redhat.com>
Migrating a GLib GByteArray is now possible directly using the newly
introduced VMSTATE_GBYTEARRAY. It uses the standard GLib API calls to
create the array, or resize it.
This is safer than implementing a C struct and manually updating the
data and len fields. This commit uses the VMSTATE_GBYTEARRAY in vdagent
to store the outbuf variable.
Signed-off-by: Arun Menon <armenon@redhat.com>
Suggested-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Peter Xu <peterx@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260423105733.113046-3-armenon@redhat.com
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
ui/vdagent.c | 13 +------------
1 file changed, 1 insertion(+), 12 deletions(-)
diff --git a/ui/vdagent.c b/ui/vdagent.c
index 8fa325bffa..b9784d4d9b 100644
--- a/ui/vdagent.c
+++ b/ui/vdagent.c
@@ -964,17 +964,6 @@ static const VMStateDescription vmstate_chunk = {
}
};
-static const VMStateDescription vmstate_vdba = {
- .name = "vdagent/bytearray",
- .version_id = 0,
- .minimum_version_id = 0,
- .fields = (const VMStateField[]) {
- VMSTATE_UINT32(len, GByteArray),
- VMSTATE_VBUFFER_ALLOC_UINT32(data, GByteArray, 0, 0, len),
- VMSTATE_END_OF_LIST()
- }
-};
-
struct CBInfoArray {
uint32_t n;
QemuClipboardInfo cbinfo[QEMU_CLIPBOARD_SELECTION__COUNT];
@@ -1064,7 +1053,7 @@ static const VMStateDescription vmstate_vdagent = {
VMSTATE_UINT32(xsize, VDAgentChardev),
VMSTATE_UINT32(xoff, VDAgentChardev),
VMSTATE_VBUFFER_ALLOC_UINT32(xbuf, VDAgentChardev, 0, 0, xsize),
- VMSTATE_STRUCT_POINTER(outbuf, VDAgentChardev, vmstate_vdba, GByteArray),
+ VMSTATE_GBYTEARRAY(outbuf, VDAgentChardev, 0),
VMSTATE_UINT32(mouse_x, VDAgentChardev),
VMSTATE_UINT32(mouse_y, VDAgentChardev),
VMSTATE_UINT32(mouse_btn, VDAgentChardev),
--
2.54.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PULL v1 09/16] hw/tpm: Add TPM CRB chunking fields
2026-06-01 21:53 [PULL v1 00/16] Merge tpm 2026/06/01 v1 Stefan Berger
` (7 preceding siblings ...)
2026-06-01 21:54 ` [PULL v1 08/16] ui/vdagent: Use VMSTATE_GBYTEARRAY to safely migrate outbuf Stefan Berger
@ 2026-06-01 21:54 ` Stefan Berger
2026-06-01 21:54 ` [PULL v1 10/16] hw/tpm: Refactor CRB_CTRL_START register access Stefan Berger
` (7 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2026-06-01 21:54 UTC (permalink / raw)
To: qemu-devel
Cc: peter.maydell, Arun Menon, Stefan Berger, Marc-André Lureau
From: Arun Menon <armenon@redhat.com>
- Add new fields to the CRB Interface Identifier and the CRB
Control Start registers.
- CRB_CTRL_START now has 2 new settings, that can be toggled using the
nextChunk and crbRspRetry bits.
- CapCRBChunk bit (10) was Reserved1 previously. The field is reused in
this revision of the specification. Refer to section 6.4.2.2 of [1]
- Add hw_compat global property called cap-chunk because the chunking
feature is only supported for machine type 11.1 and higher.
[1] https://trustedcomputinggroup.org/wp-content/uploads/PC-Client-Specific-Platform-TPM-Profile-for-TPM-2p0-v1p07_Pub.pdf
Signed-off-by: Arun Menon <armenon@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260506075813.120781-2-armenon@redhat.com
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
hw/core/machine.c | 1 +
hw/tpm/tpm_crb.c | 6 ++++++
include/hw/acpi/tpm.h | 5 ++++-
3 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 63baff859f..00eb3432a7 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -41,6 +41,7 @@
GlobalProperty hw_compat_11_0[] = {
{ "chardev-vc", "encoding", "cp437" },
+ { "tpm-crb", "cap-chunk", "off" },
};
const size_t hw_compat_11_0_len = G_N_ELEMENTS(hw_compat_11_0);
diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
index 02701ab948..bfa09c04cf 100644
--- a/hw/tpm/tpm_crb.c
+++ b/hw/tpm/tpm_crb.c
@@ -44,6 +44,8 @@ struct CRBState {
size_t be_buffer_size;
TPMPPI ppi;
+
+ bool cap_chunk;
};
typedef struct CRBState CRBState;
@@ -58,6 +60,7 @@ DECLARE_INSTANCE_CHECKER(CRBState, CRB,
#define CRB_INTF_CAP_FIFO_NOT_SUPPORTED 0b0
#define CRB_INTF_CAP_CRB_SUPPORTED 0b1
#define CRB_INTF_IF_SELECTOR_CRB 0b1
+#define CRB_INTF_CAP_CRB_CHUNK 0b1
#define CRB_CTRL_CMD_SIZE (TPM_CRB_ADDR_SIZE - A_CRB_DATA_BUFFER)
@@ -227,6 +230,7 @@ static const VMStateDescription vmstate_tpm_crb = {
static const Property tpm_crb_properties[] = {
DEFINE_PROP_TPMBE("tpmdev", CRBState, tpmbe),
+ DEFINE_PROP_BOOL("cap-chunk", CRBState, cap_chunk, true),
};
static void tpm_crb_reset(void *dev)
@@ -258,6 +262,8 @@ static void tpm_crb_reset(void *dev)
CapCRB, CRB_INTF_CAP_CRB_SUPPORTED);
ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
InterfaceSelector, CRB_INTF_IF_SELECTOR_CRB);
+ ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
+ CapCRBChunk, s->cap_chunk ? CRB_INTF_CAP_CRB_CHUNK : 0);
ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID,
RID, 0b0000);
ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID2,
diff --git a/include/hw/acpi/tpm.h b/include/hw/acpi/tpm.h
index 2ab186a745..782dc8212c 100644
--- a/include/hw/acpi/tpm.h
+++ b/include/hw/acpi/tpm.h
@@ -150,7 +150,7 @@ REG32(CRB_INTF_ID, 0x30)
FIELD(CRB_INTF_ID, InterfaceVersion, 4, 4)
FIELD(CRB_INTF_ID, CapLocality, 8, 1)
FIELD(CRB_INTF_ID, CapCRBIdleBypass, 9, 1)
- FIELD(CRB_INTF_ID, Reserved1, 10, 1)
+ FIELD(CRB_INTF_ID, CapCRBChunk, 10, 1)
FIELD(CRB_INTF_ID, CapDataXferSizeSupport, 11, 2)
FIELD(CRB_INTF_ID, CapFIFO, 13, 1)
FIELD(CRB_INTF_ID, CapCRB, 14, 1)
@@ -169,6 +169,9 @@ REG32(CRB_CTRL_STS, 0x44)
FIELD(CRB_CTRL_STS, tpmIdle, 1, 1)
REG32(CRB_CTRL_CANCEL, 0x48)
REG32(CRB_CTRL_START, 0x4C)
+ FIELD(CRB_CTRL_START, Start, 0, 1)
+ FIELD(CRB_CTRL_START, crbRspRetry, 1, 1)
+ FIELD(CRB_CTRL_START, nextChunk, 2, 1)
REG32(CRB_INT_ENABLED, 0x50)
REG32(CRB_INT_STS, 0x54)
REG32(CRB_CTRL_CMD_SIZE, 0x58)
--
2.54.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PULL v1 10/16] hw/tpm: Refactor CRB_CTRL_START register access
2026-06-01 21:53 [PULL v1 00/16] Merge tpm 2026/06/01 v1 Stefan Berger
` (8 preceding siblings ...)
2026-06-01 21:54 ` [PULL v1 09/16] hw/tpm: Add TPM CRB chunking fields Stefan Berger
@ 2026-06-01 21:54 ` Stefan Berger
2026-06-01 21:54 ` [PULL v1 11/16] hw/tpm: Add internal buffer state for chunking Stefan Berger
` (6 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2026-06-01 21:54 UTC (permalink / raw)
To: qemu-devel
Cc: peter.maydell, Arun Menon, Stefan Berger, Marc-André Lureau
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>
Link: https://lore.kernel.org/qemu-devel/20260506075813.120781-3-armenon@redhat.com
Signed-off-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 bfa09c04cf..a0f472652e 100644
--- a/hw/tpm/tpm_crb.c
+++ b/hw/tpm/tpm_crb.c
@@ -146,7 +146,7 @@ static void tpm_crb_mmio_write(void *opaque, hwaddr addr,
tpm_crb_get_active_locty(s) == locty) {
void *mem = memory_region_get_ram_ptr(&s->cmdmem);
- s->regs[R_CRB_CTRL_START] |= CRB_START_INVOKE;
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, Start, 1);
s->cmd = (TPMBackendCmd) {
.in = mem,
.in_len = MIN(tpm_cmd_get_size(mem), s->be_buffer_size),
@@ -195,7 +195,7 @@ static void tpm_crb_request_completed(TPMIf *ti, int ret)
{
CRBState *s = CRB(ti);
- s->regs[R_CRB_CTRL_START] &= ~CRB_START_INVOKE;
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, Start, 0);
if (ret != 0) {
ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS,
tpmSts, 1); /* fatal error */
--
2.54.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PULL v1 11/16] hw/tpm: Add internal buffer state for chunking
2026-06-01 21:53 [PULL v1 00/16] Merge tpm 2026/06/01 v1 Stefan Berger
` (9 preceding siblings ...)
2026-06-01 21:54 ` [PULL v1 10/16] hw/tpm: Refactor CRB_CTRL_START register access Stefan Berger
@ 2026-06-01 21:54 ` Stefan Berger
2026-06-01 21:54 ` [PULL v1 12/16] hw/tpm: Implement TPM CRB chunking logic Stefan Berger
` (5 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2026-06-01 21:54 UTC (permalink / raw)
To: qemu-devel
Cc: peter.maydell, Arun Menon, Marc-André Lureau, Stefan Berger
From: Arun Menon <armenon@redhat.com>
- Introduce GByteArray buffers to hold the command request and response
data during chunked TPM CRB transactions.
- Add helper function to clean them.
Signed-off-by: Arun Menon <armenon@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Link: https://lore.kernel.org/qemu-devel/20260506075813.120781-4-armenon@redhat.com
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
hw/tpm/tpm_crb.c | 33 +++++++++++++++++++++++++++++----
1 file changed, 29 insertions(+), 4 deletions(-)
diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
index a0f472652e..1c944d7ef2 100644
--- a/hw/tpm/tpm_crb.c
+++ b/hw/tpm/tpm_crb.c
@@ -38,10 +38,13 @@ struct CRBState {
TPMBackend *tpmbe;
TPMBackendCmd cmd;
uint32_t regs[TPM_CRB_R_MAX];
+ size_t be_buffer_size;
MemoryRegion mmio;
MemoryRegion cmdmem;
- size_t be_buffer_size;
+ GByteArray *command_buffer;
+ GByteArray *response_buffer;
+ uint32_t response_offset;
TPMPPI ppi;
@@ -86,6 +89,13 @@ enum crb_cancel {
#define TPM_CRB_NO_LOCALITY 0xff
+static void tpm_crb_clear_internal_buffers(CRBState *s)
+{
+ g_byte_array_set_size(s->response_buffer, 0);
+ g_byte_array_set_size(s->command_buffer, 0);
+ s->response_offset = 0;
+}
+
static uint64_t tpm_crb_mmio_read(void *opaque, hwaddr addr,
unsigned size)
{
@@ -135,9 +145,11 @@ static void tpm_crb_mmio_write(void *opaque, hwaddr addr,
}
break;
case A_CRB_CTRL_CANCEL:
- if (val == CRB_CANCEL_INVOKE &&
- s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) {
- tpm_backend_cancel_cmd(s->tpmbe);
+ if (val == CRB_CANCEL_INVOKE) {
+ if (s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) {
+ tpm_backend_cancel_cmd(s->tpmbe);
+ }
+ tpm_crb_clear_internal_buffers(s);
}
break;
case A_CRB_CTRL_START:
@@ -239,6 +251,7 @@ static void tpm_crb_reset(void *dev)
tpm_ppi_reset(&s->ppi);
tpm_backend_reset(s->tpmbe);
+ tpm_crb_clear_internal_buffers(s);
memset(s->regs, 0, sizeof(s->regs));
@@ -305,6 +318,9 @@ static void tpm_crb_realize(DeviceState *dev, Error **errp)
memory_region_add_subregion(get_system_memory(),
TPM_CRB_ADDR_BASE + sizeof(s->regs), &s->cmdmem);
+ s->command_buffer = g_byte_array_new();
+ s->response_buffer = g_byte_array_new();
+
tpm_ppi_init(&s->ppi, get_system_memory(),
TPM_PPI_ADDR_BASE, OBJECT(s));
@@ -315,12 +331,21 @@ static void tpm_crb_realize(DeviceState *dev, Error **errp)
}
}
+static void tpm_crb_unrealize(DeviceState *dev)
+{
+ CRBState *s = CRB(dev);
+
+ g_clear_pointer(&s->command_buffer, g_byte_array_unref);
+ g_clear_pointer(&s->response_buffer, g_byte_array_unref);
+}
+
static void tpm_crb_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
TPMIfClass *tc = TPM_IF_CLASS(klass);
dc->realize = tpm_crb_realize;
+ dc->unrealize = tpm_crb_unrealize;
device_class_set_props(dc, tpm_crb_properties);
dc->vmsd = &vmstate_tpm_crb;
dc->user_creatable = true;
--
2.54.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PULL v1 12/16] hw/tpm: Implement TPM CRB chunking logic
2026-06-01 21:53 [PULL v1 00/16] Merge tpm 2026/06/01 v1 Stefan Berger
` (10 preceding siblings ...)
2026-06-01 21:54 ` [PULL v1 11/16] hw/tpm: Add internal buffer state for chunking Stefan Berger
@ 2026-06-01 21:54 ` Stefan Berger
2026-06-01 21:54 ` [PULL v1 13/16] test/qtest: Add test for tpm crb chunking Stefan Berger
` (4 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2026-06-01 21:54 UTC (permalink / raw)
To: qemu-devel
Cc: peter.maydell, Arun Menon, Stefan Berger, Marc-André Lureau
From: Arun Menon <armenon@redhat.com>
- Add logic to populate internal TPM command request and response
buffers and to toggle the control registers after each operation.
- The chunk size is limited to CRB_CTRL_CMD_SIZE which is
(TPM_CRB_ADDR_SIZE - A_CRB_DATA_BUFFER). This comes out as 3968 bytes
(4096 - 128 or 0x1000 - 0x80), because 128 bytes are reserved for
control and status registers. In other words, only 3968 bytes are
available for the TPM data.
- With this feature, guests can send commands larger than 3968 bytes.
- Refer section 6.5.3.9 of [1] for implementation details.
[1] https://trustedcomputinggroup.org/wp-content/uploads/PC-Client-Specific-Platform-TPM-Profile-for-TPM-2p0-v1p07_Pub.pdf
Signed-off-by: Arun Menon <armenon@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260506075813.120781-5-armenon@redhat.com
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
hw/tpm/tpm_crb.c | 143 ++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 130 insertions(+), 13 deletions(-)
diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
index 1c944d7ef2..f85df08185 100644
--- a/hw/tpm/tpm_crb.c
+++ b/hw/tpm/tpm_crb.c
@@ -17,6 +17,7 @@
#include "qemu/osdep.h"
#include "qemu/module.h"
+#include "qemu/error-report.h"
#include "qapi/error.h"
#include "system/address-spaces.h"
#include "hw/core/qdev-properties.h"
@@ -66,6 +67,7 @@ DECLARE_INSTANCE_CHECKER(CRBState, CRB,
#define CRB_INTF_CAP_CRB_CHUNK 0b1
#define CRB_CTRL_CMD_SIZE (TPM_CRB_ADDR_SIZE - A_CRB_DATA_BUFFER)
+#define TPM_HEADER_SIZE 10
enum crb_loc_ctrl {
CRB_LOC_CTRL_REQUEST_ACCESS = BIT(0),
@@ -81,6 +83,8 @@ enum crb_ctrl_req {
enum crb_start {
CRB_START_INVOKE = BIT(0),
+ CRB_START_RSP_RETRY = BIT(1),
+ CRB_START_NEXT_CHUNK = BIT(2),
};
enum crb_cancel {
@@ -123,6 +127,69 @@ static uint8_t tpm_crb_get_active_locty(CRBState *s)
return ARRAY_FIELD_EX32(s->regs, CRB_LOC_STATE, activeLocality);
}
+static bool tpm_crb_append_command_request(CRBState *s)
+{
+ /*
+ * The linux guest writes the TPM command to the MMIO region in chunks.
+ * This function appends a chunk from the MMIO region to internal
+ * command_buffer.
+ */
+ void *mem = memory_region_get_ram_ptr(&s->cmdmem);
+ uint32_t to_copy = 0;
+ uint32_t total_request_size = 0;
+
+ /*
+ * The initial call extracts the total TPM command size
+ * from its header. For the subsequent calls, the data already
+ * appended in the command_buffer is used to calculate the total
+ * size, as its header stays the same.
+ */
+ if (s->command_buffer->len == 0) {
+ total_request_size = tpm_cmd_get_size(mem);
+ if (total_request_size < TPM_HEADER_SIZE) {
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS, tpmSts, 1);
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, Start, 0);
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, nextChunk, 0);
+ tpm_crb_clear_internal_buffers(s);
+ error_report("Command size %" PRIu32 " less than "
+ "TPM header size %" PRIu32,
+ total_request_size, (uint32_t)TPM_HEADER_SIZE);
+ return false;
+ }
+ } else {
+ total_request_size = tpm_cmd_get_size(s->command_buffer->data);
+ }
+ total_request_size = MIN(total_request_size, s->be_buffer_size);
+
+ if (total_request_size > s->command_buffer->len) {
+ uint32_t remaining = total_request_size - s->command_buffer->len;
+ to_copy = MIN(remaining, CRB_CTRL_CMD_SIZE);
+ g_byte_array_append(s->command_buffer, (guint8 *)mem, to_copy);
+ }
+ return true;
+}
+
+static void tpm_crb_fill_command_response(CRBState *s)
+{
+ /*
+ * Response from the tpm backend will be stored in the internal
+ * response_buffer. This function will serve that accumulated response
+ * to the linux guest in chunks by writing it back to MMIO region.
+ */
+ void *mem = memory_region_get_ram_ptr(&s->cmdmem);
+ uint32_t remaining = s->response_buffer->len - s->response_offset;
+ uint32_t to_copy = MIN(CRB_CTRL_CMD_SIZE, remaining);
+
+ memcpy(mem, s->response_buffer->data + s->response_offset, to_copy);
+
+ if (to_copy < CRB_CTRL_CMD_SIZE) {
+ memset((guint8 *)mem + to_copy, 0, CRB_CTRL_CMD_SIZE - to_copy);
+ }
+
+ s->response_offset += to_copy;
+ memory_region_set_dirty(&s->cmdmem, 0, CRB_CTRL_CMD_SIZE);
+}
+
static void tpm_crb_mmio_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
@@ -153,20 +220,58 @@ static void tpm_crb_mmio_write(void *opaque, hwaddr addr,
}
break;
case A_CRB_CTRL_START:
- if (val == CRB_START_INVOKE &&
- !(s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) &&
- tpm_crb_get_active_locty(s) == locty) {
- void *mem = memory_region_get_ram_ptr(&s->cmdmem);
-
+ if (tpm_crb_get_active_locty(s) != locty) {
+ break;
+ }
+ if (s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) {
+ /*
+ * Backend TPM is busy processing a request.
+ */
+ break;
+ }
+ if (val & CRB_START_INVOKE) {
+ if (!tpm_crb_append_command_request(s)) {
+ break;
+ }
ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, Start, 1);
+ g_byte_array_set_size(s->response_buffer, s->be_buffer_size);
s->cmd = (TPMBackendCmd) {
- .in = mem,
- .in_len = MIN(tpm_cmd_get_size(mem), s->be_buffer_size),
- .out = mem,
- .out_len = s->be_buffer_size,
+ .in = s->command_buffer->data,
+ .in_len = s->command_buffer->len,
+ .out = s->response_buffer->data,
+ .out_len = s->response_buffer->len,
};
-
tpm_backend_deliver_request(s->tpmbe, &s->cmd);
+ } else if (val & CRB_START_NEXT_CHUNK) {
+ if (!s->cap_chunk) {
+ break;
+ }
+ /*
+ * nextChunk is used both while sending and receiving data.
+ * To distinguish between the two, response_buffer is checked.
+ * If it does not have data, then that means we have not yet
+ * sent the command to the tpm backend, and therefore call
+ * tpm_crb_append_command_request().
+ */
+ if (s->response_buffer->len > 0 &&
+ s->response_offset < s->response_buffer->len) {
+ tpm_crb_fill_command_response(s);
+ } else {
+ if (!tpm_crb_append_command_request(s)) {
+ break;
+ }
+ }
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, nextChunk, 0);
+ } else if (val & CRB_START_RSP_RETRY) {
+ if (!s->cap_chunk) {
+ break;
+ }
+ if (s->response_buffer->len > 0) {
+ s->response_offset = 0;
+ tpm_crb_fill_command_response(s);
+ }
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, crbRspRetry, 0);
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, nextChunk, 0);
}
break;
case A_CRB_LOC_CTRL:
@@ -211,8 +316,21 @@ static void tpm_crb_request_completed(TPMIf *ti, int ret)
if (ret != 0) {
ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS,
tpmSts, 1); /* fatal error */
+ tpm_crb_clear_internal_buffers(s);
+ } else {
+ uint32_t actual_resp_size = tpm_cmd_get_size(s->response_buffer->data);
+ uint32_t total_resp_size = MIN(actual_resp_size, s->be_buffer_size);
+ g_byte_array_set_size(s->response_buffer, total_resp_size);
+ s->response_offset = 0;
}
- memory_region_set_dirty(&s->cmdmem, 0, CRB_CTRL_CMD_SIZE);
+ /*
+ * Send the first chunk. Subsequent chunks will be sent
+ * on receiving nextChunk from the guest
+ */
+ tpm_crb_fill_command_response(s);
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, nextChunk, 0);
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, crbRspRetry, 0);
+ g_byte_array_set_size(s->command_buffer, 0);
}
static enum TPMVersion tpm_crb_get_version(TPMIf *ti)
@@ -287,8 +405,7 @@ static void tpm_crb_reset(void *dev)
s->regs[R_CRB_CTRL_RSP_SIZE] = CRB_CTRL_CMD_SIZE;
s->regs[R_CRB_CTRL_RSP_ADDR] = TPM_CRB_ADDR_BASE + A_CRB_DATA_BUFFER;
- s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->tpmbe),
- CRB_CTRL_CMD_SIZE);
+ s->be_buffer_size = tpm_backend_get_buffer_size(s->tpmbe);
if (tpm_backend_startup_tpm(s->tpmbe, s->be_buffer_size) < 0) {
exit(1);
--
2.54.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PULL v1 13/16] test/qtest: Add test for tpm crb chunking
2026-06-01 21:53 [PULL v1 00/16] Merge tpm 2026/06/01 v1 Stefan Berger
` (11 preceding siblings ...)
2026-06-01 21:54 ` [PULL v1 12/16] hw/tpm: Implement TPM CRB chunking logic Stefan Berger
@ 2026-06-01 21:54 ` Stefan Berger
2026-06-01 21:54 ` [PULL v1 14/16] hw/tpm: Add support for VM migration with TPM CRB chunking Stefan Berger
` (3 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2026-06-01 21:54 UTC (permalink / raw)
To: qemu-devel
Cc: peter.maydell, Arun Menon, Marc-André Lureau, Stefan Berger
From: Arun Menon <armenon@redhat.com>
- New test case added to the swtpm test. Data is written and read from
the buffer in chunks.
- The chunk size is dynamically calculated by reading the
CRB_CTRL_CMD_SIZE address. This can be changed manually to test.
- Add a helper function tpm_wait_till_bit_clear()
- Note that this commit does not yet exercise the chunked read/write
logic, as current transfer sizes remain small. Testing for large
transfers is introduced in a subsequent patch: 'tests: Use ML-DSA-87
operations to cause large TPM transfers with CRB'
Signed-off-by: Arun Menon <armenon@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Link: https://lore.kernel.org/qemu-devel/20260506075813.120781-6-armenon@redhat.com
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
tests/qtest/tpm-crb-swtpm-test.c | 10 +++
tests/qtest/tpm-util.c | 109 ++++++++++++++++++++++++++-----
tests/qtest/tpm-util.h | 5 ++
3 files changed, 109 insertions(+), 15 deletions(-)
diff --git a/tests/qtest/tpm-crb-swtpm-test.c b/tests/qtest/tpm-crb-swtpm-test.c
index ffeb1c396b..050c7b0c1f 100644
--- a/tests/qtest/tpm-crb-swtpm-test.c
+++ b/tests/qtest/tpm-crb-swtpm-test.c
@@ -33,6 +33,14 @@ static void tpm_crb_swtpm_test(const void *data)
"tpm-crb", NULL);
}
+static void tpm_crb_chunk_swtpm_test(const void *data)
+{
+ const TestState *ts = data;
+
+ tpm_test_swtpm_test(ts->src_tpm_path, tpm_util_crb_chunk_transfer,
+ "tpm-crb", NULL);
+}
+
static void tpm_crb_swtpm_migration_test(const void *data)
{
const TestState *ts = data;
@@ -54,6 +62,8 @@ int main(int argc, char **argv)
g_test_init(&argc, &argv, NULL);
qtest_add_data_func("/tpm/crb-swtpm/test", &ts, tpm_crb_swtpm_test);
+ qtest_add_data_func("/tpm/crb-chunk-swtpm/test", &ts,
+ tpm_crb_chunk_swtpm_test);
qtest_add_data_func("/tpm/crb-swtpm-migration/test", &ts,
tpm_crb_swtpm_migration_test);
ret = g_test_run();
diff --git a/tests/qtest/tpm-util.c b/tests/qtest/tpm-util.c
index 0f9bef8971..151002ea24 100644
--- a/tests/qtest/tpm-util.c
+++ b/tests/qtest/tpm-util.c
@@ -14,16 +14,44 @@
#include "qemu/osdep.h"
#include <glib/gstdio.h>
+#include "qemu/bswap.h"
#include "hw/acpi/tpm.h"
#include "libqtest.h"
#include "tpm-util.h"
#include "qobject/qdict.h"
+#define CRB_ADDR_START (TPM_CRB_ADDR_BASE + A_CRB_CTRL_START)
+#define CRB_ADDR_CTRL_STS (TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS)
+#define CRB_ADDR_CTRL_CMD_SIZE \
+ (TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_SIZE)
+
+#define CRB_START_INVOKE (1 << 0)
+#define CRB_START_RSP_RETRY (1 << 1)
+#define CRB_START_NEXT_CHUNK (1 << 2)
+
+void tpm_wait_till_bit_clear(QTestState *s, uint64_t addr, uint32_t mask)
+{
+ uint32_t sts;
+ uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
+
+ while (true) {
+ sts = qtest_readl(s, addr);
+ if ((sts & mask) == 0) {
+ break;
+ }
+ if (g_get_monotonic_time() >= end_time) {
+ g_assert_cmphex(sts & mask, ==, 0);
+ break;
+ }
+ }
+}
+
void tpm_util_crb_transfer(QTestState *s,
const unsigned char *req, size_t req_size,
unsigned char *rsp, size_t rsp_size)
{
+ uint32_t tpm_sts;
uint64_t caddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR);
uint64_t raddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR);
@@ -31,24 +59,75 @@ void tpm_util_crb_transfer(QTestState *s,
qtest_memwrite(s, caddr, req, req_size);
- uint32_t sts, start = 1;
- uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
- qtest_writel(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start);
- while (true) {
- start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START);
- if ((start & 1) == 0) {
- break;
+ qtest_writel(s, CRB_ADDR_START, CRB_START_INVOKE);
+ tpm_wait_till_bit_clear(s, CRB_ADDR_START, CRB_START_INVOKE);
+
+ tpm_sts = qtest_readl(s, CRB_ADDR_CTRL_STS);
+ g_assert_cmpint(tpm_sts & 1, ==, 0);
+
+ qtest_memread(s, raddr, rsp, rsp_size);
+}
+
+void tpm_util_crb_chunk_transfer(QTestState *s,
+ const unsigned char *req, size_t req_size,
+ unsigned char *rsp, size_t rsp_size)
+{
+ uint32_t tpm_sts;
+ size_t chunk_size;
+ unsigned char header[10];
+ uint32_t actual_response_size = 0;
+
+ uint64_t caddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR);
+ uint64_t raddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR);
+ uint32_t crb_ctrl_cmd_size = qtest_readl(s, CRB_ADDR_CTRL_CMD_SIZE);
+
+ chunk_size = crb_ctrl_cmd_size;
+
+ qtest_writeb(s, TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 1);
+
+ for (size_t i = 0; i < req_size; i += chunk_size) {
+ bool last_chunk = false;
+ size_t current_chunk_size = chunk_size;
+
+ if (i + chunk_size > req_size) {
+ last_chunk = true;
+ current_chunk_size = req_size - i;
}
- if (g_get_monotonic_time() >= end_time) {
- break;
+
+ qtest_memwrite(s, caddr, req + i, current_chunk_size);
+
+ if (last_chunk) {
+ qtest_writel(s, CRB_ADDR_START, CRB_START_INVOKE);
+ tpm_wait_till_bit_clear(s, CRB_ADDR_START, CRB_START_INVOKE);
+ } else {
+ qtest_writel(s, CRB_ADDR_START, CRB_START_NEXT_CHUNK);
+ tpm_wait_till_bit_clear(s, CRB_ADDR_START, CRB_START_NEXT_CHUNK);
}
- };
- start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START);
- g_assert_cmpint(start & 1, ==, 0);
- sts = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS);
- g_assert_cmpint(sts & 1, ==, 0);
+ }
+ tpm_sts = qtest_readl(s, CRB_ADDR_CTRL_STS);
+ g_assert_cmpint(tpm_sts & 1, ==, 0);
- qtest_memread(s, raddr, rsp, rsp_size);
+ /*
+ * Read response in chunks
+ */
+
+ qtest_memread(s, raddr, header, sizeof(header));
+ actual_response_size = ldl_be_p(&header[2]);
+
+ if (actual_response_size > rsp_size) {
+ actual_response_size = rsp_size;
+ }
+
+ for (size_t i = 0; i < actual_response_size; i += chunk_size) {
+ size_t to_read = i + chunk_size > actual_response_size
+ ? actual_response_size - i
+ : chunk_size;
+ if (i > 0) {
+ qtest_writel(s, CRB_ADDR_START, CRB_START_NEXT_CHUNK);
+ tpm_wait_till_bit_clear(s, CRB_ADDR_START, CRB_START_NEXT_CHUNK);
+ }
+ qtest_memread(s, raddr, rsp + i, to_read);
+ }
}
void tpm_util_startup(QTestState *s, tx_func *tx)
diff --git a/tests/qtest/tpm-util.h b/tests/qtest/tpm-util.h
index 0cb28dd6e5..681544e7d8 100644
--- a/tests/qtest/tpm-util.h
+++ b/tests/qtest/tpm-util.h
@@ -24,10 +24,15 @@ typedef void (tx_func)(QTestState *s,
const unsigned char *req, size_t req_size,
unsigned char *rsp, size_t rsp_size);
+void tpm_wait_till_bit_clear(QTestState *s, uint64_t addr, uint32_t mask);
void tpm_util_crb_transfer(QTestState *s,
const unsigned char *req, size_t req_size,
unsigned char *rsp, size_t rsp_size);
+void tpm_util_crb_chunk_transfer(QTestState *s,
+ const unsigned char *req, size_t req_size,
+ unsigned char *rsp, size_t rsp_size);
+
void tpm_util_startup(QTestState *s, tx_func *tx);
void tpm_util_pcrextend(QTestState *s, tx_func *tx);
void tpm_util_pcrread(QTestState *s, tx_func *tx,
--
2.54.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PULL v1 14/16] hw/tpm: Add support for VM migration with TPM CRB chunking
2026-06-01 21:53 [PULL v1 00/16] Merge tpm 2026/06/01 v1 Stefan Berger
` (12 preceding siblings ...)
2026-06-01 21:54 ` [PULL v1 13/16] test/qtest: Add test for tpm crb chunking Stefan Berger
@ 2026-06-01 21:54 ` Stefan Berger
2026-06-01 21:54 ` [PULL v1 15/16] tpm_emulator: Reject a buffer size different than what was requested Stefan Berger
` (2 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2026-06-01 21:54 UTC (permalink / raw)
To: qemu-devel; +Cc: peter.maydell, Arun Menon, Stefan Berger
From: Arun Menon <armenon@redhat.com>
- Add subsection in VMState for TPM CRB with the newly introduced
command and response buffer GByteArrays, along with a needed callback,
so that newer QEMU only sends the buffers if it is necessary.
- Implement a migration blocker to prevent migration of the VM if the
user manually enables chunking capability, cap-chunk, but the machine
type does not support it, using a new hw_compat property called
allow_chunk_migration.
- Add a post_load_errp hook so that during a migration, the buffers are
validated before destination VM is started.
Signed-off-by: Arun Menon <armenon@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Link: https://lore.kernel.org/qemu-devel/20260506075813.120781-7-armenon@redhat.com
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
hw/core/machine.c | 1 +
hw/tpm/tpm_crb.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 68 insertions(+)
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 00eb3432a7..17970b78b6 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -42,6 +42,7 @@
GlobalProperty hw_compat_11_0[] = {
{ "chardev-vc", "encoding", "cp437" },
{ "tpm-crb", "cap-chunk", "off" },
+ { "tpm-crb", "x-allow-chunk-migration", "off" },
};
const size_t hw_compat_11_0_len = G_N_ELEMENTS(hw_compat_11_0);
diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
index f85df08185..54fa2042b5 100644
--- a/hw/tpm/tpm_crb.c
+++ b/hw/tpm/tpm_crb.c
@@ -24,6 +24,7 @@
#include "hw/pci/pci_ids.h"
#include "hw/acpi/tpm.h"
#include "migration/vmstate.h"
+#include "migration/blocker.h"
#include "system/tpm_backend.h"
#include "system/tpm_util.h"
#include "system/reset.h"
@@ -50,6 +51,8 @@ struct CRBState {
TPMPPI ppi;
bool cap_chunk;
+ bool allow_chunk_migration;
+ Error *migration_blocker;
};
typedef struct CRBState CRBState;
@@ -349,18 +352,68 @@ static int tpm_crb_pre_save(void *opaque)
return 0;
}
+static bool tpm_crb_chunk_needed(void *opaque)
+{
+ CRBState *s = opaque;
+
+ if (!s->allow_chunk_migration) {
+ return false;
+ }
+
+ return ((s->command_buffer && s->command_buffer->len > 0) ||
+ (s->response_buffer && s->response_buffer->len > 0));
+}
+
+static bool tpm_crb_chunk_post_load(void *opaque, int version_id, Error **errp)
+{
+ CRBState *s = opaque;
+
+ /*
+ * The external TPM emulator (example swtpm) determines the backend
+ * buffer capacity (s->be_buffer_size). This check ensures that if we
+ * migrate from a source with a PQC-enabled emulator that supports
+ * larger buffers to a destination with a non-PQC emulator, the
+ * migrated data does not exceed the destination's capacity.
+ */
+ if (s->response_buffer->len > s->be_buffer_size ||
+ s->command_buffer->len > s->be_buffer_size) {
+ error_setg(errp, "tpm-crb: Buffer sizes exceed backend capacity");
+ return false;
+ }
+ return true;
+}
+
+static const VMStateDescription vmstate_tpm_crb_chunk = {
+ .name = "tpm-crb/chunk",
+ .version_id = 0,
+ .needed = tpm_crb_chunk_needed,
+ .post_load_errp = tpm_crb_chunk_post_load,
+ .fields = (const VMStateField[]) {
+ VMSTATE_GBYTEARRAY(command_buffer, CRBState, 0),
+ VMSTATE_GBYTEARRAY(response_buffer, CRBState, 0),
+ VMSTATE_UINT32(response_offset, CRBState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_tpm_crb = {
.name = "tpm-crb",
.pre_save = tpm_crb_pre_save,
.fields = (const VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, CRBState, TPM_CRB_R_MAX),
VMSTATE_END_OF_LIST(),
+ },
+ .subsections = (const VMStateDescription * const []) {
+ &vmstate_tpm_crb_chunk,
+ NULL,
}
};
static const Property tpm_crb_properties[] = {
DEFINE_PROP_TPMBE("tpmdev", CRBState, tpmbe),
DEFINE_PROP_BOOL("cap-chunk", CRBState, cap_chunk, true),
+ DEFINE_PROP_BOOL("x-allow-chunk-migration", CRBState,
+ allow_chunk_migration, true),
};
static void tpm_crb_reset(void *dev)
@@ -415,6 +468,7 @@ static void tpm_crb_reset(void *dev)
static void tpm_crb_realize(DeviceState *dev, Error **errp)
{
CRBState *s = CRB(dev);
+ int ret;
if (!tpm_find()) {
error_setg(errp, "at most one TPM device is permitted");
@@ -424,6 +478,15 @@ static void tpm_crb_realize(DeviceState *dev, Error **errp)
error_setg(errp, "'tpmdev' property is required");
return;
}
+ if (s->cap_chunk && !s->allow_chunk_migration) {
+ error_setg(&s->migration_blocker,
+ "The tpm-crb device does not support chunk migration with "
+ "machine version less than 11.1");
+ ret = migrate_add_blocker_normal(&s->migration_blocker, errp);
+ if (ret < 0) {
+ return;
+ }
+ }
memory_region_init_io(&s->mmio, OBJECT(s), &tpm_crb_memory_ops, s,
"tpm-crb-mmio", sizeof(s->regs));
@@ -454,6 +517,10 @@ static void tpm_crb_unrealize(DeviceState *dev)
g_clear_pointer(&s->command_buffer, g_byte_array_unref);
g_clear_pointer(&s->response_buffer, g_byte_array_unref);
+
+ if (s->migration_blocker) {
+ migrate_del_blocker(&s->migration_blocker);
+ }
}
static void tpm_crb_class_init(ObjectClass *klass, const void *data)
--
2.54.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PULL v1 15/16] tpm_emulator: Reject a buffer size different than what was requested
2026-06-01 21:53 [PULL v1 00/16] Merge tpm 2026/06/01 v1 Stefan Berger
` (13 preceding siblings ...)
2026-06-01 21:54 ` [PULL v1 14/16] hw/tpm: Add support for VM migration with TPM CRB chunking Stefan Berger
@ 2026-06-01 21:54 ` Stefan Berger
2026-06-01 21:54 ` [PULL v1 16/16] tpm_emulator: Disconnect if response exceeds negotiated buffer size Stefan Berger
2026-06-02 17:23 ` [PULL v1 00/16] Merge tpm 2026/06/01 v1 Stefan Hajnoczi
16 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2026-06-01 21:54 UTC (permalink / raw)
To: qemu-devel; +Cc: peter.maydell, Stefan Berger, Marc-André Lureau
When the TIS, SPAPR, or CRB frontends negotiate a buffer size with the
TPM backend, then the tpm_emulator (swtpm) could still adjust this size
of the buffer to within bounds supported by swtpm+libtpms if the chosen
size was outside the acceptable range. This could theoretically lead to
the TPM 2 using a bigger buffer than what was requested and memory
allocated for. In practice this would not happend since the requested size
of 4096 bytes for TIS and SPAPR and 3968 bytes for CRB happen in the
(currently) supported range of ~2.5kb to 4096 bytes. With PQC support
the range will have an upper bound of 8kb and a lower bound that will
support the (pre-PQC) CRB with 3968 bytes.
Fixes: 9375c44fdfc0 ("tpm: tpm_emulator: get and set buffer size of device")
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260511142219.797048-2-stefanb@linux.ibm.com
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
backends/tpm/tpm_emulator.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c
index 75c33d290e..ac5427b84e 100644
--- a/backends/tpm/tpm_emulator.c
+++ b/backends/tpm/tpm_emulator.c
@@ -364,6 +364,7 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb,
{
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
ptm_setbuffersize psbs;
+ size_t tpm_buffersize;
if (tpm_emulator_stop_tpm(tb, errp) < 0) {
return -1;
@@ -387,8 +388,18 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb,
return -1;
}
+ tpm_buffersize = be32_to_cpu(psbs.u.resp.buffersize);
+ /* Reject different buffer size used by the TPM than what was requested. */
+ if (wanted_size != 0 && wanted_size != tpm_buffersize) {
+ error_setg(errp,
+ "tpm-emulator: TPM did not accept the requested buffer size "
+ "of %zu bytes but adjusted it to %zu bytes",
+ wanted_size, tpm_buffersize);
+ return -1;
+ }
+
if (actual_size) {
- *actual_size = be32_to_cpu(psbs.u.resp.buffersize);
+ *actual_size = tpm_buffersize;
}
trace_tpm_emulator_set_buffer_size(
--
2.54.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PULL v1 16/16] tpm_emulator: Disconnect if response exceeds negotiated buffer size
2026-06-01 21:53 [PULL v1 00/16] Merge tpm 2026/06/01 v1 Stefan Berger
` (14 preceding siblings ...)
2026-06-01 21:54 ` [PULL v1 15/16] tpm_emulator: Reject a buffer size different than what was requested Stefan Berger
@ 2026-06-01 21:54 ` Stefan Berger
2026-06-02 17:23 ` [PULL v1 00/16] Merge tpm 2026/06/01 v1 Stefan Hajnoczi
16 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2026-06-01 21:54 UTC (permalink / raw)
To: qemu-devel; +Cc: peter.maydell, Stefan Berger, Marc-André Lureau
Disconnect from the emulator if a response was to exceed the negotiated
buffer size.
The TPM TIS and SPAPR use 4096 bytes and the CRB 3968 bytes. There are
currently no TPM 2 responses using this size of a buffer and therefore
no response will be sent that is exceeding this size.
Fixes: f4ede81eed29 ("tpm: Added support for TPM emulator")
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260511142219.797048-3-stefanb@linux.ibm.com
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
backends/tpm/tpm_emulator.c | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c
index ac5427b84e..95d2de2f48 100644
--- a/backends/tpm/tpm_emulator.c
+++ b/backends/tpm/tpm_emulator.c
@@ -176,8 +176,10 @@ static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu,
bool *selftest_done,
Error **errp)
{
- ssize_t ret;
bool is_selftest = false;
+ Error *local_err = NULL;
+ uint32_t to_read;
+ ssize_t ret;
if (selftest_done) {
*selftest_done = false;
@@ -195,9 +197,25 @@ static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu,
return -1;
}
+ /*
+ * Size of response from emulator must be <= out_len (= negotiated buffer
+ * size)
+ */
+ to_read = tpm_cmd_get_size(out);
+ if (to_read > out_len) {
+ if (qio_channel_shutdown(tpm_emu->data_ioc, QIO_CHANNEL_SHUTDOWN_BOTH,
+ &local_err) < 0) {
+ error_report_err(local_err);
+ }
+ error_setg(errp, "tpm-emulator: Disconnected after receiving "
+ "unacceptable large response (%u > %u)",
+ to_read, out_len);
+ return -1;
+ }
+
ret = qio_channel_read_all(tpm_emu->data_ioc,
(char *)out + sizeof(struct tpm_resp_hdr),
- tpm_cmd_get_size(out) - sizeof(struct tpm_resp_hdr), errp);
+ to_read - sizeof(struct tpm_resp_hdr), errp);
if (ret != 0) {
return -1;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PULL v1 00/16] Merge tpm 2026/06/01 v1
2026-06-01 21:53 [PULL v1 00/16] Merge tpm 2026/06/01 v1 Stefan Berger
` (15 preceding siblings ...)
2026-06-01 21:54 ` [PULL v1 16/16] tpm_emulator: Disconnect if response exceeds negotiated buffer size Stefan Berger
@ 2026-06-02 17:23 ` Stefan Hajnoczi
16 siblings, 0 replies; 18+ messages in thread
From: Stefan Hajnoczi @ 2026-06-02 17:23 UTC (permalink / raw)
To: Stefan Berger; +Cc: qemu-devel, peter.maydell, Stefan Berger
[-- Attachment #1: Type: text/plain, Size: 116 bytes --]
Applied, thanks.
Please update the changelog at https://wiki.qemu.org/ChangeLog/11.1 for any user-visible changes.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 18+ messages in thread