qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

tpm: add CRB device

tpm_crb is a device for TPM 2.0 Command Response Buffer (CRB)
Interface as defined in TCG PC Client Platform TPM Profile (PTP)
Specification Family “2.0” Level 00 Revision 01.03 v22.

The PTP allows device implementation to switch between TIS and CRB
model at run time, but given that CRB is a simpler device to
implement, I chose to implement it as a different device.

The device doesn't implement other locality than 0 for now (my laptop
TPM doesn't either, so I assume this isn't so bad)

Tested with some success with Linux upstream and Windows 10, seabios &
modified ovmf. The device is recognized and correctly transmit
command/response with passthrough & emu. However, we are missing PPI
ACPI part atm.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>

authored by

Marc-André Lureau and committed by
Stefan Berger
4ab6cb4c 6a8a2354

+666 -10
+1
default-configs/i386-softmmu.mak
··· 37 37 CONFIG_I8259=y 38 38 CONFIG_PFLASH_CFI01=y 39 39 CONFIG_TPM_TIS=$(CONFIG_TPM) 40 + CONFIG_TPM_CRB=$(CONFIG_TPM) 40 41 CONFIG_MC146818RTC=y 41 42 CONFIG_PCI_PIIX=y 42 43 CONFIG_WDT_IB700=y
+1
default-configs/x86_64-softmmu.mak
··· 37 37 CONFIG_I8259=y 38 38 CONFIG_PFLASH_CFI01=y 39 39 CONFIG_TPM_TIS=$(CONFIG_TPM) 40 + CONFIG_TPM_CRB=$(CONFIG_TPM) 40 41 CONFIG_MC146818RTC=y 41 42 CONFIG_PCI_PIIX=y 42 43 CONFIG_WDT_IB700=y
+26 -8
hw/i386/acpi-build.c
··· 2224 2224 aml_append(sb_scope, scope); 2225 2225 } 2226 2226 } 2227 + 2228 + if (TPM_IS_CRB(tpm_find())) { 2229 + dev = aml_device("TPM"); 2230 + aml_append(dev, aml_name_decl("_HID", aml_string("MSFT0101"))); 2231 + crs = aml_resource_template(); 2232 + aml_append(crs, aml_memory32_fixed(TPM_CRB_ADDR_BASE, 2233 + TPM_CRB_ADDR_SIZE, AML_READ_WRITE)); 2234 + aml_append(dev, aml_name_decl("_CRS", crs)); 2235 + 2236 + method = aml_method("_STA", 0, AML_NOTSERIALIZED); 2237 + aml_append(method, aml_return(aml_int(0x0f))); 2238 + aml_append(dev, method); 2239 + 2240 + aml_append(sb_scope, dev); 2241 + } 2242 + 2227 2243 aml_append(dsdt, sb_scope); 2228 2244 2229 2245 /* copy AML table into ACPI tables blob and patch header there */ ··· 2285 2301 if (TPM_IS_TIS(tpm_find())) { 2286 2302 tpm2_ptr->control_area_address = cpu_to_le64(0); 2287 2303 tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_MMIO); 2288 - 2289 - tpm2_ptr->log_area_minimum_length = 2290 - cpu_to_le32(TPM_LOG_AREA_MINIMUM_SIZE); 2291 - 2292 - /* log area start address to be filled by Guest linker */ 2293 - bios_linker_loader_add_pointer(linker, 2294 - ACPI_BUILD_TABLE_FILE, log_addr_offset, log_addr_size, 2295 - ACPI_BUILD_TPMLOG_FILE, 0); 2304 + } else if (TPM_IS_CRB(tpm_find())) { 2305 + tpm2_ptr->control_area_address = cpu_to_le64(TPM_CRB_ADDR_CTRL); 2306 + tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_CRB); 2296 2307 } else { 2297 2308 g_warn_if_reached(); 2298 2309 } 2299 2310 2311 + tpm2_ptr->log_area_minimum_length = 2312 + cpu_to_le32(TPM_LOG_AREA_MINIMUM_SIZE); 2313 + 2314 + /* log area start address to be filled by Guest linker */ 2315 + bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, 2316 + log_addr_offset, log_addr_size, 2317 + ACPI_BUILD_TPMLOG_FILE, 0); 2300 2318 build_header(linker, table_data, 2301 2319 (void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL, NULL); 2302 2320 }
+1
hw/tpm/Makefile.objs
··· 1 1 common-obj-y += tpm_util.o 2 2 common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o 3 + common-obj-$(CONFIG_TPM_CRB) += tpm_crb.o 3 4 common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o 4 5 common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o
+303
hw/tpm/tpm_crb.c
··· 1 + /* 2 + * tpm_crb.c - QEMU's TPM CRB interface emulator 3 + * 4 + * Copyright (c) 2018 Red Hat, Inc. 5 + * 6 + * Authors: 7 + * Marc-André Lureau <marcandre.lureau@redhat.com> 8 + * 9 + * This work is licensed under the terms of the GNU GPL, version 2 or later. 10 + * See the COPYING file in the top-level directory. 11 + * 12 + * tpm_crb is a device for TPM 2.0 Command Response Buffer (CRB) Interface 13 + * as defined in TCG PC Client Platform TPM Profile (PTP) Specification 14 + * Family “2.0” Level 00 Revision 01.03 v22 15 + */ 16 + 17 + #include "qemu/osdep.h" 18 + 19 + #include "qemu-common.h" 20 + #include "qapi/error.h" 21 + #include "exec/address-spaces.h" 22 + 23 + #include "hw/qdev-core.h" 24 + #include "hw/qdev-properties.h" 25 + #include "hw/pci/pci_ids.h" 26 + #include "hw/acpi/tpm.h" 27 + #include "migration/vmstate.h" 28 + #include "sysemu/tpm_backend.h" 29 + #include "tpm_int.h" 30 + #include "tpm_util.h" 31 + 32 + typedef struct CRBState { 33 + DeviceState parent_obj; 34 + 35 + TPMBackend *tpmbe; 36 + TPMBackendCmd cmd; 37 + uint32_t regs[TPM_CRB_R_MAX]; 38 + MemoryRegion mmio; 39 + MemoryRegion cmdmem; 40 + 41 + size_t be_buffer_size; 42 + } CRBState; 43 + 44 + #define CRB(obj) OBJECT_CHECK(CRBState, (obj), TYPE_TPM_CRB) 45 + 46 + #define DEBUG_CRB 0 47 + 48 + #define DPRINTF(fmt, ...) do { \ 49 + if (DEBUG_CRB) { \ 50 + printf(fmt, ## __VA_ARGS__); \ 51 + } \ 52 + } while (0) 53 + 54 + #define CRB_INTF_TYPE_CRB_ACTIVE 0b1 55 + #define CRB_INTF_VERSION_CRB 0b1 56 + #define CRB_INTF_CAP_LOCALITY_0_ONLY 0b0 57 + #define CRB_INTF_CAP_IDLE_FAST 0b0 58 + #define CRB_INTF_CAP_XFER_SIZE_64 0b11 59 + #define CRB_INTF_CAP_FIFO_NOT_SUPPORTED 0b0 60 + #define CRB_INTF_CAP_CRB_SUPPORTED 0b1 61 + #define CRB_INTF_IF_SELECTOR_CRB 0b1 62 + 63 + #define CRB_CTRL_CMD_SIZE (TPM_CRB_ADDR_SIZE - A_CRB_DATA_BUFFER) 64 + 65 + enum crb_loc_ctrl { 66 + CRB_LOC_CTRL_REQUEST_ACCESS = BIT(0), 67 + CRB_LOC_CTRL_RELINQUISH = BIT(1), 68 + CRB_LOC_CTRL_SEIZE = BIT(2), 69 + CRB_LOC_CTRL_RESET_ESTABLISHMENT_BIT = BIT(3), 70 + }; 71 + 72 + enum crb_ctrl_req { 73 + CRB_CTRL_REQ_CMD_READY = BIT(0), 74 + CRB_CTRL_REQ_GO_IDLE = BIT(1), 75 + }; 76 + 77 + enum crb_start { 78 + CRB_START_INVOKE = BIT(0), 79 + }; 80 + 81 + enum crb_cancel { 82 + CRB_CANCEL_INVOKE = BIT(0), 83 + }; 84 + 85 + static uint64_t tpm_crb_mmio_read(void *opaque, hwaddr addr, 86 + unsigned size) 87 + { 88 + CRBState *s = CRB(opaque); 89 + void *regs = (void *)&s->regs + (addr & ~3); 90 + unsigned offset = addr & 3; 91 + uint32_t val = *(uint32_t *)regs >> (8 * offset); 92 + 93 + DPRINTF("CRB read 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx32 "\n", 94 + addr, size, val); 95 + return val; 96 + } 97 + 98 + static void tpm_crb_mmio_write(void *opaque, hwaddr addr, 99 + uint64_t val, unsigned size) 100 + { 101 + CRBState *s = CRB(opaque); 102 + DPRINTF("CRB write 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx64 "\n", 103 + addr, size, val); 104 + 105 + switch (addr) { 106 + case A_CRB_CTRL_REQ: 107 + switch (val) { 108 + case CRB_CTRL_REQ_CMD_READY: 109 + ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS, 110 + tpmIdle, 0); 111 + break; 112 + case CRB_CTRL_REQ_GO_IDLE: 113 + ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS, 114 + tpmIdle, 1); 115 + break; 116 + } 117 + break; 118 + case A_CRB_CTRL_CANCEL: 119 + if (val == CRB_CANCEL_INVOKE && 120 + s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) { 121 + tpm_backend_cancel_cmd(s->tpmbe); 122 + } 123 + break; 124 + case A_CRB_CTRL_START: 125 + if (val == CRB_START_INVOKE && 126 + !(s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE)) { 127 + void *mem = memory_region_get_ram_ptr(&s->cmdmem); 128 + 129 + s->regs[R_CRB_CTRL_START] |= CRB_START_INVOKE; 130 + s->cmd = (TPMBackendCmd) { 131 + .in = mem, 132 + .in_len = MIN(tpm_cmd_get_size(mem), s->be_buffer_size), 133 + .out = mem, 134 + .out_len = s->be_buffer_size, 135 + }; 136 + 137 + tpm_backend_deliver_request(s->tpmbe, &s->cmd); 138 + } 139 + break; 140 + case A_CRB_LOC_CTRL: 141 + switch (val) { 142 + case CRB_LOC_CTRL_RESET_ESTABLISHMENT_BIT: 143 + /* not loc 3 or 4 */ 144 + break; 145 + case CRB_LOC_CTRL_RELINQUISH: 146 + break; 147 + case CRB_LOC_CTRL_REQUEST_ACCESS: 148 + ARRAY_FIELD_DP32(s->regs, CRB_LOC_STS, 149 + Granted, 1); 150 + ARRAY_FIELD_DP32(s->regs, CRB_LOC_STS, 151 + beenSeized, 0); 152 + ARRAY_FIELD_DP32(s->regs, CRB_LOC_STATE, 153 + locAssigned, 1); 154 + ARRAY_FIELD_DP32(s->regs, CRB_LOC_STATE, 155 + tpmRegValidSts, 1); 156 + break; 157 + } 158 + break; 159 + } 160 + } 161 + 162 + static const MemoryRegionOps tpm_crb_memory_ops = { 163 + .read = tpm_crb_mmio_read, 164 + .write = tpm_crb_mmio_write, 165 + .endianness = DEVICE_LITTLE_ENDIAN, 166 + .valid = { 167 + .min_access_size = 1, 168 + .max_access_size = 4, 169 + }, 170 + }; 171 + 172 + static void tpm_crb_request_completed(TPMIf *ti, int ret) 173 + { 174 + CRBState *s = CRB(ti); 175 + 176 + s->regs[R_CRB_CTRL_START] &= ~CRB_START_INVOKE; 177 + if (ret != 0) { 178 + ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS, 179 + tpmSts, 1); /* fatal error */ 180 + } 181 + } 182 + 183 + static enum TPMVersion tpm_crb_get_version(TPMIf *ti) 184 + { 185 + CRBState *s = CRB(ti); 186 + 187 + return tpm_backend_get_tpm_version(s->tpmbe); 188 + } 189 + 190 + static int tpm_crb_pre_save(void *opaque) 191 + { 192 + CRBState *s = opaque; 193 + 194 + tpm_backend_finish_sync(s->tpmbe); 195 + 196 + return 0; 197 + } 198 + 199 + static const VMStateDescription vmstate_tpm_crb = { 200 + .name = "tpm-crb", 201 + .pre_save = tpm_crb_pre_save, 202 + .fields = (VMStateField[]) { 203 + VMSTATE_UINT32_ARRAY(regs, CRBState, TPM_CRB_R_MAX), 204 + VMSTATE_END_OF_LIST(), 205 + } 206 + }; 207 + 208 + static Property tpm_crb_properties[] = { 209 + DEFINE_PROP_TPMBE("tpmdev", CRBState, tpmbe), 210 + DEFINE_PROP_END_OF_LIST(), 211 + }; 212 + 213 + static void tpm_crb_realize(DeviceState *dev, Error **errp) 214 + { 215 + CRBState *s = CRB(dev); 216 + 217 + if (!tpm_find()) { 218 + error_setg(errp, "at most one TPM device is permitted"); 219 + return; 220 + } 221 + if (!s->tpmbe) { 222 + error_setg(errp, "'tpmdev' property is required"); 223 + return; 224 + } 225 + 226 + memory_region_init_io(&s->mmio, OBJECT(s), &tpm_crb_memory_ops, s, 227 + "tpm-crb-mmio", sizeof(s->regs)); 228 + memory_region_init_ram(&s->cmdmem, OBJECT(s), 229 + "tpm-crb-cmd", CRB_CTRL_CMD_SIZE, errp); 230 + 231 + memory_region_add_subregion(get_system_memory(), 232 + TPM_CRB_ADDR_BASE, &s->mmio); 233 + memory_region_add_subregion(get_system_memory(), 234 + TPM_CRB_ADDR_BASE + sizeof(s->regs), &s->cmdmem); 235 + 236 + tpm_backend_reset(s->tpmbe); 237 + 238 + ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, 239 + InterfaceType, CRB_INTF_TYPE_CRB_ACTIVE); 240 + ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, 241 + InterfaceVersion, CRB_INTF_VERSION_CRB); 242 + ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, 243 + CapLocality, CRB_INTF_CAP_LOCALITY_0_ONLY); 244 + ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, 245 + CapCRBIdleBypass, CRB_INTF_CAP_IDLE_FAST); 246 + ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, 247 + CapDataXferSizeSupport, CRB_INTF_CAP_XFER_SIZE_64); 248 + ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, 249 + CapFIFO, CRB_INTF_CAP_FIFO_NOT_SUPPORTED); 250 + ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, 251 + CapCRB, CRB_INTF_CAP_CRB_SUPPORTED); 252 + ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, 253 + InterfaceSelector, CRB_INTF_IF_SELECTOR_CRB); 254 + ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, 255 + RID, 0b0000); 256 + ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID2, 257 + VID, PCI_VENDOR_ID_IBM); 258 + 259 + s->regs[R_CRB_CTRL_CMD_SIZE] = CRB_CTRL_CMD_SIZE; 260 + s->regs[R_CRB_CTRL_CMD_LADDR] = TPM_CRB_ADDR_BASE + A_CRB_DATA_BUFFER; 261 + s->regs[R_CRB_CTRL_RSP_SIZE] = CRB_CTRL_CMD_SIZE; 262 + s->regs[R_CRB_CTRL_RSP_ADDR] = TPM_CRB_ADDR_BASE + A_CRB_DATA_BUFFER; 263 + 264 + s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->tpmbe), 265 + CRB_CTRL_CMD_SIZE); 266 + 267 + tpm_backend_startup_tpm(s->tpmbe, s->be_buffer_size); 268 + } 269 + 270 + static void tpm_crb_class_init(ObjectClass *klass, void *data) 271 + { 272 + DeviceClass *dc = DEVICE_CLASS(klass); 273 + TPMIfClass *tc = TPM_IF_CLASS(klass); 274 + 275 + dc->realize = tpm_crb_realize; 276 + dc->props = tpm_crb_properties; 277 + dc->vmsd = &vmstate_tpm_crb; 278 + dc->user_creatable = true; 279 + tc->model = TPM_MODEL_TPM_CRB; 280 + tc->get_version = tpm_crb_get_version; 281 + tc->request_completed = tpm_crb_request_completed; 282 + 283 + set_bit(DEVICE_CATEGORY_MISC, dc->categories); 284 + } 285 + 286 + static const TypeInfo tpm_crb_info = { 287 + .name = TYPE_TPM_CRB, 288 + /* could be TYPE_SYS_BUS_DEVICE (or LPC etc) */ 289 + .parent = TYPE_DEVICE, 290 + .instance_size = sizeof(CRBState), 291 + .class_init = tpm_crb_class_init, 292 + .interfaces = (InterfaceInfo[]) { 293 + { TYPE_TPM_IF }, 294 + { } 295 + } 296 + }; 297 + 298 + static void tpm_crb_register(void) 299 + { 300 + type_register_static(&tpm_crb_info); 301 + } 302 + 303 + type_init(tpm_crb_register)
+51
include/hw/acpi/tpm.h
··· 16 16 #ifndef HW_ACPI_TPM_H 17 17 #define HW_ACPI_TPM_H 18 18 19 + #include "hw/registerfields.h" 20 + 19 21 #define TPM_TIS_ADDR_BASE 0xFED40000 20 22 #define TPM_TIS_ADDR_SIZE 0x5000 21 23 22 24 #define TPM_TIS_IRQ 5 23 25 26 + REG32(CRB_LOC_STATE, 0x00) 27 + FIELD(CRB_LOC_STATE, tpmEstablished, 0, 1) 28 + FIELD(CRB_LOC_STATE, locAssigned, 1, 1) 29 + FIELD(CRB_LOC_STATE, activeLocality, 2, 3) 30 + FIELD(CRB_LOC_STATE, reserved, 5, 2) 31 + FIELD(CRB_LOC_STATE, tpmRegValidSts, 7, 1) 32 + REG32(CRB_LOC_CTRL, 0x08) 33 + REG32(CRB_LOC_STS, 0x0C) 34 + FIELD(CRB_LOC_STS, Granted, 0, 1) 35 + FIELD(CRB_LOC_STS, beenSeized, 1, 1) 36 + REG32(CRB_INTF_ID, 0x30) 37 + FIELD(CRB_INTF_ID, InterfaceType, 0, 4) 38 + FIELD(CRB_INTF_ID, InterfaceVersion, 4, 4) 39 + FIELD(CRB_INTF_ID, CapLocality, 8, 1) 40 + FIELD(CRB_INTF_ID, CapCRBIdleBypass, 9, 1) 41 + FIELD(CRB_INTF_ID, Reserved1, 10, 1) 42 + FIELD(CRB_INTF_ID, CapDataXferSizeSupport, 11, 2) 43 + FIELD(CRB_INTF_ID, CapFIFO, 13, 1) 44 + FIELD(CRB_INTF_ID, CapCRB, 14, 1) 45 + FIELD(CRB_INTF_ID, CapIFRes, 15, 2) 46 + FIELD(CRB_INTF_ID, InterfaceSelector, 17, 2) 47 + FIELD(CRB_INTF_ID, IntfSelLock, 19, 1) 48 + FIELD(CRB_INTF_ID, Reserved2, 20, 4) 49 + FIELD(CRB_INTF_ID, RID, 24, 8) 50 + REG32(CRB_INTF_ID2, 0x34) 51 + FIELD(CRB_INTF_ID2, VID, 0, 16) 52 + FIELD(CRB_INTF_ID2, DID, 16, 16) 53 + REG32(CRB_CTRL_EXT, 0x38) 54 + REG32(CRB_CTRL_REQ, 0x40) 55 + REG32(CRB_CTRL_STS, 0x44) 56 + FIELD(CRB_CTRL_STS, tpmSts, 0, 1) 57 + FIELD(CRB_CTRL_STS, tpmIdle, 1, 1) 58 + REG32(CRB_CTRL_CANCEL, 0x48) 59 + REG32(CRB_CTRL_START, 0x4C) 60 + REG32(CRB_INT_ENABLED, 0x50) 61 + REG32(CRB_INT_STS, 0x54) 62 + REG32(CRB_CTRL_CMD_SIZE, 0x58) 63 + REG32(CRB_CTRL_CMD_LADDR, 0x5C) 64 + REG32(CRB_CTRL_CMD_HADDR, 0x60) 65 + REG32(CRB_CTRL_RSP_SIZE, 0x64) 66 + REG32(CRB_CTRL_RSP_ADDR, 0x68) 67 + REG32(CRB_DATA_BUFFER, 0x80) 68 + 69 + #define TPM_CRB_ADDR_BASE 0xFED40000 70 + #define TPM_CRB_ADDR_SIZE 0x1000 71 + #define TPM_CRB_ADDR_CTRL (TPM_CRB_ADDR_BASE + A_CRB_CTRL_REQ) 72 + #define TPM_CRB_R_MAX R_CRB_DATA_BUFFER 73 + 24 74 #define TPM_LOG_AREA_MINIMUM_SIZE (64 * 1024) 25 75 26 76 #define TPM_TCPA_ACPI_CLASS_CLIENT 0 ··· 30 80 #define TPM2_ACPI_CLASS_SERVER 1 31 81 32 82 #define TPM2_START_METHOD_MMIO 6 83 + #define TPM2_START_METHOD_CRB 7 33 84 34 85 #endif /* HW_ACPI_TPM_H */
+3
include/sysemu/tpm.h
··· 46 46 } TPMIfClass; 47 47 48 48 #define TYPE_TPM_TIS "tpm-tis" 49 + #define TYPE_TPM_CRB "tpm-crb" 49 50 50 51 #define TPM_IS_TIS(chr) \ 51 52 object_dynamic_cast(OBJECT(chr), TYPE_TPM_TIS) 53 + #define TPM_IS_CRB(chr) \ 54 + object_dynamic_cast(OBJECT(chr), TYPE_TPM_CRB) 52 55 53 56 /* returns NULL unless there is exactly one TPM device */ 54 57 static inline TPMIf *tpm_find(void)
+3 -2
qapi/tpm.json
··· 11 11 # An enumeration of TPM models 12 12 # 13 13 # @tpm-tis: TPM TIS model 14 + # @tpm-crb: TPM CRB model (since 2.12) 14 15 # 15 16 # Since: 1.5 16 17 ## 17 - { 'enum': 'TpmModel', 'data': [ 'tpm-tis' ] } 18 + { 'enum': 'TpmModel', 'data': [ 'tpm-tis', 'tpm-crb' ] } 18 19 19 20 ## 20 21 # @query-tpm-models: ··· 28 29 # Example: 29 30 # 30 31 # -> { "execute": "query-tpm-models" } 31 - # <- { "return": [ "tpm-tis" ] } 32 + # <- { "return": [ "tpm-tis", "tpm-crb" ] } 32 33 # 33 34 ## 34 35 { 'command': 'query-tpm-models', 'returns': ['TpmModel'] }
+2
tests/Makefile.include
··· 286 286 ifeq ($(CONFIG_VHOST_USER_NET_TEST_i386),) 287 287 check-qtest-x86_64-$(CONFIG_VHOST_USER_NET_TEST_x86_64) += tests/vhost-user-test$(EXESUF) 288 288 endif 289 + check-qtest-i386-$(CONFIG_TPM) += tests/tpm-crb-test$(EXESUF) 289 290 check-qtest-i386-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF) 290 291 check-qtest-i386-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF) 291 292 check-qtest-i386-$(CONFIG_POSIX) += tests/test-filter-redirector$(EXESUF) ··· 708 709 tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y) 709 710 tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \ 710 711 tests/io-channel-helpers.o $(test-io-obj-y) 712 + tests/tpm-crb-test$(EXESUF): tests/tpm-crb-test.o $(test-io-obj-y) 711 713 tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \ 712 714 tests/io-channel-helpers.o $(test-io-obj-y) 713 715 tests/test-io-channel-tls$(EXESUF): tests/test-io-channel-tls.o \
+275
tests/tpm-crb-test.c
··· 1 + /* 2 + * QTest testcase for TPM CRB 3 + * 4 + * Copyright (c) 2018 Red Hat, Inc. 5 + * 6 + * Authors: 7 + * Marc-André Lureau <marcandre.lureau@redhat.com> 8 + * 9 + * This work is licensed under the terms of the GNU GPL, version 2 or later. 10 + * See the COPYING file in the top-level directory. 11 + */ 12 + 13 + #include "qemu/osdep.h" 14 + #include <glib/gstdio.h> 15 + 16 + #include "hw/acpi/tpm.h" 17 + #include "hw/tpm/tpm_ioctl.h" 18 + #include "io/channel-socket.h" 19 + #include "libqtest.h" 20 + #include "qapi/error.h" 21 + 22 + #define TPM_RC_FAILURE 0x101 23 + #define TPM2_ST_NO_SESSIONS 0x8001 24 + 25 + struct tpm_hdr { 26 + uint16_t tag; 27 + uint32_t len; 28 + uint32_t code; /*ordinal/error */ 29 + char buffer[]; 30 + } QEMU_PACKED; 31 + 32 + typedef struct TestState { 33 + CompatGMutex data_mutex; 34 + CompatGCond data_cond; 35 + SocketAddress *addr; 36 + QIOChannel *tpm_ioc; 37 + GThread *emu_tpm_thread; 38 + struct tpm_hdr *tpm_msg; 39 + } TestState; 40 + 41 + static void test_wait_cond(TestState *s) 42 + { 43 + gint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; 44 + 45 + g_mutex_lock(&s->data_mutex); 46 + if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) { 47 + g_assert_not_reached(); 48 + } 49 + g_mutex_unlock(&s->data_mutex); 50 + } 51 + 52 + static void *emu_tpm_thread(void *data) 53 + { 54 + TestState *s = data; 55 + QIOChannel *ioc = s->tpm_ioc; 56 + 57 + s->tpm_msg = g_new(struct tpm_hdr, 1); 58 + while (true) { 59 + int minhlen = sizeof(s->tpm_msg->tag) + sizeof(s->tpm_msg->len); 60 + 61 + if (!qio_channel_read(ioc, (char *)s->tpm_msg, minhlen, &error_abort)) { 62 + break; 63 + } 64 + s->tpm_msg->tag = be16_to_cpu(s->tpm_msg->tag); 65 + s->tpm_msg->len = be32_to_cpu(s->tpm_msg->len); 66 + g_assert_cmpint(s->tpm_msg->len, >=, minhlen); 67 + g_assert_cmpint(s->tpm_msg->tag, ==, TPM2_ST_NO_SESSIONS); 68 + 69 + s->tpm_msg = g_realloc(s->tpm_msg, s->tpm_msg->len); 70 + qio_channel_read(ioc, (char *)&s->tpm_msg->code, 71 + s->tpm_msg->len - minhlen, &error_abort); 72 + s->tpm_msg->code = be32_to_cpu(s->tpm_msg->code); 73 + 74 + /* reply error */ 75 + s->tpm_msg->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS); 76 + s->tpm_msg->len = cpu_to_be32(sizeof(struct tpm_hdr)); 77 + s->tpm_msg->code = cpu_to_be32(TPM_RC_FAILURE); 78 + qio_channel_write(ioc, (char *)s->tpm_msg, be32_to_cpu(s->tpm_msg->len), 79 + &error_abort); 80 + } 81 + 82 + g_free(s->tpm_msg); 83 + s->tpm_msg = NULL; 84 + object_unref(OBJECT(s->tpm_ioc)); 85 + return NULL; 86 + } 87 + 88 + static void *emu_ctrl_thread(void *data) 89 + { 90 + TestState *s = data; 91 + QIOChannelSocket *lioc = qio_channel_socket_new(); 92 + QIOChannel *ioc; 93 + 94 + qio_channel_socket_listen_sync(lioc, s->addr, &error_abort); 95 + g_cond_signal(&s->data_cond); 96 + 97 + qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN); 98 + ioc = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort)); 99 + g_assert(ioc); 100 + 101 + { 102 + uint32_t cmd = 0; 103 + struct iovec iov = { .iov_base = &cmd, .iov_len = sizeof(cmd) }; 104 + int *pfd = NULL; 105 + size_t nfd = 0; 106 + 107 + qio_channel_readv_full(ioc, &iov, 1, &pfd, &nfd, &error_abort); 108 + cmd = be32_to_cpu(cmd); 109 + g_assert_cmpint(cmd, ==, CMD_SET_DATAFD); 110 + g_assert_cmpint(nfd, ==, 1); 111 + s->tpm_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(*pfd, &error_abort)); 112 + g_free(pfd); 113 + 114 + cmd = 0; 115 + qio_channel_write(ioc, (char *)&cmd, sizeof(cmd), &error_abort); 116 + 117 + s->emu_tpm_thread = g_thread_new(NULL, emu_tpm_thread, s); 118 + } 119 + 120 + while (true) { 121 + uint32_t cmd; 122 + ssize_t ret; 123 + 124 + ret = qio_channel_read(ioc, (char *)&cmd, sizeof(cmd), NULL); 125 + if (ret <= 0) { 126 + break; 127 + } 128 + 129 + cmd = be32_to_cpu(cmd); 130 + switch (cmd) { 131 + case CMD_GET_CAPABILITY: { 132 + ptm_cap cap = cpu_to_be64(0x3fff); 133 + qio_channel_write(ioc, (char *)&cap, sizeof(cap), &error_abort); 134 + break; 135 + } 136 + case CMD_INIT: { 137 + ptm_init init; 138 + qio_channel_read(ioc, (char *)&init.u.req, sizeof(init.u.req), 139 + &error_abort); 140 + init.u.resp.tpm_result = 0; 141 + qio_channel_write(ioc, (char *)&init.u.resp, sizeof(init.u.resp), 142 + &error_abort); 143 + break; 144 + } 145 + case CMD_SHUTDOWN: { 146 + ptm_res res = 0; 147 + qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort); 148 + qio_channel_close(s->tpm_ioc, &error_abort); 149 + g_thread_join(s->emu_tpm_thread); 150 + break; 151 + } 152 + case CMD_STOP: { 153 + ptm_res res = 0; 154 + qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort); 155 + break; 156 + } 157 + case CMD_SET_BUFFERSIZE: { 158 + ptm_setbuffersize sbs; 159 + qio_channel_read(ioc, (char *)&sbs.u.req, sizeof(sbs.u.req), 160 + &error_abort); 161 + sbs.u.resp.buffersize = sbs.u.req.buffersize ?: cpu_to_be32(4096); 162 + sbs.u.resp.tpm_result = 0; 163 + sbs.u.resp.minsize = cpu_to_be32(128); 164 + sbs.u.resp.maxsize = cpu_to_be32(4096); 165 + qio_channel_write(ioc, (char *)&sbs.u.resp, sizeof(sbs.u.resp), 166 + &error_abort); 167 + break; 168 + } 169 + case CMD_SET_LOCALITY: { 170 + ptm_loc loc; 171 + /* Note: this time it's not u.req / u.resp... */ 172 + qio_channel_read(ioc, (char *)&loc, sizeof(loc), &error_abort); 173 + g_assert_cmpint(loc.u.req.loc, ==, 0); 174 + loc.u.resp.tpm_result = 0; 175 + qio_channel_write(ioc, (char *)&loc, sizeof(loc), &error_abort); 176 + break; 177 + } 178 + default: 179 + g_debug("unimplemented %u", cmd); 180 + g_assert_not_reached(); 181 + } 182 + } 183 + 184 + object_unref(OBJECT(ioc)); 185 + object_unref(OBJECT(lioc)); 186 + return NULL; 187 + } 188 + 189 + #define TPM_CMD "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00" 190 + 191 + static void tpm_crb_test(const void *data) 192 + { 193 + const TestState *s = data; 194 + uint32_t intfid = readl(TPM_CRB_ADDR_BASE + A_CRB_INTF_ID); 195 + uint32_t csize = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_SIZE); 196 + uint64_t caddr = readq(TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR); 197 + uint32_t rsize = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_SIZE); 198 + uint64_t raddr = readq(TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR); 199 + 200 + g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceType), ==, 1); 201 + g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceVersion), ==, 1); 202 + g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapLocality), ==, 0); 203 + g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapCRBIdleBypass), ==, 0); 204 + g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapDataXferSizeSupport), 205 + ==, 3); 206 + g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapFIFO), ==, 0); 207 + g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapCRB), ==, 1); 208 + g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceSelector), ==, 1); 209 + g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, RID), ==, 0); 210 + 211 + g_assert_cmpint(csize, >=, 128); 212 + g_assert_cmpint(rsize, >=, 128); 213 + g_assert_cmpint(caddr, >, TPM_CRB_ADDR_BASE); 214 + g_assert_cmpint(raddr, >, TPM_CRB_ADDR_BASE); 215 + 216 + memwrite(caddr, TPM_CMD, sizeof(TPM_CMD)); 217 + 218 + uint32_t sts, start = 1; 219 + uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; 220 + writel(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start); 221 + do { 222 + start = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); 223 + if ((start & 1) == 0) { 224 + break; 225 + } 226 + } while (g_get_monotonic_time() < end_time); 227 + start = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); 228 + g_assert_cmpint(start & 1, ==, 0); 229 + sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); 230 + g_assert_cmpint(sts & 1, ==, 0); 231 + 232 + struct tpm_hdr tpm_msg; 233 + memread(raddr, &tpm_msg, sizeof(tpm_msg)); 234 + g_assert_cmpmem(&tpm_msg, sizeof(tpm_msg), s->tpm_msg, sizeof(*s->tpm_msg)); 235 + } 236 + 237 + int main(int argc, char **argv) 238 + { 239 + int ret; 240 + char *args, *tmp_path = g_dir_make_tmp("qemu-tpm-crb-test.XXXXXX", NULL); 241 + GThread *thread; 242 + TestState test; 243 + 244 + module_call_init(MODULE_INIT_QOM); 245 + g_test_init(&argc, &argv, NULL); 246 + 247 + test.addr = g_new0(SocketAddress, 1); 248 + test.addr->type = SOCKET_ADDRESS_TYPE_UNIX; 249 + test.addr->u.q_unix.path = g_build_filename(tmp_path, "sock", NULL); 250 + g_mutex_init(&test.data_mutex); 251 + g_cond_init(&test.data_cond); 252 + 253 + thread = g_thread_new(NULL, emu_ctrl_thread, &test); 254 + test_wait_cond(&test); 255 + 256 + args = g_strdup_printf( 257 + "-chardev socket,id=chr,path=%s " 258 + "-tpmdev emulator,id=dev,chardev=chr " 259 + "-device tpm-crb,tpmdev=dev", 260 + test.addr->u.q_unix.path); 261 + qtest_start(args); 262 + 263 + qtest_add_data_func("/tpm-crb/test", &test, tpm_crb_test); 264 + ret = g_test_run(); 265 + 266 + qtest_end(); 267 + 268 + g_thread_join(thread); 269 + g_unlink(test.addr->u.q_unix.path); 270 + qapi_free_SocketAddress(test.addr); 271 + g_rmdir(tmp_path); 272 + g_free(tmp_path); 273 + g_free(args); 274 + return ret; 275 + }