diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c index 75c33d290e..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; } @@ -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( diff --git a/hw/core/machine.c b/hw/core/machine.c index 63baff859f..17970b78b6 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -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); diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c index 02701ab948..54fa2042b5 100644 --- a/hw/tpm/tpm_crb.c +++ b/hw/tpm/tpm_crb.c @@ -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; 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) 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, +}; diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 728dde54b3..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 @@ -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, 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 */ 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-tests.c b/tests/qtest/tpm-tests.c index 197714f8d9..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); @@ -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-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 + * + */ + +#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-test.c b/tests/qtest/tpm-tis-i2c-test.c index 3a1af026f2..f614f888f3 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) { @@ -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) diff --git a/tests/qtest/tpm-tis-i2c-util.c b/tests/qtest/tpm-tis-i2c-util.c new file mode 100644 index 0000000000..c73420ec5b --- /dev/null +++ b/tests/qtest/tpm-tis-i2c-util.c @@ -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 + * + */ + +#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); +} diff --git a/tests/qtest/tpm-tis-i2c-util.h b/tests/qtest/tpm-tis-i2c-util.h new file mode 100644 index 0000000000..499bf4964a --- /dev/null +++ b/tests/qtest/tpm-tis-i2c-util.h @@ -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 + * + */ + +#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 */ diff --git a/tests/qtest/tpm-util.c b/tests/qtest/tpm-util.c index 2cb2dd4796..151002ea24 100644 --- a/tests/qtest/tpm-util.c +++ b/tests/qtest/tpm-util.c @@ -14,16 +14,44 @@ #include "qemu/osdep.h" #include +#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, 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, 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),