Merge tag 'pull-tpm-2026-06-01-1' of https://github.com/stefanberger/qemu-tpm into staging

Merge tpm 2026/06/01 v1

# -----BEGIN PGP SIGNATURE-----
#
# iQEzBAABCgAdFiEEuBi5yt+QicLVzsZrda1lgCoLQhEFAmod/1EACgkQda1lgCoL
# QhGFmwf/SDA8DQGekVksB+TiDV11a+2I8FzBHJmQ2RhKbSGxY77ZbWei+Z45fU9/
# 2e0aDizw7GDRPGthKChjOYA5SAVf7gwUaSkhv21xDNgsddcdCr05y1RljOfShtbw
# S55XOk6Tx2zX/00nTqskyw9bqz2YwnAh5vPjfDWy6TV5k7Q3BlMyooFeJu/19pU7
# RnMLfHdJHlYSQ2Bn4ZT6agXsoQIvnJK51Poq0TJQo0PW8kWFwcp13Ic+uyvRfkF3
# U6JdTGBXJxCHMgIkO4EG4o5PT7Dy8miKFepJCUgyjHesi7iFYQlIAnsJth9anMjL
# zoqnMVNN1jVrcuzVLG9oNHB7+hZ/Eg==
# =Wlez
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon 01 Jun 2026 17:53:21 EDT
# gpg:                using RSA key B818B9CADF9089C2D5CEC66B75AD65802A0B4211
# gpg: Good signature from "Stefan Berger <stefanb@linux.vnet.ibm.com>" [unknown]
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: B818 B9CA DF90 89C2 D5CE  C66B 75AD 6580 2A0B 4211

* tag 'pull-tpm-2026-06-01-1' of https://github.com/stefanberger/qemu-tpm:
  tpm_emulator: Disconnect if response exceeds negotiated buffer size
  tpm_emulator: Reject a buffer size different than what was requested
  hw/tpm: Add support for VM migration with TPM CRB chunking
  test/qtest: Add test for tpm crb chunking
  hw/tpm: Implement TPM CRB chunking logic
  hw/tpm: Add internal buffer state for chunking
  hw/tpm: Refactor CRB_CTRL_START register access
  hw/tpm: Add TPM CRB chunking fields
  ui/vdagent: Use VMSTATE_GBYTEARRAY to safely migrate outbuf
  migration/vmstate: Add VMState support for GByteArray
  tests: Add a TPM TIS I2C swtpm test
  tests: Check whether the I2C master flag is set
  tests: Rename id of tpmdev to tpm0
  tests: Convert string arrays to byte arrays
  tests: Have TPM I2C read/write functions take QTestState as first parameter
  tests: Move TPM I2C bus read/write functions to common files

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi
2026-06-02 11:34:55 -04:00
17 changed files with 794 additions and 220 deletions

View File

@@ -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;
}
@@ -364,6 +382,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 +406,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(

View File

@@ -41,6 +41,8 @@
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);

View File

@@ -17,12 +17,14 @@
#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"
#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"
@@ -38,12 +40,19 @@ 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;
bool cap_chunk;
bool allow_chunk_migration;
Error *migration_blocker;
};
typedef struct CRBState CRBState;
@@ -58,8 +67,10 @@ 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)
#define TPM_HEADER_SIZE 10
enum crb_loc_ctrl {
CRB_LOC_CTRL_REQUEST_ACCESS = BIT(0),
@@ -75,6 +86,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 {
@@ -83,6 +96,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)
{
@@ -110,6 +130,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)
{
@@ -132,26 +215,66 @@ 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:
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);
s->regs[R_CRB_CTRL_START] |= CRB_START_INVOKE;
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:
@@ -192,12 +315,25 @@ 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 */
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)
@@ -216,17 +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)
@@ -235,6 +422,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));
@@ -258,6 +446,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,
@@ -268,8 +458,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);
@@ -279,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");
@@ -288,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));
@@ -299,6 +498,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));
@@ -309,12 +511,25 @@ 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);
if (s->migration_blocker) {
migrate_del_blocker(&s->migration_blocker);
}
}
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;

View File

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

View File

@@ -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

View File

@@ -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,
};

View File

@@ -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
@@ -401,7 +401,8 @@ 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-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,

View File

@@ -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 */

View File

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

View File

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

View File

@@ -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;
}

View File

@@ -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)
{
@@ -98,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);
}
@@ -166,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 |
@@ -179,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);
}
}
@@ -199,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);
}
@@ -234,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);
@@ -249,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);
@@ -264,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 |
@@ -275,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 |
@@ -293,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 |
@@ -312,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 |
@@ -330,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 |
@@ -338,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 |
@@ -349,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 |
@@ -357,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);
@@ -378,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 |
@@ -401,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) {
@@ -411,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);
@@ -432,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);
@@ -453,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 |
@@ -464,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
@@ -475,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;
@@ -508,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);
@@ -532,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, ==,
@@ -557,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);
@@ -582,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);
@@ -592,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)

View File

@@ -0,0 +1,102 @@
/*
* 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"
#include "tpm-emu.h"
uint32_t aspeed_bus_addr;
static uint8_t cur_locty = 0xff;
static void tpm_tis_i2c_set_locty(QTestState *s, uint8_t locty)
{
if (cur_locty != locty) {
cur_locty = locty;
aspeed_i2c_writeb(s, aspeed_bus_addr, I2C_SLAVE_ADDR,
TPM_I2C_REG_LOC_SEL, locty);
}
}
uint8_t tpm_tis_i2c_readb(QTestState *s, uint8_t locty, uint8_t 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(QTestState *s, uint8_t locty, uint8_t reg)
{
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(QTestState *s, uint8_t locty, uint8_t 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(QTestState *s, uint8_t locty, uint8_t reg, uint8_t v)
{
if (reg != TPM_I2C_REG_LOC_SEL) {
tpm_tis_i2c_set_locty(s, locty);
}
aspeed_i2c_writeb(s, aspeed_bus_addr, I2C_SLAVE_ADDR, reg, 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(s, locty);
}
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);
}

View File

@@ -0,0 +1,34 @@
/*
* 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(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(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 */

View File

@@ -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,33 +59,88 @@ 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;
}
if (g_get_monotonic_time() >= end_time) {
break;
}
};
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);
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;
}
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);
}
}
tpm_sts = qtest_readl(s, CRB_ADDR_CTRL_STS);
g_assert_cmpint(tpm_sts & 1, ==, 0);
/*
* 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)
{
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 +151,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 +179,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));
@@ -227,8 +319,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);
@@ -236,8 +328,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,

View File

@@ -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,

View File

@@ -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),