| 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/module.h" |
| 20 | #include "qemu/error-report.h" |
| 21 | #include "qapi/error.h" |
| 22 | #include "system/address-spaces.h" |
| 23 | #include "hw/core/qdev-properties.h" |
| 24 | #include "hw/pci/pci_ids.h" |
| 25 | #include "hw/acpi/tpm.h" |
| 26 | #include "migration/vmstate.h" |
| 27 | #include "migration/blocker.h" |
| 28 | #include "system/tpm_backend.h" |
| 29 | #include "system/tpm_util.h" |
| 30 | #include "system/reset.h" |
| 31 | #include "system/xen.h" |
| 32 | #include "tpm_prop.h" |
| 33 | #include "tpm_ppi.h" |
| 34 | #include "trace.h" |
| 35 | #include "qom/object.h" |
| 36 | |
| 37 | struct CRBState { |
| 38 | DeviceState parent_obj; |
| 39 | |
| 40 | TPMBackend *tpmbe; |
| 41 | TPMBackendCmd cmd; |
| 42 | uint32_t regs[TPM_CRB_R_MAX]; |
| 43 | size_t be_buffer_size; |
| 44 | MemoryRegion mmio; |
| 45 | MemoryRegion cmdmem; |
| 46 | |
| 47 | GByteArray *command_buffer; |
| 48 | GByteArray *response_buffer; |
| 49 | uint32_t response_offset; |
| 50 | |
| 51 | TPMPPI ppi; |
| 52 | |
| 53 | bool cap_chunk; |
| 54 | bool allow_chunk_migration; |
| 55 | Error *migration_blocker; |
| 56 | }; |
| 57 | typedef struct CRBState CRBState; |
| 58 | |
| 59 | DECLARE_INSTANCE_CHECKER(CRBState, CRB, |
| 60 | TYPE_TPM_CRB) |
| 61 | |
| 62 | #define CRB_INTF_TYPE_CRB_ACTIVE 0b1 |
| 63 | #define CRB_INTF_VERSION_CRB 0b1 |
| 64 | #define CRB_INTF_CAP_LOCALITY_0_ONLY 0b0 |
| 65 | #define CRB_INTF_CAP_IDLE_FAST 0b0 |
| 66 | #define CRB_INTF_CAP_XFER_SIZE_64 0b11 |
| 67 | #define CRB_INTF_CAP_FIFO_NOT_SUPPORTED 0b0 |
| 68 | #define CRB_INTF_CAP_CRB_SUPPORTED 0b1 |
| 69 | #define CRB_INTF_IF_SELECTOR_CRB 0b1 |
| 70 | #define CRB_INTF_CAP_CRB_CHUNK 0b1 |
| 71 | |
| 72 | #define CRB_CTRL_CMD_SIZE (TPM_CRB_ADDR_SIZE - A_CRB_DATA_BUFFER) |
| 73 | #define TPM_HEADER_SIZE 10 |
| 74 | |
| 75 | enum crb_loc_ctrl { |
| 76 | CRB_LOC_CTRL_REQUEST_ACCESS = BIT(0), |
| 77 | CRB_LOC_CTRL_RELINQUISH = BIT(1), |
| 78 | CRB_LOC_CTRL_SEIZE = BIT(2), |
| 79 | CRB_LOC_CTRL_RESET_ESTABLISHMENT_BIT = BIT(3), |
| 80 | }; |
| 81 | |
| 82 | enum crb_ctrl_req { |
| 83 | CRB_CTRL_REQ_CMD_READY = BIT(0), |
| 84 | CRB_CTRL_REQ_GO_IDLE = BIT(1), |
| 85 | }; |
| 86 | |
| 87 | enum crb_start { |
| 88 | CRB_START_INVOKE = BIT(0), |
| 89 | CRB_START_RSP_RETRY = BIT(1), |
| 90 | CRB_START_NEXT_CHUNK = BIT(2), |
| 91 | }; |
| 92 | |
| 93 | enum crb_cancel { |
| 94 | CRB_CANCEL_INVOKE = BIT(0), |
| 95 | }; |
| 96 | |
| 97 | #define TPM_CRB_NO_LOCALITY 0xff |
| 98 | |
| 99 | static void tpm_crb_clear_internal_buffers(CRBState *s) |
| 100 | { |
| 101 | g_byte_array_set_size(s->response_buffer, 0); |
| 102 | g_byte_array_set_size(s->command_buffer, 0); |
| 103 | s->response_offset = 0; |
| 104 | } |
| 105 | |
| 106 | static uint64_t tpm_crb_mmio_read(void *opaque, hwaddr addr, |
| 107 | unsigned size) |
| 108 | { |
| 109 | CRBState *s = CRB(opaque); |
| 110 | void *regs = (void *)&s->regs + (addr & ~3); |
| 111 | unsigned offset = addr & 3; |
| 112 | uint32_t val = *(uint32_t *)regs >> (8 * offset); |
| 113 | |
| 114 | switch (addr) { |
| 115 | case A_CRB_LOC_STATE: |
| 116 | val |= !tpm_backend_get_tpm_established_flag(s->tpmbe); |
| 117 | break; |
| 118 | } |
| 119 | |
| 120 | trace_tpm_crb_mmio_read(addr, size, val); |
| 121 | |
| 122 | return val; |
| 123 | } |
| 124 | |
| 125 | static uint8_t tpm_crb_get_active_locty(CRBState *s) |
| 126 | { |
| 127 | if (!ARRAY_FIELD_EX32(s->regs, CRB_LOC_STATE, locAssigned)) { |
| 128 | return TPM_CRB_NO_LOCALITY; |
| 129 | } |
| 130 | return ARRAY_FIELD_EX32(s->regs, CRB_LOC_STATE, activeLocality); |
| 131 | } |
| 132 | |
| 133 | static bool tpm_crb_append_command_request(CRBState *s) |
| 134 | { |
| 135 | /* |
| 136 | * The linux guest writes the TPM command to the MMIO region in chunks. |
| 137 | * This function appends a chunk from the MMIO region to internal |
| 138 | * command_buffer. |
| 139 | */ |
| 140 | void *mem = memory_region_get_ram_ptr(&s->cmdmem); |
| 141 | uint32_t to_copy = 0; |
| 142 | uint32_t total_request_size = 0; |
| 143 | |
| 144 | /* |
| 145 | * The initial call extracts the total TPM command size |
| 146 | * from its header. For the subsequent calls, the data already |
| 147 | * appended in the command_buffer is used to calculate the total |
| 148 | * size, as its header stays the same. |
| 149 | */ |
| 150 | if (s->command_buffer->len == 0) { |
| 151 | total_request_size = tpm_cmd_get_size(mem); |
| 152 | if (total_request_size < TPM_HEADER_SIZE) { |
| 153 | ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS, tpmSts, 1); |
| 154 | ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, Start, 0); |
| 155 | ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, nextChunk, 0); |
| 156 | tpm_crb_clear_internal_buffers(s); |
| 157 | error_report("Command size %" PRIu32 " less than " |
| 158 | "TPM header size %" PRIu32, |
| 159 | total_request_size, (uint32_t)TPM_HEADER_SIZE); |
| 160 | return false; |
| 161 | } |
| 162 | } else { |
| 163 | total_request_size = tpm_cmd_get_size(s->command_buffer->data); |
| 164 | } |
| 165 | total_request_size = MIN(total_request_size, s->be_buffer_size); |
| 166 | |
| 167 | if (total_request_size > s->command_buffer->len) { |
| 168 | uint32_t remaining = total_request_size - s->command_buffer->len; |
| 169 | to_copy = MIN(remaining, CRB_CTRL_CMD_SIZE); |
| 170 | g_byte_array_append(s->command_buffer, (guint8 *)mem, to_copy); |
| 171 | } |
| 172 | return true; |
| 173 | } |
| 174 | |
| 175 | static void tpm_crb_fill_command_response(CRBState *s) |
| 176 | { |
| 177 | /* |
| 178 | * Response from the tpm backend will be stored in the internal |
| 179 | * response_buffer. This function will serve that accumulated response |
| 180 | * to the linux guest in chunks by writing it back to MMIO region. |
| 181 | */ |
| 182 | void *mem = memory_region_get_ram_ptr(&s->cmdmem); |
| 183 | uint32_t remaining = s->response_buffer->len - s->response_offset; |
| 184 | uint32_t to_copy = MIN(CRB_CTRL_CMD_SIZE, remaining); |
| 185 | |
| 186 | memcpy(mem, s->response_buffer->data + s->response_offset, to_copy); |
| 187 | |
| 188 | if (to_copy < CRB_CTRL_CMD_SIZE) { |
| 189 | memset((guint8 *)mem + to_copy, 0, CRB_CTRL_CMD_SIZE - to_copy); |
| 190 | } |
| 191 | |
| 192 | s->response_offset += to_copy; |
| 193 | memory_region_set_dirty(&s->cmdmem, 0, CRB_CTRL_CMD_SIZE); |
| 194 | } |
| 195 | |
| 196 | static void tpm_crb_mmio_write(void *opaque, hwaddr addr, |
| 197 | uint64_t val, unsigned size) |
| 198 | { |
| 199 | CRBState *s = CRB(opaque); |
| 200 | uint8_t locty = addr >> 12; |
| 201 | |
| 202 | trace_tpm_crb_mmio_write(addr, size, val); |
| 203 | |
| 204 | switch (addr) { |
| 205 | case A_CRB_CTRL_REQ: |
| 206 | switch (val) { |
| 207 | case CRB_CTRL_REQ_CMD_READY: |
| 208 | ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS, |
| 209 | tpmIdle, 0); |
| 210 | break; |
| 211 | case CRB_CTRL_REQ_GO_IDLE: |
| 212 | ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS, |
| 213 | tpmIdle, 1); |
| 214 | break; |
| 215 | } |
| 216 | break; |
| 217 | case A_CRB_CTRL_CANCEL: |
| 218 | if (val == CRB_CANCEL_INVOKE) { |
| 219 | if (s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) { |
| 220 | tpm_backend_cancel_cmd(s->tpmbe); |
| 221 | } |
| 222 | tpm_crb_clear_internal_buffers(s); |
| 223 | } |
| 224 | break; |
| 225 | case A_CRB_CTRL_START: |
| 226 | if (tpm_crb_get_active_locty(s) != locty) { |
| 227 | break; |
| 228 | } |
| 229 | if (s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) { |
| 230 | /* |
| 231 | * Backend TPM is busy processing a request. |
| 232 | */ |
| 233 | break; |
| 234 | } |
| 235 | if (val & CRB_START_INVOKE) { |
| 236 | if (!tpm_crb_append_command_request(s)) { |
| 237 | break; |
| 238 | } |
| 239 | ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, Start, 1); |
| 240 | g_byte_array_set_size(s->response_buffer, s->be_buffer_size); |
| 241 | s->cmd = (TPMBackendCmd) { |
| 242 | .in = s->command_buffer->data, |
| 243 | .in_len = s->command_buffer->len, |
| 244 | .out = s->response_buffer->data, |
| 245 | .out_len = s->response_buffer->len, |
| 246 | }; |
| 247 | tpm_backend_deliver_request(s->tpmbe, &s->cmd); |
| 248 | } else if (val & CRB_START_NEXT_CHUNK) { |
| 249 | if (!s->cap_chunk) { |
| 250 | break; |
| 251 | } |
| 252 | /* |
| 253 | * nextChunk is used both while sending and receiving data. |
| 254 | * To distinguish between the two, response_buffer is checked. |
| 255 | * If it does not have data, then that means we have not yet |
| 256 | * sent the command to the tpm backend, and therefore call |
| 257 | * tpm_crb_append_command_request(). |
| 258 | */ |
| 259 | if (s->response_buffer->len > 0 && |
| 260 | s->response_offset < s->response_buffer->len) { |
| 261 | tpm_crb_fill_command_response(s); |
| 262 | } else { |
| 263 | if (!tpm_crb_append_command_request(s)) { |
| 264 | break; |
| 265 | } |
| 266 | } |
| 267 | ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, nextChunk, 0); |
| 268 | } else if (val & CRB_START_RSP_RETRY) { |
| 269 | if (!s->cap_chunk) { |
| 270 | break; |
| 271 | } |
| 272 | if (s->response_buffer->len > 0) { |
| 273 | s->response_offset = 0; |
| 274 | tpm_crb_fill_command_response(s); |
| 275 | } |
| 276 | ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, crbRspRetry, 0); |
| 277 | ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, nextChunk, 0); |
| 278 | } |
| 279 | break; |
| 280 | case A_CRB_LOC_CTRL: |
| 281 | switch (val) { |
| 282 | case CRB_LOC_CTRL_RESET_ESTABLISHMENT_BIT: |
| 283 | /* not loc 3 or 4 */ |
| 284 | break; |
| 285 | case CRB_LOC_CTRL_RELINQUISH: |
| 286 | ARRAY_FIELD_DP32(s->regs, CRB_LOC_STATE, |
| 287 | locAssigned, 0); |
| 288 | ARRAY_FIELD_DP32(s->regs, CRB_LOC_STS, |
| 289 | Granted, 0); |
| 290 | break; |
| 291 | case CRB_LOC_CTRL_REQUEST_ACCESS: |
| 292 | ARRAY_FIELD_DP32(s->regs, CRB_LOC_STS, |
| 293 | Granted, 1); |
| 294 | ARRAY_FIELD_DP32(s->regs, CRB_LOC_STS, |
| 295 | beenSeized, 0); |
| 296 | ARRAY_FIELD_DP32(s->regs, CRB_LOC_STATE, |
| 297 | locAssigned, 1); |
| 298 | break; |
| 299 | } |
| 300 | break; |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | static const MemoryRegionOps tpm_crb_memory_ops = { |
| 305 | .read = tpm_crb_mmio_read, |
| 306 | .write = tpm_crb_mmio_write, |
| 307 | .endianness = DEVICE_LITTLE_ENDIAN, |
| 308 | .valid = { |
| 309 | .min_access_size = 1, |
| 310 | .max_access_size = 4, |
| 311 | }, |
| 312 | }; |
| 313 | |
| 314 | static void tpm_crb_request_completed(TPMIf *ti, int ret) |
| 315 | { |
| 316 | CRBState *s = CRB(ti); |
| 317 | |
| 318 | ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, Start, 0); |
| 319 | if (ret != 0) { |
| 320 | ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS, |
| 321 | tpmSts, 1); /* fatal error */ |
| 322 | tpm_crb_clear_internal_buffers(s); |
| 323 | } else { |
| 324 | uint32_t actual_resp_size = tpm_cmd_get_size(s->response_buffer->data); |
| 325 | uint32_t total_resp_size = MIN(actual_resp_size, s->be_buffer_size); |
| 326 | g_byte_array_set_size(s->response_buffer, total_resp_size); |
| 327 | s->response_offset = 0; |
| 328 | } |
| 329 | /* |
| 330 | * Send the first chunk. Subsequent chunks will be sent |
| 331 | * on receiving nextChunk from the guest |
| 332 | */ |
| 333 | tpm_crb_fill_command_response(s); |
| 334 | ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, nextChunk, 0); |
| 335 | ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, crbRspRetry, 0); |
| 336 | g_byte_array_set_size(s->command_buffer, 0); |
| 337 | } |
| 338 | |
| 339 | static enum TPMVersion tpm_crb_get_version(TPMIf *ti) |
| 340 | { |
| 341 | CRBState *s = CRB(ti); |
| 342 | |
| 343 | return tpm_backend_get_tpm_version(s->tpmbe); |
| 344 | } |
| 345 | |
| 346 | static int tpm_crb_pre_save(void *opaque) |
| 347 | { |
| 348 | CRBState *s = opaque; |
| 349 | |
| 350 | tpm_backend_finish_sync(s->tpmbe); |
| 351 | |
| 352 | return 0; |
| 353 | } |
| 354 | |
| 355 | static bool tpm_crb_chunk_needed(void *opaque) |
| 356 | { |
| 357 | CRBState *s = opaque; |
| 358 | |
| 359 | if (!s->allow_chunk_migration) { |
| 360 | return false; |
| 361 | } |
| 362 | |
| 363 | return ((s->command_buffer && s->command_buffer->len > 0) || |
| 364 | (s->response_buffer && s->response_buffer->len > 0)); |
| 365 | } |
| 366 | |
| 367 | static bool tpm_crb_chunk_post_load(void *opaque, int version_id, Error **errp) |
| 368 | { |
| 369 | CRBState *s = opaque; |
| 370 | |
| 371 | /* |
| 372 | * The external TPM emulator (example swtpm) determines the backend |
| 373 | * buffer capacity (s->be_buffer_size). This check ensures that if we |
| 374 | * migrate from a source with a PQC-enabled emulator that supports |
| 375 | * larger buffers to a destination with a non-PQC emulator, the |
| 376 | * migrated data does not exceed the destination's capacity. |
| 377 | */ |
| 378 | if (s->response_buffer->len > s->be_buffer_size || |
| 379 | s->command_buffer->len > s->be_buffer_size) { |
| 380 | error_setg(errp, "tpm-crb: Buffer sizes exceed backend capacity"); |
| 381 | return false; |
| 382 | } |
| 383 | return true; |
| 384 | } |
| 385 | |
| 386 | static const VMStateDescription vmstate_tpm_crb_chunk = { |
| 387 | .name = "tpm-crb/chunk", |
| 388 | .version_id = 0, |
| 389 | .needed = tpm_crb_chunk_needed, |
| 390 | .post_load_errp = tpm_crb_chunk_post_load, |
| 391 | .fields = (const VMStateField[]) { |
| 392 | VMSTATE_GBYTEARRAY(command_buffer, CRBState, 0), |
| 393 | VMSTATE_GBYTEARRAY(response_buffer, CRBState, 0), |
| 394 | VMSTATE_UINT32(response_offset, CRBState), |
| 395 | VMSTATE_END_OF_LIST() |
| 396 | } |
| 397 | }; |
| 398 | |
| 399 | static const VMStateDescription vmstate_tpm_crb = { |
| 400 | .name = "tpm-crb", |
| 401 | .pre_save = tpm_crb_pre_save, |
| 402 | .fields = (const VMStateField[]) { |
| 403 | VMSTATE_UINT32_ARRAY(regs, CRBState, TPM_CRB_R_MAX), |
| 404 | VMSTATE_END_OF_LIST(), |
| 405 | }, |
| 406 | .subsections = (const VMStateDescription * const []) { |
| 407 | &vmstate_tpm_crb_chunk, |
| 408 | NULL, |
| 409 | } |
| 410 | }; |
| 411 | |
| 412 | static const Property tpm_crb_properties[] = { |
| 413 | DEFINE_PROP_TPMBE("tpmdev", CRBState, tpmbe), |
| 414 | DEFINE_PROP_BOOL("cap-chunk", CRBState, cap_chunk, true), |
| 415 | DEFINE_PROP_BOOL("x-allow-chunk-migration", CRBState, |
| 416 | allow_chunk_migration, true), |
| 417 | }; |
| 418 | |
| 419 | static void tpm_crb_reset(void *dev) |
| 420 | { |
| 421 | CRBState *s = CRB(dev); |
| 422 | |
| 423 | tpm_ppi_reset(&s->ppi); |
| 424 | tpm_backend_reset(s->tpmbe); |
| 425 | tpm_crb_clear_internal_buffers(s); |
| 426 | |
| 427 | memset(s->regs, 0, sizeof(s->regs)); |
| 428 | |
| 429 | ARRAY_FIELD_DP32(s->regs, CRB_LOC_STATE, |
| 430 | tpmRegValidSts, 1); |
| 431 | ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS, |
| 432 | tpmIdle, 1); |
| 433 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, |
| 434 | InterfaceType, CRB_INTF_TYPE_CRB_ACTIVE); |
| 435 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, |
| 436 | InterfaceVersion, CRB_INTF_VERSION_CRB); |
| 437 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, |
| 438 | CapLocality, CRB_INTF_CAP_LOCALITY_0_ONLY); |
| 439 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, |
| 440 | CapCRBIdleBypass, CRB_INTF_CAP_IDLE_FAST); |
| 441 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, |
| 442 | CapDataXferSizeSupport, CRB_INTF_CAP_XFER_SIZE_64); |
| 443 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, |
| 444 | CapFIFO, CRB_INTF_CAP_FIFO_NOT_SUPPORTED); |
| 445 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, |
| 446 | CapCRB, CRB_INTF_CAP_CRB_SUPPORTED); |
| 447 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, |
| 448 | InterfaceSelector, CRB_INTF_IF_SELECTOR_CRB); |
| 449 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, |
| 450 | CapCRBChunk, s->cap_chunk ? CRB_INTF_CAP_CRB_CHUNK : 0); |
| 451 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, |
| 452 | RID, 0b0000); |
| 453 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID2, |
| 454 | VID, PCI_VENDOR_ID_IBM); |
| 455 | |
| 456 | s->regs[R_CRB_CTRL_CMD_SIZE] = CRB_CTRL_CMD_SIZE; |
| 457 | s->regs[R_CRB_CTRL_CMD_LADDR] = TPM_CRB_ADDR_BASE + A_CRB_DATA_BUFFER; |
| 458 | s->regs[R_CRB_CTRL_RSP_SIZE] = CRB_CTRL_CMD_SIZE; |
| 459 | s->regs[R_CRB_CTRL_RSP_ADDR] = TPM_CRB_ADDR_BASE + A_CRB_DATA_BUFFER; |
| 460 | |
| 461 | s->be_buffer_size = tpm_backend_get_buffer_size(s->tpmbe); |
| 462 | |
| 463 | if (tpm_backend_startup_tpm(s->tpmbe, s->be_buffer_size) < 0) { |
| 464 | exit(1); |
| 465 | } |
| 466 | } |
| 467 | |
| 468 | static void tpm_crb_realize(DeviceState *dev, Error **errp) |
| 469 | { |
| 470 | CRBState *s = CRB(dev); |
| 471 | int ret; |
| 472 | |
| 473 | if (!tpm_find()) { |
| 474 | error_setg(errp, "at most one TPM device is permitted"); |
| 475 | return; |
| 476 | } |
| 477 | if (!s->tpmbe) { |
| 478 | error_setg(errp, "'tpmdev' property is required"); |
| 479 | return; |
| 480 | } |
| 481 | if (s->cap_chunk && !s->allow_chunk_migration) { |
| 482 | error_setg(&s->migration_blocker, |
| 483 | "The tpm-crb device does not support chunk migration with " |
| 484 | "machine version less than 11.1"); |
| 485 | ret = migrate_add_blocker_normal(&s->migration_blocker, errp); |
| 486 | if (ret < 0) { |
| 487 | return; |
| 488 | } |
| 489 | } |
| 490 | |
| 491 | memory_region_init_io(&s->mmio, OBJECT(s), &tpm_crb_memory_ops, s, |
| 492 | "tpm-crb-mmio", sizeof(s->regs)); |
| 493 | memory_region_init_ram(&s->cmdmem, OBJECT(s), |
| 494 | "tpm-crb-cmd", CRB_CTRL_CMD_SIZE, errp); |
| 495 | |
| 496 | memory_region_add_subregion(get_system_memory(), |
| 497 | TPM_CRB_ADDR_BASE, &s->mmio); |
| 498 | memory_region_add_subregion(get_system_memory(), |
| 499 | TPM_CRB_ADDR_BASE + sizeof(s->regs), &s->cmdmem); |
| 500 | |
| 501 | s->command_buffer = g_byte_array_new(); |
| 502 | s->response_buffer = g_byte_array_new(); |
| 503 | |
| 504 | tpm_ppi_init(&s->ppi, get_system_memory(), |
| 505 | TPM_PPI_ADDR_BASE, OBJECT(s)); |
| 506 | |
| 507 | if (xen_enabled()) { |
| 508 | tpm_crb_reset(dev); |
| 509 | } else { |
| 510 | qemu_register_reset(tpm_crb_reset, dev); |
| 511 | } |
| 512 | } |
| 513 | |
| 514 | static void tpm_crb_unrealize(DeviceState *dev) |
| 515 | { |
| 516 | CRBState *s = CRB(dev); |
| 517 | |
| 518 | g_clear_pointer(&s->command_buffer, g_byte_array_unref); |
| 519 | g_clear_pointer(&s->response_buffer, g_byte_array_unref); |
| 520 | |
| 521 | if (s->migration_blocker) { |
| 522 | migrate_del_blocker(&s->migration_blocker); |
| 523 | } |
| 524 | } |
| 525 | |
| 526 | static void tpm_crb_class_init(ObjectClass *klass, const void *data) |
| 527 | { |
| 528 | DeviceClass *dc = DEVICE_CLASS(klass); |
| 529 | TPMIfClass *tc = TPM_IF_CLASS(klass); |
| 530 | |
| 531 | dc->realize = tpm_crb_realize; |
| 532 | dc->unrealize = tpm_crb_unrealize; |
| 533 | device_class_set_props(dc, tpm_crb_properties); |
| 534 | dc->vmsd = &vmstate_tpm_crb; |
| 535 | dc->user_creatable = true; |
| 536 | tc->model = TPM_MODEL_TPM_CRB; |
| 537 | tc->ppi_enabled = true; |
| 538 | tc->get_version = tpm_crb_get_version; |
| 539 | tc->request_completed = tpm_crb_request_completed; |
| 540 | |
| 541 | set_bit(DEVICE_CATEGORY_MISC, dc->categories); |
| 542 | } |
| 543 | |
| 544 | static const TypeInfo tpm_crb_info = { |
| 545 | .name = TYPE_TPM_CRB, |
| 546 | /* could be TYPE_SYS_BUS_DEVICE (or LPC etc) */ |
| 547 | .parent = TYPE_DEVICE, |
| 548 | .instance_size = sizeof(CRBState), |
| 549 | .class_init = tpm_crb_class_init, |
| 550 | .interfaces = (const InterfaceInfo[]) { |
| 551 | { TYPE_TPM_IF }, |
| 552 | { } |
| 553 | } |
| 554 | }; |
| 555 | |
| 556 | static void tpm_crb_register(void) |
| 557 | { |
| 558 | type_register_static(&tpm_crb_info); |
| 559 | } |
| 560 | |
| 561 | type_init(tpm_crb_register) |