1 /* 2 * CXL Utility library for mailbox interface 3 * 4 * Copyright(C) 2020 Intel Corporation. 5 * 6 * This work is licensed under the terms of the GNU GPL, version 2. See the 7 * COPYING file in the top-level directory. 8 */ 9 10 #include "qemu/osdep.h" 11 #include "hw/cxl/cxl.h" 12 #include "hw/cxl/cxl_events.h" 13 #include "hw/pci/pci.h" 14 #include "qemu/cutils.h" 15 #include "qemu/log.h" 16 #include "qemu/units.h" 17 #include "qemu/uuid.h" 18 19 #define CXL_CAPACITY_MULTIPLIER (256 * MiB) 20 21 /* 22 * How to add a new command, example. The command set FOO, with cmd BAR. 23 * 1. Add the command set and cmd to the enum. 24 * FOO = 0x7f, 25 * #define BAR 0 26 * 2. Implement the handler 27 * static CXLRetCode cmd_foo_bar(struct cxl_cmd *cmd, 28 * CXLDeviceState *cxl_dstate, uint16_t *len) 29 * 3. Add the command to the cxl_cmd_set[][] 30 * [FOO][BAR] = { "FOO_BAR", cmd_foo_bar, x, y }, 31 * 4. Implement your handler 32 * define_mailbox_handler(FOO_BAR) { ... return CXL_MBOX_SUCCESS; } 33 * 34 * 35 * Writing the handler: 36 * The handler will provide the &struct cxl_cmd, the &CXLDeviceState, and the 37 * in/out length of the payload. The handler is responsible for consuming the 38 * payload from cmd->payload and operating upon it as necessary. It must then 39 * fill the output data into cmd->payload (overwriting what was there), 40 * setting the length, and returning a valid return code. 41 * 42 * XXX: The handler need not worry about endianness. The payload is read out of 43 * a register interface that already deals with it. 44 */ 45 46 enum { 47 EVENTS = 0x01, 48 #define GET_RECORDS 0x0 49 #define CLEAR_RECORDS 0x1 50 #define GET_INTERRUPT_POLICY 0x2 51 #define SET_INTERRUPT_POLICY 0x3 52 FIRMWARE_UPDATE = 0x02, 53 #define GET_INFO 0x0 54 TIMESTAMP = 0x03, 55 #define GET 0x0 56 #define SET 0x1 57 LOGS = 0x04, 58 #define GET_SUPPORTED 0x0 59 #define GET_LOG 0x1 60 IDENTIFY = 0x40, 61 #define MEMORY_DEVICE 0x0 62 CCLS = 0x41, 63 #define GET_PARTITION_INFO 0x0 64 #define GET_LSA 0x2 65 #define SET_LSA 0x3 66 MEDIA_AND_POISON = 0x43, 67 #define GET_POISON_LIST 0x0 68 #define INJECT_POISON 0x1 69 #define CLEAR_POISON 0x2 70 }; 71 72 73 static CXLRetCode cmd_events_get_records(const struct cxl_cmd *cmd, 74 uint8_t *payload_in, size_t len_in, 75 uint8_t *payload_out, size_t *len_out, 76 CXLCCI *cci) 77 { 78 CXLDeviceState *cxlds = &CXL_TYPE3(cci->d)->cxl_dstate; 79 CXLGetEventPayload *pl; 80 uint8_t log_type; 81 int max_recs; 82 83 if (cmd->in < sizeof(log_type)) { 84 return CXL_MBOX_INVALID_INPUT; 85 } 86 87 log_type = payload_in[0]; 88 89 pl = (CXLGetEventPayload *)payload_out; 90 memset(pl, 0, sizeof(*pl)); 91 92 max_recs = (cxlds->payload_size - CXL_EVENT_PAYLOAD_HDR_SIZE) / 93 CXL_EVENT_RECORD_SIZE; 94 if (max_recs > 0xFFFF) { 95 max_recs = 0xFFFF; 96 } 97 98 return cxl_event_get_records(cxlds, pl, log_type, max_recs, len_out); 99 } 100 101 static CXLRetCode cmd_events_clear_records(const struct cxl_cmd *cmd, 102 uint8_t *payload_in, 103 size_t len_in, 104 uint8_t *payload_out, 105 size_t *len_out, 106 CXLCCI *cci) 107 { 108 CXLDeviceState *cxlds = &CXL_TYPE3(cci->d)->cxl_dstate; 109 CXLClearEventPayload *pl; 110 111 pl = (CXLClearEventPayload *)payload_in; 112 *len_out = 0; 113 return cxl_event_clear_records(cxlds, pl); 114 } 115 116 static CXLRetCode cmd_events_get_interrupt_policy(const struct cxl_cmd *cmd, 117 uint8_t *payload_in, 118 size_t len_in, 119 uint8_t *payload_out, 120 size_t *len_out, 121 CXLCCI *cci) 122 { 123 CXLDeviceState *cxlds = &CXL_TYPE3(cci->d)->cxl_dstate; 124 CXLEventInterruptPolicy *policy; 125 CXLEventLog *log; 126 127 policy = (CXLEventInterruptPolicy *)payload_out; 128 memset(policy, 0, sizeof(*policy)); 129 130 log = &cxlds->event_logs[CXL_EVENT_TYPE_INFO]; 131 if (log->irq_enabled) { 132 policy->info_settings = CXL_EVENT_INT_SETTING(log->irq_vec); 133 } 134 135 log = &cxlds->event_logs[CXL_EVENT_TYPE_WARN]; 136 if (log->irq_enabled) { 137 policy->warn_settings = CXL_EVENT_INT_SETTING(log->irq_vec); 138 } 139 140 log = &cxlds->event_logs[CXL_EVENT_TYPE_FAIL]; 141 if (log->irq_enabled) { 142 policy->failure_settings = CXL_EVENT_INT_SETTING(log->irq_vec); 143 } 144 145 log = &cxlds->event_logs[CXL_EVENT_TYPE_FATAL]; 146 if (log->irq_enabled) { 147 policy->fatal_settings = CXL_EVENT_INT_SETTING(log->irq_vec); 148 } 149 150 log = &cxlds->event_logs[CXL_EVENT_TYPE_DYNAMIC_CAP]; 151 if (log->irq_enabled) { 152 /* Dynamic Capacity borrows the same vector as info */ 153 policy->dyn_cap_settings = CXL_INT_MSI_MSIX; 154 } 155 156 *len_out = sizeof(*policy); 157 return CXL_MBOX_SUCCESS; 158 } 159 160 static CXLRetCode cmd_events_set_interrupt_policy(const struct cxl_cmd *cmd, 161 uint8_t *payload_in, 162 size_t len_in, 163 uint8_t *payload_out, 164 size_t *len_out, 165 CXLCCI *cci) 166 { 167 CXLDeviceState *cxlds = &CXL_TYPE3(cci->d)->cxl_dstate; 168 CXLEventInterruptPolicy *policy; 169 CXLEventLog *log; 170 171 if (len_in < CXL_EVENT_INT_SETTING_MIN_LEN) { 172 return CXL_MBOX_INVALID_PAYLOAD_LENGTH; 173 } 174 175 policy = (CXLEventInterruptPolicy *)payload_in; 176 177 log = &cxlds->event_logs[CXL_EVENT_TYPE_INFO]; 178 log->irq_enabled = (policy->info_settings & CXL_EVENT_INT_MODE_MASK) == 179 CXL_INT_MSI_MSIX; 180 181 log = &cxlds->event_logs[CXL_EVENT_TYPE_WARN]; 182 log->irq_enabled = (policy->warn_settings & CXL_EVENT_INT_MODE_MASK) == 183 CXL_INT_MSI_MSIX; 184 185 log = &cxlds->event_logs[CXL_EVENT_TYPE_FAIL]; 186 log->irq_enabled = (policy->failure_settings & CXL_EVENT_INT_MODE_MASK) == 187 CXL_INT_MSI_MSIX; 188 189 log = &cxlds->event_logs[CXL_EVENT_TYPE_FATAL]; 190 log->irq_enabled = (policy->fatal_settings & CXL_EVENT_INT_MODE_MASK) == 191 CXL_INT_MSI_MSIX; 192 193 /* DCD is optional */ 194 if (len_in < sizeof(*policy)) { 195 return CXL_MBOX_SUCCESS; 196 } 197 198 log = &cxlds->event_logs[CXL_EVENT_TYPE_DYNAMIC_CAP]; 199 log->irq_enabled = (policy->dyn_cap_settings & CXL_EVENT_INT_MODE_MASK) == 200 CXL_INT_MSI_MSIX; 201 202 *len_out = 0; 203 return CXL_MBOX_SUCCESS; 204 } 205 206 /* 8.2.9.2.1 */ 207 static CXLRetCode cmd_firmware_update_get_info(const struct cxl_cmd *cmd, 208 uint8_t *payload_in, 209 size_t len, 210 uint8_t *payload_out, 211 size_t *len_out, 212 CXLCCI *cci) 213 { 214 CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; 215 struct { 216 uint8_t slots_supported; 217 uint8_t slot_info; 218 uint8_t caps; 219 uint8_t rsvd[0xd]; 220 char fw_rev1[0x10]; 221 char fw_rev2[0x10]; 222 char fw_rev3[0x10]; 223 char fw_rev4[0x10]; 224 } QEMU_PACKED *fw_info; 225 QEMU_BUILD_BUG_ON(sizeof(*fw_info) != 0x50); 226 227 if ((cxl_dstate->vmem_size < CXL_CAPACITY_MULTIPLIER) || 228 (cxl_dstate->pmem_size < CXL_CAPACITY_MULTIPLIER)) { 229 return CXL_MBOX_INTERNAL_ERROR; 230 } 231 232 fw_info = (void *)payload_out; 233 memset(fw_info, 0, sizeof(*fw_info)); 234 235 fw_info->slots_supported = 2; 236 fw_info->slot_info = BIT(0) | BIT(3); 237 fw_info->caps = 0; 238 pstrcpy(fw_info->fw_rev1, sizeof(fw_info->fw_rev1), "BWFW VERSION 0"); 239 240 *len_out = sizeof(*fw_info); 241 return CXL_MBOX_SUCCESS; 242 } 243 244 /* 8.2.9.3.1 */ 245 static CXLRetCode cmd_timestamp_get(const struct cxl_cmd *cmd, 246 uint8_t *payload_in, 247 size_t len_in, 248 uint8_t *payload_out, 249 size_t *len_out, 250 CXLCCI *cci) 251 { 252 CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; 253 uint64_t final_time = cxl_device_get_timestamp(cxl_dstate); 254 255 stq_le_p(payload_out, final_time); 256 *len_out = 8; 257 258 return CXL_MBOX_SUCCESS; 259 } 260 261 /* 8.2.9.3.2 */ 262 static CXLRetCode cmd_timestamp_set(const struct cxl_cmd *cmd, 263 uint8_t *payload_in, 264 size_t len_in, 265 uint8_t *payload_out, 266 size_t *len_out, 267 CXLCCI *cci) 268 { 269 CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; 270 271 cxl_dstate->timestamp.set = true; 272 cxl_dstate->timestamp.last_set = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 273 274 cxl_dstate->timestamp.host_set = le64_to_cpu(*(uint64_t *)payload_in); 275 276 *len_out = 0; 277 return CXL_MBOX_SUCCESS; 278 } 279 280 /* CXL 3.0 8.2.9.5.2.1 Command Effects Log (CEL) */ 281 static const QemuUUID cel_uuid = { 282 .data = UUID(0x0da9c0b5, 0xbf41, 0x4b78, 0x8f, 0x79, 283 0x96, 0xb1, 0x62, 0x3b, 0x3f, 0x17) 284 }; 285 286 /* 8.2.9.4.1 */ 287 static CXLRetCode cmd_logs_get_supported(const struct cxl_cmd *cmd, 288 uint8_t *payload_in, 289 size_t len_in, 290 uint8_t *payload_out, 291 size_t *len_out, 292 CXLCCI *cci) 293 { 294 struct { 295 uint16_t entries; 296 uint8_t rsvd[6]; 297 struct { 298 QemuUUID uuid; 299 uint32_t size; 300 } log_entries[1]; 301 } QEMU_PACKED *supported_logs = (void *)payload_out; 302 QEMU_BUILD_BUG_ON(sizeof(*supported_logs) != 0x1c); 303 304 supported_logs->entries = 1; 305 supported_logs->log_entries[0].uuid = cel_uuid; 306 supported_logs->log_entries[0].size = 4 * cci->cel_size; 307 308 *len_out = sizeof(*supported_logs); 309 return CXL_MBOX_SUCCESS; 310 } 311 312 /* 8.2.9.4.2 */ 313 static CXLRetCode cmd_logs_get_log(const struct cxl_cmd *cmd, 314 uint8_t *payload_in, 315 size_t len_in, 316 uint8_t *payload_out, 317 size_t *len_out, 318 CXLCCI *cci) 319 { 320 struct { 321 QemuUUID uuid; 322 uint32_t offset; 323 uint32_t length; 324 } QEMU_PACKED QEMU_ALIGNED(16) *get_log; 325 326 get_log = (void *)payload_in; 327 328 /* 329 * 8.2.9.4.2 330 * The device shall return Invalid Parameter if the Offset or Length 331 * fields attempt to access beyond the size of the log as reported by Get 332 * Supported Logs. 333 * 334 * XXX: Spec is wrong, "Invalid Parameter" isn't a thing. 335 * XXX: Spec doesn't address incorrect UUID incorrectness. 336 * 337 * The CEL buffer is large enough to fit all commands in the emulation, so 338 * the only possible failure would be if the mailbox itself isn't big 339 * enough. 340 */ 341 if (get_log->offset + get_log->length > cci->payload_max) { 342 return CXL_MBOX_INVALID_INPUT; 343 } 344 345 if (!qemu_uuid_is_equal(&get_log->uuid, &cel_uuid)) { 346 return CXL_MBOX_UNSUPPORTED; 347 } 348 349 /* Store off everything to local variables so we can wipe out the payload */ 350 *len_out = get_log->length; 351 352 memmove(payload_out, cci->cel_log + get_log->offset, get_log->length); 353 354 return CXL_MBOX_SUCCESS; 355 } 356 357 /* 8.2.9.5.1.1 */ 358 static CXLRetCode cmd_identify_memory_device(const struct cxl_cmd *cmd, 359 uint8_t *payload_in, 360 size_t len_in, 361 uint8_t *payload_out, 362 size_t *len_out, 363 CXLCCI *cci) 364 { 365 struct { 366 char fw_revision[0x10]; 367 uint64_t total_capacity; 368 uint64_t volatile_capacity; 369 uint64_t persistent_capacity; 370 uint64_t partition_align; 371 uint16_t info_event_log_size; 372 uint16_t warning_event_log_size; 373 uint16_t failure_event_log_size; 374 uint16_t fatal_event_log_size; 375 uint32_t lsa_size; 376 uint8_t poison_list_max_mer[3]; 377 uint16_t inject_poison_limit; 378 uint8_t poison_caps; 379 uint8_t qos_telemetry_caps; 380 } QEMU_PACKED *id; 381 QEMU_BUILD_BUG_ON(sizeof(*id) != 0x43); 382 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 383 CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); 384 CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; 385 386 if ((!QEMU_IS_ALIGNED(cxl_dstate->vmem_size, CXL_CAPACITY_MULTIPLIER)) || 387 (!QEMU_IS_ALIGNED(cxl_dstate->pmem_size, CXL_CAPACITY_MULTIPLIER))) { 388 return CXL_MBOX_INTERNAL_ERROR; 389 } 390 391 id = (void *)payload_out; 392 memset(id, 0, sizeof(*id)); 393 394 snprintf(id->fw_revision, 0x10, "BWFW VERSION %02d", 0); 395 396 stq_le_p(&id->total_capacity, 397 cxl_dstate->mem_size / CXL_CAPACITY_MULTIPLIER); 398 stq_le_p(&id->persistent_capacity, 399 cxl_dstate->pmem_size / CXL_CAPACITY_MULTIPLIER); 400 stq_le_p(&id->volatile_capacity, 401 cxl_dstate->vmem_size / CXL_CAPACITY_MULTIPLIER); 402 stl_le_p(&id->lsa_size, cvc->get_lsa_size(ct3d)); 403 /* 256 poison records */ 404 st24_le_p(id->poison_list_max_mer, 256); 405 /* No limit - so limited by main poison record limit */ 406 stw_le_p(&id->inject_poison_limit, 0); 407 408 *len_out = sizeof(*id); 409 return CXL_MBOX_SUCCESS; 410 } 411 412 static CXLRetCode cmd_ccls_get_partition_info(const struct cxl_cmd *cmd, 413 uint8_t *payload_in, 414 size_t len_in, 415 uint8_t *payload_out, 416 size_t *len_out, 417 CXLCCI *cci) 418 { 419 CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; 420 struct { 421 uint64_t active_vmem; 422 uint64_t active_pmem; 423 uint64_t next_vmem; 424 uint64_t next_pmem; 425 } QEMU_PACKED *part_info = (void *)payload_out; 426 QEMU_BUILD_BUG_ON(sizeof(*part_info) != 0x20); 427 428 if ((!QEMU_IS_ALIGNED(cxl_dstate->vmem_size, CXL_CAPACITY_MULTIPLIER)) || 429 (!QEMU_IS_ALIGNED(cxl_dstate->pmem_size, CXL_CAPACITY_MULTIPLIER))) { 430 return CXL_MBOX_INTERNAL_ERROR; 431 } 432 433 stq_le_p(&part_info->active_vmem, 434 cxl_dstate->vmem_size / CXL_CAPACITY_MULTIPLIER); 435 /* 436 * When both next_vmem and next_pmem are 0, there is no pending change to 437 * partitioning. 438 */ 439 stq_le_p(&part_info->next_vmem, 0); 440 stq_le_p(&part_info->active_pmem, 441 cxl_dstate->pmem_size / CXL_CAPACITY_MULTIPLIER); 442 stq_le_p(&part_info->next_pmem, 0); 443 444 *len_out = sizeof(*part_info); 445 return CXL_MBOX_SUCCESS; 446 } 447 448 static CXLRetCode cmd_ccls_get_lsa(const struct cxl_cmd *cmd, 449 uint8_t *payload_in, 450 size_t len_in, 451 uint8_t *payload_out, 452 size_t *len_out, 453 CXLCCI *cci) 454 { 455 struct { 456 uint32_t offset; 457 uint32_t length; 458 } QEMU_PACKED *get_lsa; 459 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 460 CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); 461 uint32_t offset, length; 462 463 get_lsa = (void *)payload_in; 464 offset = get_lsa->offset; 465 length = get_lsa->length; 466 467 if (offset + length > cvc->get_lsa_size(ct3d)) { 468 *len_out = 0; 469 return CXL_MBOX_INVALID_INPUT; 470 } 471 472 *len_out = cvc->get_lsa(ct3d, payload_out, length, offset); 473 return CXL_MBOX_SUCCESS; 474 } 475 476 static CXLRetCode cmd_ccls_set_lsa(const struct cxl_cmd *cmd, 477 uint8_t *payload_in, 478 size_t len_in, 479 uint8_t *payload_out, 480 size_t *len_out, 481 CXLCCI *cci) 482 { 483 struct set_lsa_pl { 484 uint32_t offset; 485 uint32_t rsvd; 486 uint8_t data[]; 487 } QEMU_PACKED; 488 struct set_lsa_pl *set_lsa_payload = (void *)payload_in; 489 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 490 CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); 491 const size_t hdr_len = offsetof(struct set_lsa_pl, data); 492 493 *len_out = 0; 494 if (!len_in) { 495 return CXL_MBOX_SUCCESS; 496 } 497 498 if (set_lsa_payload->offset + len_in > cvc->get_lsa_size(ct3d) + hdr_len) { 499 return CXL_MBOX_INVALID_INPUT; 500 } 501 len_in -= hdr_len; 502 503 cvc->set_lsa(ct3d, set_lsa_payload->data, len_in, set_lsa_payload->offset); 504 return CXL_MBOX_SUCCESS; 505 } 506 507 /* 508 * This is very inefficient, but good enough for now! 509 * Also the payload will always fit, so no need to handle the MORE flag and 510 * make this stateful. We may want to allow longer poison lists to aid 511 * testing that kernel functionality. 512 */ 513 static CXLRetCode cmd_media_get_poison_list(const struct cxl_cmd *cmd, 514 uint8_t *payload_in, 515 size_t len_in, 516 uint8_t *payload_out, 517 size_t *len_out, 518 CXLCCI *cci) 519 { 520 struct get_poison_list_pl { 521 uint64_t pa; 522 uint64_t length; 523 } QEMU_PACKED; 524 525 struct get_poison_list_out_pl { 526 uint8_t flags; 527 uint8_t rsvd1; 528 uint64_t overflow_timestamp; 529 uint16_t count; 530 uint8_t rsvd2[0x14]; 531 struct { 532 uint64_t addr; 533 uint32_t length; 534 uint32_t resv; 535 } QEMU_PACKED records[]; 536 } QEMU_PACKED; 537 538 struct get_poison_list_pl *in = (void *)payload_in; 539 struct get_poison_list_out_pl *out = (void *)payload_out; 540 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 541 uint16_t record_count = 0, i = 0; 542 uint64_t query_start, query_length; 543 CXLPoisonList *poison_list = &ct3d->poison_list; 544 CXLPoison *ent; 545 uint16_t out_pl_len; 546 547 query_start = ldq_le_p(&in->pa); 548 /* 64 byte alignment required */ 549 if (query_start & 0x3f) { 550 return CXL_MBOX_INVALID_INPUT; 551 } 552 query_length = ldq_le_p(&in->length) * CXL_CACHE_LINE_SIZE; 553 554 QLIST_FOREACH(ent, poison_list, node) { 555 /* Check for no overlap */ 556 if (ent->start >= query_start + query_length || 557 ent->start + ent->length <= query_start) { 558 continue; 559 } 560 record_count++; 561 } 562 out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]); 563 assert(out_pl_len <= CXL_MAILBOX_MAX_PAYLOAD_SIZE); 564 565 memset(out, 0, out_pl_len); 566 QLIST_FOREACH(ent, poison_list, node) { 567 uint64_t start, stop; 568 569 /* Check for no overlap */ 570 if (ent->start >= query_start + query_length || 571 ent->start + ent->length <= query_start) { 572 continue; 573 } 574 575 /* Deal with overlap */ 576 start = MAX(ROUND_DOWN(ent->start, 64ull), query_start); 577 stop = MIN(ROUND_DOWN(ent->start, 64ull) + ent->length, 578 query_start + query_length); 579 stq_le_p(&out->records[i].addr, start | (ent->type & 0x7)); 580 stl_le_p(&out->records[i].length, (stop - start) / CXL_CACHE_LINE_SIZE); 581 i++; 582 } 583 if (ct3d->poison_list_overflowed) { 584 out->flags = (1 << 1); 585 stq_le_p(&out->overflow_timestamp, ct3d->poison_list_overflow_ts); 586 } 587 stw_le_p(&out->count, record_count); 588 *len_out = out_pl_len; 589 return CXL_MBOX_SUCCESS; 590 } 591 592 static CXLRetCode cmd_media_inject_poison(const struct cxl_cmd *cmd, 593 uint8_t *payload_in, 594 size_t len_in, 595 uint8_t *payload_out, 596 size_t *len_out, 597 CXLCCI *cci) 598 { 599 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 600 CXLPoisonList *poison_list = &ct3d->poison_list; 601 CXLPoison *ent; 602 struct inject_poison_pl { 603 uint64_t dpa; 604 }; 605 struct inject_poison_pl *in = (void *)payload_in; 606 uint64_t dpa = ldq_le_p(&in->dpa); 607 CXLPoison *p; 608 609 QLIST_FOREACH(ent, poison_list, node) { 610 if (dpa >= ent->start && 611 dpa + CXL_CACHE_LINE_SIZE <= ent->start + ent->length) { 612 return CXL_MBOX_SUCCESS; 613 } 614 } 615 616 if (ct3d->poison_list_cnt == CXL_POISON_LIST_LIMIT) { 617 return CXL_MBOX_INJECT_POISON_LIMIT; 618 } 619 p = g_new0(CXLPoison, 1); 620 621 p->length = CXL_CACHE_LINE_SIZE; 622 p->start = dpa; 623 p->type = CXL_POISON_TYPE_INJECTED; 624 625 /* 626 * Possible todo: Merge with existing entry if next to it and if same type 627 */ 628 QLIST_INSERT_HEAD(poison_list, p, node); 629 ct3d->poison_list_cnt++; 630 *len_out = 0; 631 632 return CXL_MBOX_SUCCESS; 633 } 634 635 static CXLRetCode cmd_media_clear_poison(const struct cxl_cmd *cmd, 636 uint8_t *payload_in, 637 size_t len_in, 638 uint8_t *payload_out, 639 size_t *len_out, 640 CXLCCI *cci) 641 { 642 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 643 CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; 644 CXLPoisonList *poison_list = &ct3d->poison_list; 645 CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); 646 struct clear_poison_pl { 647 uint64_t dpa; 648 uint8_t data[64]; 649 }; 650 CXLPoison *ent; 651 uint64_t dpa; 652 653 struct clear_poison_pl *in = (void *)payload_in; 654 655 dpa = ldq_le_p(&in->dpa); 656 if (dpa + CXL_CACHE_LINE_SIZE > cxl_dstate->mem_size) { 657 return CXL_MBOX_INVALID_PA; 658 } 659 660 /* Clearing a region with no poison is not an error so always do so */ 661 if (cvc->set_cacheline) { 662 if (!cvc->set_cacheline(ct3d, dpa, in->data)) { 663 return CXL_MBOX_INTERNAL_ERROR; 664 } 665 } 666 667 QLIST_FOREACH(ent, poison_list, node) { 668 /* 669 * Test for contained in entry. Simpler than general case 670 * as clearing 64 bytes and entries 64 byte aligned 671 */ 672 if ((dpa >= ent->start) && (dpa < ent->start + ent->length)) { 673 break; 674 } 675 } 676 if (!ent) { 677 return CXL_MBOX_SUCCESS; 678 } 679 680 QLIST_REMOVE(ent, node); 681 ct3d->poison_list_cnt--; 682 683 if (dpa > ent->start) { 684 CXLPoison *frag; 685 /* Cannot overflow as replacing existing entry */ 686 687 frag = g_new0(CXLPoison, 1); 688 689 frag->start = ent->start; 690 frag->length = dpa - ent->start; 691 frag->type = ent->type; 692 693 QLIST_INSERT_HEAD(poison_list, frag, node); 694 ct3d->poison_list_cnt++; 695 } 696 697 if (dpa + CXL_CACHE_LINE_SIZE < ent->start + ent->length) { 698 CXLPoison *frag; 699 700 if (ct3d->poison_list_cnt == CXL_POISON_LIST_LIMIT) { 701 cxl_set_poison_list_overflowed(ct3d); 702 } else { 703 frag = g_new0(CXLPoison, 1); 704 705 frag->start = dpa + CXL_CACHE_LINE_SIZE; 706 frag->length = ent->start + ent->length - frag->start; 707 frag->type = ent->type; 708 QLIST_INSERT_HEAD(poison_list, frag, node); 709 ct3d->poison_list_cnt++; 710 } 711 } 712 /* Any fragments have been added, free original entry */ 713 g_free(ent); 714 *len_out = 0; 715 716 return CXL_MBOX_SUCCESS; 717 } 718 719 #define IMMEDIATE_CONFIG_CHANGE (1 << 1) 720 #define IMMEDIATE_DATA_CHANGE (1 << 2) 721 #define IMMEDIATE_POLICY_CHANGE (1 << 3) 722 #define IMMEDIATE_LOG_CHANGE (1 << 4) 723 724 static const struct cxl_cmd cxl_cmd_set[256][256] = { 725 [EVENTS][GET_RECORDS] = { "EVENTS_GET_RECORDS", 726 cmd_events_get_records, 1, 0 }, 727 [EVENTS][CLEAR_RECORDS] = { "EVENTS_CLEAR_RECORDS", 728 cmd_events_clear_records, ~0, IMMEDIATE_LOG_CHANGE }, 729 [EVENTS][GET_INTERRUPT_POLICY] = { "EVENTS_GET_INTERRUPT_POLICY", 730 cmd_events_get_interrupt_policy, 0, 0 }, 731 [EVENTS][SET_INTERRUPT_POLICY] = { "EVENTS_SET_INTERRUPT_POLICY", 732 cmd_events_set_interrupt_policy, 733 ~0, IMMEDIATE_CONFIG_CHANGE }, 734 [FIRMWARE_UPDATE][GET_INFO] = { "FIRMWARE_UPDATE_GET_INFO", 735 cmd_firmware_update_get_info, 0, 0 }, 736 [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 }, 737 [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set, 738 8, IMMEDIATE_POLICY_CHANGE }, 739 [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 740 0, 0 }, 741 [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, 742 [IDENTIFY][MEMORY_DEVICE] = { "IDENTIFY_MEMORY_DEVICE", 743 cmd_identify_memory_device, 0, 0 }, 744 [CCLS][GET_PARTITION_INFO] = { "CCLS_GET_PARTITION_INFO", 745 cmd_ccls_get_partition_info, 0, 0 }, 746 [CCLS][GET_LSA] = { "CCLS_GET_LSA", cmd_ccls_get_lsa, 8, 0 }, 747 [CCLS][SET_LSA] = { "CCLS_SET_LSA", cmd_ccls_set_lsa, 748 ~0, IMMEDIATE_CONFIG_CHANGE | IMMEDIATE_DATA_CHANGE }, 749 [MEDIA_AND_POISON][GET_POISON_LIST] = { "MEDIA_AND_POISON_GET_POISON_LIST", 750 cmd_media_get_poison_list, 16, 0 }, 751 [MEDIA_AND_POISON][INJECT_POISON] = { "MEDIA_AND_POISON_INJECT_POISON", 752 cmd_media_inject_poison, 8, 0 }, 753 [MEDIA_AND_POISON][CLEAR_POISON] = { "MEDIA_AND_POISON_CLEAR_POISON", 754 cmd_media_clear_poison, 72, 0 }, 755 }; 756 757 static const struct cxl_cmd cxl_cmd_set_sw[256][256] = { 758 [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 }, 759 [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set, 0, 760 IMMEDIATE_POLICY_CHANGE }, 761 [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0, 762 0 }, 763 [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, 764 }; 765 766 int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, 767 size_t len_in, uint8_t *pl_in, size_t *len_out, 768 uint8_t *pl_out, bool *bg_started) 769 { 770 const struct cxl_cmd *cxl_cmd; 771 opcode_handler h; 772 773 *len_out = 0; 774 cxl_cmd = &cci->cxl_cmd_set[set][cmd]; 775 h = cxl_cmd->handler; 776 if (!h) { 777 qemu_log_mask(LOG_UNIMP, "Command %04xh not implemented\n", 778 set << 8 | cmd); 779 return CXL_MBOX_UNSUPPORTED; 780 } 781 782 if (len_in != cxl_cmd->in && cxl_cmd->in != ~0) { 783 return CXL_MBOX_INVALID_PAYLOAD_LENGTH; 784 } 785 786 return (*h)(cxl_cmd, pl_in, len_in, pl_out, len_out, cci); 787 } 788 789 void cxl_init_cci(CXLCCI *cci, size_t payload_max) 790 { 791 cci->payload_max = payload_max; 792 for (int set = 0; set < 256; set++) { 793 for (int cmd = 0; cmd < 256; cmd++) { 794 if (cci->cxl_cmd_set[set][cmd].handler) { 795 const struct cxl_cmd *c = &cci->cxl_cmd_set[set][cmd]; 796 struct cel_log *log = 797 &cci->cel_log[cci->cel_size]; 798 799 log->opcode = (set << 8) | cmd; 800 log->effect = c->effect; 801 cci->cel_size++; 802 } 803 } 804 } 805 } 806 807 void cxl_initialize_mailbox_swcci(CXLCCI *cci, DeviceState *intf, 808 DeviceState *d, size_t payload_max) 809 { 810 cci->cxl_cmd_set = cxl_cmd_set_sw; 811 cci->d = d; 812 cci->intf = intf; 813 cxl_init_cci(cci, payload_max); 814 } 815 816 void cxl_initialize_mailbox_t3(CXLCCI *cci, DeviceState *d, size_t payload_max) 817 { 818 cci->cxl_cmd_set = cxl_cmd_set; 819 cci->d = d; 820 821 /* No separation for PCI MB as protocol handled in PCI device */ 822 cci->intf = d; 823 cxl_init_cci(cci, payload_max); 824 } 825