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/pci/msi.h" 12 #include "hw/pci/msix.h" 13 #include "hw/cxl/cxl.h" 14 #include "hw/cxl/cxl_events.h" 15 #include "hw/cxl/cxl_mailbox.h" 16 #include "hw/pci/pci.h" 17 #include "hw/pci-bridge/cxl_upstream_port.h" 18 #include "qemu/cutils.h" 19 #include "qemu/log.h" 20 #include "qemu/units.h" 21 #include "qemu/uuid.h" 22 #include "sysemu/hostmem.h" 23 #include "qemu/range.h" 24 25 #define CXL_CAPACITY_MULTIPLIER (256 * MiB) 26 #define CXL_DC_EVENT_LOG_SIZE 8 27 #define CXL_NUM_EXTENTS_SUPPORTED 512 28 #define CXL_NUM_TAGS_SUPPORTED 0 29 30 /* 31 * How to add a new command, example. The command set FOO, with cmd BAR. 32 * 1. Add the command set and cmd to the enum. 33 * FOO = 0x7f, 34 * #define BAR 0 35 * 2. Implement the handler 36 * static CXLRetCode cmd_foo_bar(struct cxl_cmd *cmd, 37 * CXLDeviceState *cxl_dstate, uint16_t *len) 38 * 3. Add the command to the cxl_cmd_set[][] 39 * [FOO][BAR] = { "FOO_BAR", cmd_foo_bar, x, y }, 40 * 4. Implement your handler 41 * define_mailbox_handler(FOO_BAR) { ... return CXL_MBOX_SUCCESS; } 42 * 43 * 44 * Writing the handler: 45 * The handler will provide the &struct cxl_cmd, the &CXLDeviceState, and the 46 * in/out length of the payload. The handler is responsible for consuming the 47 * payload from cmd->payload and operating upon it as necessary. It must then 48 * fill the output data into cmd->payload (overwriting what was there), 49 * setting the length, and returning a valid return code. 50 * 51 * XXX: The handler need not worry about endianness. The payload is read out of 52 * a register interface that already deals with it. 53 */ 54 55 enum { 56 INFOSTAT = 0x00, 57 #define IS_IDENTIFY 0x1 58 #define BACKGROUND_OPERATION_STATUS 0x2 59 EVENTS = 0x01, 60 #define GET_RECORDS 0x0 61 #define CLEAR_RECORDS 0x1 62 #define GET_INTERRUPT_POLICY 0x2 63 #define SET_INTERRUPT_POLICY 0x3 64 FIRMWARE_UPDATE = 0x02, 65 #define GET_INFO 0x0 66 #define TRANSFER 0x1 67 #define ACTIVATE 0x2 68 TIMESTAMP = 0x03, 69 #define GET 0x0 70 #define SET 0x1 71 LOGS = 0x04, 72 #define GET_SUPPORTED 0x0 73 #define GET_LOG 0x1 74 FEATURES = 0x05, 75 #define GET_SUPPORTED 0x0 76 #define GET_FEATURE 0x1 77 #define SET_FEATURE 0x2 78 IDENTIFY = 0x40, 79 #define MEMORY_DEVICE 0x0 80 CCLS = 0x41, 81 #define GET_PARTITION_INFO 0x0 82 #define GET_LSA 0x2 83 #define SET_LSA 0x3 84 SANITIZE = 0x44, 85 #define OVERWRITE 0x0 86 #define SECURE_ERASE 0x1 87 PERSISTENT_MEM = 0x45, 88 #define GET_SECURITY_STATE 0x0 89 MEDIA_AND_POISON = 0x43, 90 #define GET_POISON_LIST 0x0 91 #define INJECT_POISON 0x1 92 #define CLEAR_POISON 0x2 93 #define GET_SCAN_MEDIA_CAPABILITIES 0x3 94 #define SCAN_MEDIA 0x4 95 #define GET_SCAN_MEDIA_RESULTS 0x5 96 DCD_CONFIG = 0x48, 97 #define GET_DC_CONFIG 0x0 98 #define GET_DYN_CAP_EXT_LIST 0x1 99 #define ADD_DYN_CAP_RSP 0x2 100 #define RELEASE_DYN_CAP 0x3 101 PHYSICAL_SWITCH = 0x51, 102 #define IDENTIFY_SWITCH_DEVICE 0x0 103 #define GET_PHYSICAL_PORT_STATE 0x1 104 TUNNEL = 0x53, 105 #define MANAGEMENT_COMMAND 0x0 106 }; 107 108 /* CCI Message Format CXL r3.1 Figure 7-19 */ 109 typedef struct CXLCCIMessage { 110 uint8_t category; 111 #define CXL_CCI_CAT_REQ 0 112 #define CXL_CCI_CAT_RSP 1 113 uint8_t tag; 114 uint8_t resv1; 115 uint8_t command; 116 uint8_t command_set; 117 uint8_t pl_length[3]; 118 uint16_t rc; 119 uint16_t vendor_specific; 120 uint8_t payload[]; 121 } QEMU_PACKED CXLCCIMessage; 122 123 /* This command is only defined to an MLD FM Owned LD or an MHD */ 124 static CXLRetCode cmd_tunnel_management_cmd(const struct cxl_cmd *cmd, 125 uint8_t *payload_in, 126 size_t len_in, 127 uint8_t *payload_out, 128 size_t *len_out, 129 CXLCCI *cci) 130 { 131 PCIDevice *tunnel_target; 132 CXLCCI *target_cci; 133 struct { 134 uint8_t port_or_ld_id; 135 uint8_t target_type; 136 uint16_t size; 137 CXLCCIMessage ccimessage; 138 } QEMU_PACKED *in; 139 struct { 140 uint16_t resp_len; 141 uint8_t resv[2]; 142 CXLCCIMessage ccimessage; 143 } QEMU_PACKED *out; 144 size_t pl_length, length_out; 145 bool bg_started; 146 int rc; 147 148 if (cmd->in < sizeof(*in)) { 149 return CXL_MBOX_INVALID_INPUT; 150 } 151 in = (void *)payload_in; 152 out = (void *)payload_out; 153 154 /* Enough room for minimum sized message - no payload */ 155 if (in->size < sizeof(in->ccimessage)) { 156 return CXL_MBOX_INVALID_PAYLOAD_LENGTH; 157 } 158 /* Length of input payload should be in->size + a wrapping tunnel header */ 159 if (in->size != len_in - offsetof(typeof(*out), ccimessage)) { 160 return CXL_MBOX_INVALID_PAYLOAD_LENGTH; 161 } 162 if (in->ccimessage.category != CXL_CCI_CAT_REQ) { 163 return CXL_MBOX_INVALID_INPUT; 164 } 165 166 if (in->target_type != 0) { 167 qemu_log_mask(LOG_UNIMP, 168 "Tunneled Command sent to non existent FM-LD"); 169 return CXL_MBOX_INVALID_INPUT; 170 } 171 172 /* 173 * Target of a tunnel unfortunately depends on type of CCI readint 174 * the message. 175 * If in a switch, then it's the port number. 176 * If in an MLD it is the ld number. 177 * If in an MHD target type indicate where we are going. 178 */ 179 if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) { 180 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 181 if (in->port_or_ld_id != 0) { 182 /* Only pretending to have one for now! */ 183 return CXL_MBOX_INVALID_INPUT; 184 } 185 target_cci = &ct3d->ld0_cci; 186 } else if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_USP)) { 187 CXLUpstreamPort *usp = CXL_USP(cci->d); 188 189 tunnel_target = pcie_find_port_by_pn(&PCI_BRIDGE(usp)->sec_bus, 190 in->port_or_ld_id); 191 if (!tunnel_target) { 192 return CXL_MBOX_INVALID_INPUT; 193 } 194 tunnel_target = 195 pci_bridge_get_sec_bus(PCI_BRIDGE(tunnel_target))->devices[0]; 196 if (!tunnel_target) { 197 return CXL_MBOX_INVALID_INPUT; 198 } 199 if (object_dynamic_cast(OBJECT(tunnel_target), TYPE_CXL_TYPE3)) { 200 CXLType3Dev *ct3d = CXL_TYPE3(tunnel_target); 201 /* Tunneled VDMs always land on FM Owned LD */ 202 target_cci = &ct3d->vdm_fm_owned_ld_mctp_cci; 203 } else { 204 return CXL_MBOX_INVALID_INPUT; 205 } 206 } else { 207 return CXL_MBOX_INVALID_INPUT; 208 } 209 210 pl_length = in->ccimessage.pl_length[2] << 16 | 211 in->ccimessage.pl_length[1] << 8 | in->ccimessage.pl_length[0]; 212 rc = cxl_process_cci_message(target_cci, 213 in->ccimessage.command_set, 214 in->ccimessage.command, 215 pl_length, in->ccimessage.payload, 216 &length_out, out->ccimessage.payload, 217 &bg_started); 218 /* Payload should be in place. Rest of CCI header and needs filling */ 219 out->resp_len = length_out + sizeof(CXLCCIMessage); 220 st24_le_p(out->ccimessage.pl_length, length_out); 221 out->ccimessage.rc = rc; 222 out->ccimessage.category = CXL_CCI_CAT_RSP; 223 out->ccimessage.command = in->ccimessage.command; 224 out->ccimessage.command_set = in->ccimessage.command_set; 225 out->ccimessage.tag = in->ccimessage.tag; 226 *len_out = length_out + sizeof(*out); 227 228 return CXL_MBOX_SUCCESS; 229 } 230 231 static CXLRetCode cmd_events_get_records(const struct cxl_cmd *cmd, 232 uint8_t *payload_in, size_t len_in, 233 uint8_t *payload_out, size_t *len_out, 234 CXLCCI *cci) 235 { 236 CXLDeviceState *cxlds = &CXL_TYPE3(cci->d)->cxl_dstate; 237 CXLGetEventPayload *pl; 238 uint8_t log_type; 239 int max_recs; 240 241 if (cmd->in < sizeof(log_type)) { 242 return CXL_MBOX_INVALID_INPUT; 243 } 244 245 log_type = payload_in[0]; 246 247 pl = (CXLGetEventPayload *)payload_out; 248 249 max_recs = (cxlds->payload_size - CXL_EVENT_PAYLOAD_HDR_SIZE) / 250 CXL_EVENT_RECORD_SIZE; 251 if (max_recs > 0xFFFF) { 252 max_recs = 0xFFFF; 253 } 254 255 return cxl_event_get_records(cxlds, pl, log_type, max_recs, len_out); 256 } 257 258 static CXLRetCode cmd_events_clear_records(const struct cxl_cmd *cmd, 259 uint8_t *payload_in, 260 size_t len_in, 261 uint8_t *payload_out, 262 size_t *len_out, 263 CXLCCI *cci) 264 { 265 CXLDeviceState *cxlds = &CXL_TYPE3(cci->d)->cxl_dstate; 266 CXLClearEventPayload *pl; 267 268 pl = (CXLClearEventPayload *)payload_in; 269 *len_out = 0; 270 return cxl_event_clear_records(cxlds, pl); 271 } 272 273 static CXLRetCode cmd_events_get_interrupt_policy(const struct cxl_cmd *cmd, 274 uint8_t *payload_in, 275 size_t len_in, 276 uint8_t *payload_out, 277 size_t *len_out, 278 CXLCCI *cci) 279 { 280 CXLDeviceState *cxlds = &CXL_TYPE3(cci->d)->cxl_dstate; 281 CXLEventInterruptPolicy *policy; 282 CXLEventLog *log; 283 284 policy = (CXLEventInterruptPolicy *)payload_out; 285 286 log = &cxlds->event_logs[CXL_EVENT_TYPE_INFO]; 287 if (log->irq_enabled) { 288 policy->info_settings = CXL_EVENT_INT_SETTING(log->irq_vec); 289 } 290 291 log = &cxlds->event_logs[CXL_EVENT_TYPE_WARN]; 292 if (log->irq_enabled) { 293 policy->warn_settings = CXL_EVENT_INT_SETTING(log->irq_vec); 294 } 295 296 log = &cxlds->event_logs[CXL_EVENT_TYPE_FAIL]; 297 if (log->irq_enabled) { 298 policy->failure_settings = CXL_EVENT_INT_SETTING(log->irq_vec); 299 } 300 301 log = &cxlds->event_logs[CXL_EVENT_TYPE_FATAL]; 302 if (log->irq_enabled) { 303 policy->fatal_settings = CXL_EVENT_INT_SETTING(log->irq_vec); 304 } 305 306 log = &cxlds->event_logs[CXL_EVENT_TYPE_DYNAMIC_CAP]; 307 if (log->irq_enabled) { 308 /* Dynamic Capacity borrows the same vector as info */ 309 policy->dyn_cap_settings = CXL_INT_MSI_MSIX; 310 } 311 312 *len_out = sizeof(*policy); 313 return CXL_MBOX_SUCCESS; 314 } 315 316 static CXLRetCode cmd_events_set_interrupt_policy(const struct cxl_cmd *cmd, 317 uint8_t *payload_in, 318 size_t len_in, 319 uint8_t *payload_out, 320 size_t *len_out, 321 CXLCCI *cci) 322 { 323 CXLDeviceState *cxlds = &CXL_TYPE3(cci->d)->cxl_dstate; 324 CXLEventInterruptPolicy *policy; 325 CXLEventLog *log; 326 327 if (len_in < CXL_EVENT_INT_SETTING_MIN_LEN) { 328 return CXL_MBOX_INVALID_PAYLOAD_LENGTH; 329 } 330 331 policy = (CXLEventInterruptPolicy *)payload_in; 332 333 log = &cxlds->event_logs[CXL_EVENT_TYPE_INFO]; 334 log->irq_enabled = (policy->info_settings & CXL_EVENT_INT_MODE_MASK) == 335 CXL_INT_MSI_MSIX; 336 337 log = &cxlds->event_logs[CXL_EVENT_TYPE_WARN]; 338 log->irq_enabled = (policy->warn_settings & CXL_EVENT_INT_MODE_MASK) == 339 CXL_INT_MSI_MSIX; 340 341 log = &cxlds->event_logs[CXL_EVENT_TYPE_FAIL]; 342 log->irq_enabled = (policy->failure_settings & CXL_EVENT_INT_MODE_MASK) == 343 CXL_INT_MSI_MSIX; 344 345 log = &cxlds->event_logs[CXL_EVENT_TYPE_FATAL]; 346 log->irq_enabled = (policy->fatal_settings & CXL_EVENT_INT_MODE_MASK) == 347 CXL_INT_MSI_MSIX; 348 349 /* DCD is optional */ 350 if (len_in < sizeof(*policy)) { 351 return CXL_MBOX_SUCCESS; 352 } 353 354 log = &cxlds->event_logs[CXL_EVENT_TYPE_DYNAMIC_CAP]; 355 log->irq_enabled = (policy->dyn_cap_settings & CXL_EVENT_INT_MODE_MASK) == 356 CXL_INT_MSI_MSIX; 357 358 *len_out = 0; 359 return CXL_MBOX_SUCCESS; 360 } 361 362 /* CXL r3.1 section 8.2.9.1.1: Identify (Opcode 0001h) */ 363 static CXLRetCode cmd_infostat_identify(const struct cxl_cmd *cmd, 364 uint8_t *payload_in, 365 size_t len_in, 366 uint8_t *payload_out, 367 size_t *len_out, 368 CXLCCI *cci) 369 { 370 PCIDeviceClass *class = PCI_DEVICE_GET_CLASS(cci->d); 371 struct { 372 uint16_t pcie_vid; 373 uint16_t pcie_did; 374 uint16_t pcie_subsys_vid; 375 uint16_t pcie_subsys_id; 376 uint64_t sn; 377 uint8_t max_message_size; 378 uint8_t component_type; 379 } QEMU_PACKED *is_identify; 380 QEMU_BUILD_BUG_ON(sizeof(*is_identify) != 18); 381 382 is_identify = (void *)payload_out; 383 is_identify->pcie_vid = class->vendor_id; 384 is_identify->pcie_did = class->device_id; 385 if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_USP)) { 386 is_identify->sn = CXL_USP(cci->d)->sn; 387 /* Subsystem info not defined for a USP */ 388 is_identify->pcie_subsys_vid = 0; 389 is_identify->pcie_subsys_id = 0; 390 is_identify->component_type = 0x0; /* Switch */ 391 } else if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) { 392 PCIDevice *pci_dev = PCI_DEVICE(cci->d); 393 394 is_identify->sn = CXL_TYPE3(cci->d)->sn; 395 /* 396 * We can't always use class->subsystem_vendor_id as 397 * it is not set if the defaults are used. 398 */ 399 is_identify->pcie_subsys_vid = 400 pci_get_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID); 401 is_identify->pcie_subsys_id = 402 pci_get_word(pci_dev->config + PCI_SUBSYSTEM_ID); 403 is_identify->component_type = 0x3; /* Type 3 */ 404 } 405 406 /* TODO: Allow this to vary across different CCIs */ 407 is_identify->max_message_size = 9; /* 512 bytes - MCTP_CXL_MAILBOX_BYTES */ 408 *len_out = sizeof(*is_identify); 409 return CXL_MBOX_SUCCESS; 410 } 411 412 static void cxl_set_dsp_active_bm(PCIBus *b, PCIDevice *d, 413 void *private) 414 { 415 uint8_t *bm = private; 416 if (object_dynamic_cast(OBJECT(d), TYPE_CXL_DSP)) { 417 uint8_t port = PCIE_PORT(d)->port; 418 bm[port / 8] |= 1 << (port % 8); 419 } 420 } 421 422 /* CXL r3.1 Section 7.6.7.1.1: Identify Switch Device (Opcode 5100h) */ 423 static CXLRetCode cmd_identify_switch_device(const struct cxl_cmd *cmd, 424 uint8_t *payload_in, 425 size_t len_in, 426 uint8_t *payload_out, 427 size_t *len_out, 428 CXLCCI *cci) 429 { 430 PCIEPort *usp = PCIE_PORT(cci->d); 431 PCIBus *bus = &PCI_BRIDGE(cci->d)->sec_bus; 432 int num_phys_ports = pcie_count_ds_ports(bus); 433 434 struct cxl_fmapi_ident_switch_dev_resp_pl { 435 uint8_t ingress_port_id; 436 uint8_t rsvd; 437 uint8_t num_physical_ports; 438 uint8_t num_vcss; 439 uint8_t active_port_bitmask[0x20]; 440 uint8_t active_vcs_bitmask[0x20]; 441 uint16_t total_vppbs; 442 uint16_t bound_vppbs; 443 uint8_t num_hdm_decoders_per_usp; 444 } QEMU_PACKED *out; 445 QEMU_BUILD_BUG_ON(sizeof(*out) != 0x49); 446 447 out = (struct cxl_fmapi_ident_switch_dev_resp_pl *)payload_out; 448 *out = (struct cxl_fmapi_ident_switch_dev_resp_pl) { 449 .num_physical_ports = num_phys_ports + 1, /* 1 USP */ 450 .num_vcss = 1, /* Not yet support multiple VCS - potentially tricky */ 451 .active_vcs_bitmask[0] = 0x1, 452 .total_vppbs = num_phys_ports + 1, 453 .bound_vppbs = num_phys_ports + 1, 454 .num_hdm_decoders_per_usp = 4, 455 }; 456 457 /* Depends on the CCI type */ 458 if (object_dynamic_cast(OBJECT(cci->intf), TYPE_PCIE_PORT)) { 459 out->ingress_port_id = PCIE_PORT(cci->intf)->port; 460 } else { 461 /* MCTP? */ 462 out->ingress_port_id = 0; 463 } 464 465 pci_for_each_device_under_bus(bus, cxl_set_dsp_active_bm, 466 out->active_port_bitmask); 467 out->active_port_bitmask[usp->port / 8] |= (1 << usp->port % 8); 468 469 *len_out = sizeof(*out); 470 471 return CXL_MBOX_SUCCESS; 472 } 473 474 /* CXL r3.1 Section 7.6.7.1.2: Get Physical Port State (Opcode 5101h) */ 475 static CXLRetCode cmd_get_physical_port_state(const struct cxl_cmd *cmd, 476 uint8_t *payload_in, 477 size_t len_in, 478 uint8_t *payload_out, 479 size_t *len_out, 480 CXLCCI *cci) 481 { 482 /* CXL r3.1 Table 7-17: Get Physical Port State Request Payload */ 483 struct cxl_fmapi_get_phys_port_state_req_pl { 484 uint8_t num_ports; 485 uint8_t ports[]; 486 } QEMU_PACKED *in; 487 488 /* 489 * CXL r3.1 Table 7-19: Get Physical Port State Port Information Block 490 * Format 491 */ 492 struct cxl_fmapi_port_state_info_block { 493 uint8_t port_id; 494 uint8_t config_state; 495 uint8_t connected_device_cxl_version; 496 uint8_t rsv1; 497 uint8_t connected_device_type; 498 uint8_t port_cxl_version_bitmask; 499 uint8_t max_link_width; 500 uint8_t negotiated_link_width; 501 uint8_t supported_link_speeds_vector; 502 uint8_t max_link_speed; 503 uint8_t current_link_speed; 504 uint8_t ltssm_state; 505 uint8_t first_lane_num; 506 uint16_t link_state; 507 uint8_t supported_ld_count; 508 } QEMU_PACKED; 509 510 /* CXL r3.1 Table 7-18: Get Physical Port State Response Payload */ 511 struct cxl_fmapi_get_phys_port_state_resp_pl { 512 uint8_t num_ports; 513 uint8_t rsv1[3]; 514 struct cxl_fmapi_port_state_info_block ports[]; 515 } QEMU_PACKED *out; 516 PCIBus *bus = &PCI_BRIDGE(cci->d)->sec_bus; 517 PCIEPort *usp = PCIE_PORT(cci->d); 518 size_t pl_size; 519 int i; 520 521 in = (struct cxl_fmapi_get_phys_port_state_req_pl *)payload_in; 522 out = (struct cxl_fmapi_get_phys_port_state_resp_pl *)payload_out; 523 524 /* Check if what was requested can fit */ 525 if (sizeof(*out) + sizeof(*out->ports) * in->num_ports > cci->payload_max) { 526 return CXL_MBOX_INVALID_INPUT; 527 } 528 529 /* For success there should be a match for each requested */ 530 out->num_ports = in->num_ports; 531 532 for (i = 0; i < in->num_ports; i++) { 533 struct cxl_fmapi_port_state_info_block *port; 534 /* First try to match on downstream port */ 535 PCIDevice *port_dev; 536 uint16_t lnkcap, lnkcap2, lnksta; 537 538 port = &out->ports[i]; 539 540 port_dev = pcie_find_port_by_pn(bus, in->ports[i]); 541 if (port_dev) { /* DSP */ 542 PCIDevice *ds_dev = pci_bridge_get_sec_bus(PCI_BRIDGE(port_dev)) 543 ->devices[0]; 544 port->config_state = 3; 545 if (ds_dev) { 546 if (object_dynamic_cast(OBJECT(ds_dev), TYPE_CXL_TYPE3)) { 547 port->connected_device_type = 5; /* Assume MLD for now */ 548 } else { 549 port->connected_device_type = 1; 550 } 551 } else { 552 port->connected_device_type = 0; 553 } 554 port->supported_ld_count = 3; 555 } else if (usp->port == in->ports[i]) { /* USP */ 556 port_dev = PCI_DEVICE(usp); 557 port->config_state = 4; 558 port->connected_device_type = 0; 559 } else { 560 return CXL_MBOX_INVALID_INPUT; 561 } 562 563 port->port_id = in->ports[i]; 564 /* Information on status of this port in lnksta, lnkcap */ 565 if (!port_dev->exp.exp_cap) { 566 return CXL_MBOX_INTERNAL_ERROR; 567 } 568 lnksta = port_dev->config_read(port_dev, 569 port_dev->exp.exp_cap + PCI_EXP_LNKSTA, 570 sizeof(lnksta)); 571 lnkcap = port_dev->config_read(port_dev, 572 port_dev->exp.exp_cap + PCI_EXP_LNKCAP, 573 sizeof(lnkcap)); 574 lnkcap2 = port_dev->config_read(port_dev, 575 port_dev->exp.exp_cap + PCI_EXP_LNKCAP2, 576 sizeof(lnkcap2)); 577 578 port->max_link_width = (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4; 579 port->negotiated_link_width = (lnksta & PCI_EXP_LNKSTA_NLW) >> 4; 580 /* No definition for SLS field in linux/pci_regs.h */ 581 port->supported_link_speeds_vector = (lnkcap2 & 0xFE) >> 1; 582 port->max_link_speed = lnkcap & PCI_EXP_LNKCAP_SLS; 583 port->current_link_speed = lnksta & PCI_EXP_LNKSTA_CLS; 584 /* TODO: Track down if we can get the rest of the info */ 585 port->ltssm_state = 0x7; 586 port->first_lane_num = 0; 587 port->link_state = 0; 588 port->port_cxl_version_bitmask = 0x2; 589 port->connected_device_cxl_version = 0x2; 590 } 591 592 pl_size = sizeof(*out) + sizeof(*out->ports) * in->num_ports; 593 *len_out = pl_size; 594 595 return CXL_MBOX_SUCCESS; 596 } 597 598 /* CXL r3.1 Section 8.2.9.1.2: Background Operation Status (Opcode 0002h) */ 599 static CXLRetCode cmd_infostat_bg_op_sts(const struct cxl_cmd *cmd, 600 uint8_t *payload_in, 601 size_t len_in, 602 uint8_t *payload_out, 603 size_t *len_out, 604 CXLCCI *cci) 605 { 606 struct { 607 uint8_t status; 608 uint8_t rsvd; 609 uint16_t opcode; 610 uint16_t returncode; 611 uint16_t vendor_ext_status; 612 } QEMU_PACKED *bg_op_status; 613 QEMU_BUILD_BUG_ON(sizeof(*bg_op_status) != 8); 614 615 bg_op_status = (void *)payload_out; 616 bg_op_status->status = cci->bg.complete_pct << 1; 617 if (cci->bg.runtime > 0) { 618 bg_op_status->status |= 1U << 0; 619 } 620 bg_op_status->opcode = cci->bg.opcode; 621 bg_op_status->returncode = cci->bg.ret_code; 622 *len_out = sizeof(*bg_op_status); 623 624 return CXL_MBOX_SUCCESS; 625 } 626 627 #define CXL_FW_SLOTS 2 628 #define CXL_FW_SIZE 0x02000000 /* 32 mb */ 629 630 /* CXL r3.1 Section 8.2.9.3.1: Get FW Info (Opcode 0200h) */ 631 static CXLRetCode cmd_firmware_update_get_info(const struct cxl_cmd *cmd, 632 uint8_t *payload_in, 633 size_t len, 634 uint8_t *payload_out, 635 size_t *len_out, 636 CXLCCI *cci) 637 { 638 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 639 CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; 640 struct { 641 uint8_t slots_supported; 642 uint8_t slot_info; 643 uint8_t caps; 644 uint8_t rsvd[0xd]; 645 char fw_rev1[0x10]; 646 char fw_rev2[0x10]; 647 char fw_rev3[0x10]; 648 char fw_rev4[0x10]; 649 } QEMU_PACKED *fw_info; 650 QEMU_BUILD_BUG_ON(sizeof(*fw_info) != 0x50); 651 652 if ((cxl_dstate->vmem_size < CXL_CAPACITY_MULTIPLIER) || 653 (cxl_dstate->pmem_size < CXL_CAPACITY_MULTIPLIER) || 654 (ct3d->dc.total_capacity < CXL_CAPACITY_MULTIPLIER)) { 655 return CXL_MBOX_INTERNAL_ERROR; 656 } 657 658 fw_info = (void *)payload_out; 659 660 fw_info->slots_supported = CXL_FW_SLOTS; 661 fw_info->slot_info = (cci->fw.active_slot & 0x7) | 662 ((cci->fw.staged_slot & 0x7) << 3); 663 fw_info->caps = BIT(0); /* online update supported */ 664 665 if (cci->fw.slot[0]) { 666 pstrcpy(fw_info->fw_rev1, sizeof(fw_info->fw_rev1), "BWFW VERSION 0"); 667 } 668 if (cci->fw.slot[1]) { 669 pstrcpy(fw_info->fw_rev2, sizeof(fw_info->fw_rev2), "BWFW VERSION 1"); 670 } 671 672 *len_out = sizeof(*fw_info); 673 return CXL_MBOX_SUCCESS; 674 } 675 676 /* CXL r3.1 section 8.2.9.3.2: Transfer FW (Opcode 0201h) */ 677 #define CXL_FW_XFER_ALIGNMENT 128 678 679 #define CXL_FW_XFER_ACTION_FULL 0x0 680 #define CXL_FW_XFER_ACTION_INIT 0x1 681 #define CXL_FW_XFER_ACTION_CONTINUE 0x2 682 #define CXL_FW_XFER_ACTION_END 0x3 683 #define CXL_FW_XFER_ACTION_ABORT 0x4 684 685 static CXLRetCode cmd_firmware_update_transfer(const struct cxl_cmd *cmd, 686 uint8_t *payload_in, 687 size_t len, 688 uint8_t *payload_out, 689 size_t *len_out, 690 CXLCCI *cci) 691 { 692 struct { 693 uint8_t action; 694 uint8_t slot; 695 uint8_t rsvd1[2]; 696 uint32_t offset; 697 uint8_t rsvd2[0x78]; 698 uint8_t data[]; 699 } QEMU_PACKED *fw_transfer = (void *)payload_in; 700 size_t offset, length; 701 702 if (fw_transfer->action == CXL_FW_XFER_ACTION_ABORT) { 703 /* 704 * At this point there aren't any on-going transfers 705 * running in the bg - this is serialized before this 706 * call altogether. Just mark the state machine and 707 * disregard any other input. 708 */ 709 cci->fw.transferring = false; 710 return CXL_MBOX_SUCCESS; 711 } 712 713 offset = fw_transfer->offset * CXL_FW_XFER_ALIGNMENT; 714 length = len - sizeof(*fw_transfer); 715 if (offset + length > CXL_FW_SIZE) { 716 return CXL_MBOX_INVALID_INPUT; 717 } 718 719 if (cci->fw.transferring) { 720 if (fw_transfer->action == CXL_FW_XFER_ACTION_FULL || 721 fw_transfer->action == CXL_FW_XFER_ACTION_INIT) { 722 return CXL_MBOX_FW_XFER_IN_PROGRESS; 723 } 724 /* 725 * Abort partitioned package transfer if over 30 secs 726 * between parts. As opposed to the explicit ABORT action, 727 * semantically treat this condition as an error - as 728 * if a part action were passed without a previous INIT. 729 */ 730 if (difftime(time(NULL), cci->fw.last_partxfer) > 30.0) { 731 cci->fw.transferring = false; 732 return CXL_MBOX_INVALID_INPUT; 733 } 734 } else if (fw_transfer->action == CXL_FW_XFER_ACTION_CONTINUE || 735 fw_transfer->action == CXL_FW_XFER_ACTION_END) { 736 return CXL_MBOX_INVALID_INPUT; 737 } 738 739 /* allow back-to-back retransmission */ 740 if ((offset != cci->fw.prev_offset || length != cci->fw.prev_len) && 741 (fw_transfer->action == CXL_FW_XFER_ACTION_CONTINUE || 742 fw_transfer->action == CXL_FW_XFER_ACTION_END)) { 743 /* verify no overlaps */ 744 if (offset < cci->fw.prev_offset + cci->fw.prev_len) { 745 return CXL_MBOX_FW_XFER_OUT_OF_ORDER; 746 } 747 } 748 749 switch (fw_transfer->action) { 750 case CXL_FW_XFER_ACTION_FULL: /* ignores offset */ 751 case CXL_FW_XFER_ACTION_END: 752 if (fw_transfer->slot == 0 || 753 fw_transfer->slot == cci->fw.active_slot || 754 fw_transfer->slot > CXL_FW_SLOTS) { 755 return CXL_MBOX_FW_INVALID_SLOT; 756 } 757 758 /* mark the slot used upon bg completion */ 759 break; 760 case CXL_FW_XFER_ACTION_INIT: 761 if (offset != 0) { 762 return CXL_MBOX_INVALID_INPUT; 763 } 764 765 cci->fw.transferring = true; 766 cci->fw.prev_offset = offset; 767 cci->fw.prev_len = length; 768 break; 769 case CXL_FW_XFER_ACTION_CONTINUE: 770 cci->fw.prev_offset = offset; 771 cci->fw.prev_len = length; 772 break; 773 default: 774 return CXL_MBOX_INVALID_INPUT; 775 } 776 777 if (fw_transfer->action == CXL_FW_XFER_ACTION_FULL) { 778 cci->bg.runtime = 10 * 1000UL; 779 } else { 780 cci->bg.runtime = 2 * 1000UL; 781 } 782 /* keep relevant context for bg completion */ 783 cci->fw.curr_action = fw_transfer->action; 784 cci->fw.curr_slot = fw_transfer->slot; 785 *len_out = 0; 786 787 return CXL_MBOX_BG_STARTED; 788 } 789 790 static void __do_firmware_xfer(CXLCCI *cci) 791 { 792 switch (cci->fw.curr_action) { 793 case CXL_FW_XFER_ACTION_FULL: 794 case CXL_FW_XFER_ACTION_END: 795 cci->fw.slot[cci->fw.curr_slot - 1] = true; 796 cci->fw.transferring = false; 797 break; 798 case CXL_FW_XFER_ACTION_INIT: 799 case CXL_FW_XFER_ACTION_CONTINUE: 800 time(&cci->fw.last_partxfer); 801 break; 802 default: 803 break; 804 } 805 } 806 807 /* CXL r3.1 section 8.2.9.3.3: Activate FW (Opcode 0202h) */ 808 static CXLRetCode cmd_firmware_update_activate(const struct cxl_cmd *cmd, 809 uint8_t *payload_in, 810 size_t len, 811 uint8_t *payload_out, 812 size_t *len_out, 813 CXLCCI *cci) 814 { 815 struct { 816 uint8_t action; 817 uint8_t slot; 818 } QEMU_PACKED *fw_activate = (void *)payload_in; 819 QEMU_BUILD_BUG_ON(sizeof(*fw_activate) != 0x2); 820 821 if (fw_activate->slot == 0 || 822 fw_activate->slot == cci->fw.active_slot || 823 fw_activate->slot > CXL_FW_SLOTS) { 824 return CXL_MBOX_FW_INVALID_SLOT; 825 } 826 827 /* ensure that an actual fw package is there */ 828 if (!cci->fw.slot[fw_activate->slot - 1]) { 829 return CXL_MBOX_FW_INVALID_SLOT; 830 } 831 832 switch (fw_activate->action) { 833 case 0: /* online */ 834 cci->fw.active_slot = fw_activate->slot; 835 break; 836 case 1: /* reset */ 837 cci->fw.staged_slot = fw_activate->slot; 838 break; 839 default: 840 return CXL_MBOX_INVALID_INPUT; 841 } 842 843 return CXL_MBOX_SUCCESS; 844 } 845 846 /* CXL r3.1 Section 8.2.9.4.1: Get Timestamp (Opcode 0300h) */ 847 static CXLRetCode cmd_timestamp_get(const struct cxl_cmd *cmd, 848 uint8_t *payload_in, 849 size_t len_in, 850 uint8_t *payload_out, 851 size_t *len_out, 852 CXLCCI *cci) 853 { 854 CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; 855 uint64_t final_time = cxl_device_get_timestamp(cxl_dstate); 856 857 stq_le_p(payload_out, final_time); 858 *len_out = 8; 859 860 return CXL_MBOX_SUCCESS; 861 } 862 863 /* CXL r3.1 Section 8.2.9.4.2: Set Timestamp (Opcode 0301h) */ 864 static CXLRetCode cmd_timestamp_set(const struct cxl_cmd *cmd, 865 uint8_t *payload_in, 866 size_t len_in, 867 uint8_t *payload_out, 868 size_t *len_out, 869 CXLCCI *cci) 870 { 871 CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; 872 873 cxl_dstate->timestamp.set = true; 874 cxl_dstate->timestamp.last_set = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 875 876 cxl_dstate->timestamp.host_set = le64_to_cpu(*(uint64_t *)payload_in); 877 878 *len_out = 0; 879 return CXL_MBOX_SUCCESS; 880 } 881 882 /* CXL r3.1 Section 8.2.9.5.2.1: Command Effects Log (CEL) */ 883 static const QemuUUID cel_uuid = { 884 .data = UUID(0x0da9c0b5, 0xbf41, 0x4b78, 0x8f, 0x79, 885 0x96, 0xb1, 0x62, 0x3b, 0x3f, 0x17) 886 }; 887 888 /* CXL r3.1 Section 8.2.9.5.1: Get Supported Logs (Opcode 0400h) */ 889 static CXLRetCode cmd_logs_get_supported(const struct cxl_cmd *cmd, 890 uint8_t *payload_in, 891 size_t len_in, 892 uint8_t *payload_out, 893 size_t *len_out, 894 CXLCCI *cci) 895 { 896 struct { 897 uint16_t entries; 898 uint8_t rsvd[6]; 899 struct { 900 QemuUUID uuid; 901 uint32_t size; 902 } log_entries[1]; 903 } QEMU_PACKED *supported_logs = (void *)payload_out; 904 QEMU_BUILD_BUG_ON(sizeof(*supported_logs) != 0x1c); 905 906 supported_logs->entries = 1; 907 supported_logs->log_entries[0].uuid = cel_uuid; 908 supported_logs->log_entries[0].size = 4 * cci->cel_size; 909 910 *len_out = sizeof(*supported_logs); 911 return CXL_MBOX_SUCCESS; 912 } 913 914 /* CXL r3.1 Section 8.2.9.5.2: Get Log (Opcode 0401h) */ 915 static CXLRetCode cmd_logs_get_log(const struct cxl_cmd *cmd, 916 uint8_t *payload_in, 917 size_t len_in, 918 uint8_t *payload_out, 919 size_t *len_out, 920 CXLCCI *cci) 921 { 922 struct { 923 QemuUUID uuid; 924 uint32_t offset; 925 uint32_t length; 926 } QEMU_PACKED QEMU_ALIGNED(16) *get_log; 927 928 get_log = (void *)payload_in; 929 930 /* 931 * CXL r3.1 Section 8.2.9.5.2: Get Log (Opcode 0401h) 932 * The device shall return Invalid Input if the Offset or Length 933 * fields attempt to access beyond the size of the log as reported by Get 934 * Supported Logs. 935 * 936 * The CEL buffer is large enough to fit all commands in the emulation, so 937 * the only possible failure would be if the mailbox itself isn't big 938 * enough. 939 */ 940 if (get_log->offset + get_log->length > cci->payload_max) { 941 return CXL_MBOX_INVALID_INPUT; 942 } 943 944 if (!qemu_uuid_is_equal(&get_log->uuid, &cel_uuid)) { 945 return CXL_MBOX_INVALID_LOG; 946 } 947 948 /* Store off everything to local variables so we can wipe out the payload */ 949 *len_out = get_log->length; 950 951 memmove(payload_out, cci->cel_log + get_log->offset, get_log->length); 952 953 return CXL_MBOX_SUCCESS; 954 } 955 956 /* CXL r3.1 section 8.2.9.6: Features */ 957 /* 958 * Get Supported Features output payload 959 * CXL r3.1 section 8.2.9.6.1 Table 8-96 960 */ 961 typedef struct CXLSupportedFeatureHeader { 962 uint16_t entries; 963 uint16_t nsuppfeats_dev; 964 uint32_t reserved; 965 } QEMU_PACKED CXLSupportedFeatureHeader; 966 967 /* 968 * Get Supported Features Supported Feature Entry 969 * CXL r3.1 section 8.2.9.6.1 Table 8-97 970 */ 971 typedef struct CXLSupportedFeatureEntry { 972 QemuUUID uuid; 973 uint16_t feat_index; 974 uint16_t get_feat_size; 975 uint16_t set_feat_size; 976 uint32_t attr_flags; 977 uint8_t get_feat_version; 978 uint8_t set_feat_version; 979 uint16_t set_feat_effects; 980 uint8_t rsvd[18]; 981 } QEMU_PACKED CXLSupportedFeatureEntry; 982 983 /* 984 * Get Supported Features Supported Feature Entry 985 * CXL rev 3.1 section 8.2.9.6.1 Table 8-97 986 */ 987 /* Supported Feature Entry : attribute flags */ 988 #define CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE BIT(0) 989 #define CXL_FEAT_ENTRY_ATTR_FLAG_DEEPEST_RESET_PERSISTENCE_MASK GENMASK(3, 1) 990 #define CXL_FEAT_ENTRY_ATTR_FLAG_PERSIST_ACROSS_FIRMWARE_UPDATE BIT(4) 991 #define CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SELECTION BIT(5) 992 #define CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_SAVED_SELECTION BIT(6) 993 994 /* Supported Feature Entry : set feature effects */ 995 #define CXL_FEAT_ENTRY_SFE_CONFIG_CHANGE_COLD_RESET BIT(0) 996 #define CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE BIT(1) 997 #define CXL_FEAT_ENTRY_SFE_IMMEDIATE_DATA_CHANGE BIT(2) 998 #define CXL_FEAT_ENTRY_SFE_IMMEDIATE_POLICY_CHANGE BIT(3) 999 #define CXL_FEAT_ENTRY_SFE_IMMEDIATE_LOG_CHANGE BIT(4) 1000 #define CXL_FEAT_ENTRY_SFE_SECURITY_STATE_CHANGE BIT(5) 1001 #define CXL_FEAT_ENTRY_SFE_BACKGROUND_OPERATION BIT(6) 1002 #define CXL_FEAT_ENTRY_SFE_SUPPORT_SECONDARY_MAILBOX BIT(7) 1003 #define CXL_FEAT_ENTRY_SFE_SUPPORT_ABORT_BACKGROUND_OPERATION BIT(8) 1004 #define CXL_FEAT_ENTRY_SFE_CEL_VALID BIT(9) 1005 #define CXL_FEAT_ENTRY_SFE_CONFIG_CHANGE_CONV_RESET BIT(10) 1006 #define CXL_FEAT_ENTRY_SFE_CONFIG_CHANGE_CXL_RESET BIT(11) 1007 1008 enum CXL_SUPPORTED_FEATURES_LIST { 1009 CXL_FEATURE_PATROL_SCRUB = 0, 1010 CXL_FEATURE_ECS, 1011 CXL_FEATURE_MAX 1012 }; 1013 1014 /* Get Feature CXL 3.1 Spec 8.2.9.6.2 */ 1015 /* 1016 * Get Feature input payload 1017 * CXL r3.1 section 8.2.9.6.2 Table 8-99 1018 */ 1019 /* Get Feature : Payload in selection */ 1020 enum CXL_GET_FEATURE_SELECTION { 1021 CXL_GET_FEATURE_SEL_CURRENT_VALUE, 1022 CXL_GET_FEATURE_SEL_DEFAULT_VALUE, 1023 CXL_GET_FEATURE_SEL_SAVED_VALUE, 1024 CXL_GET_FEATURE_SEL_MAX 1025 }; 1026 1027 /* Set Feature CXL 3.1 Spec 8.2.9.6.3 */ 1028 /* 1029 * Set Feature input payload 1030 * CXL r3.1 section 8.2.9.6.3 Table 8-101 1031 */ 1032 typedef struct CXLSetFeatureInHeader { 1033 QemuUUID uuid; 1034 uint32_t flags; 1035 uint16_t offset; 1036 uint8_t version; 1037 uint8_t rsvd[9]; 1038 } QEMU_PACKED QEMU_ALIGNED(16) CXLSetFeatureInHeader; 1039 1040 /* Set Feature : Payload in flags */ 1041 #define CXL_SET_FEATURE_FLAG_DATA_TRANSFER_MASK 0x7 1042 enum CXL_SET_FEATURE_FLAG_DATA_TRANSFER { 1043 CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER, 1044 CXL_SET_FEATURE_FLAG_INITIATE_DATA_TRANSFER, 1045 CXL_SET_FEATURE_FLAG_CONTINUE_DATA_TRANSFER, 1046 CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER, 1047 CXL_SET_FEATURE_FLAG_ABORT_DATA_TRANSFER, 1048 CXL_SET_FEATURE_FLAG_DATA_TRANSFER_MAX 1049 }; 1050 #define CXL_SET_FEAT_DATA_SAVED_ACROSS_RESET BIT(3) 1051 1052 /* CXL r3.1 section 8.2.9.9.11.1: Device Patrol Scrub Control Feature */ 1053 static const QemuUUID patrol_scrub_uuid = { 1054 .data = UUID(0x96dad7d6, 0xfde8, 0x482b, 0xa7, 0x33, 1055 0x75, 0x77, 0x4e, 0x06, 0xdb, 0x8a) 1056 }; 1057 1058 typedef struct CXLMemPatrolScrubSetFeature { 1059 CXLSetFeatureInHeader hdr; 1060 CXLMemPatrolScrubWriteAttrs feat_data; 1061 } QEMU_PACKED QEMU_ALIGNED(16) CXLMemPatrolScrubSetFeature; 1062 1063 /* 1064 * CXL r3.1 section 8.2.9.9.11.2: 1065 * DDR5 Error Check Scrub (ECS) Control Feature 1066 */ 1067 static const QemuUUID ecs_uuid = { 1068 .data = UUID(0xe5b13f22, 0x2328, 0x4a14, 0xb8, 0xba, 1069 0xb9, 0x69, 0x1e, 0x89, 0x33, 0x86) 1070 }; 1071 1072 typedef struct CXLMemECSSetFeature { 1073 CXLSetFeatureInHeader hdr; 1074 CXLMemECSWriteAttrs feat_data[]; 1075 } QEMU_PACKED QEMU_ALIGNED(16) CXLMemECSSetFeature; 1076 1077 /* CXL r3.1 section 8.2.9.6.1: Get Supported Features (Opcode 0500h) */ 1078 static CXLRetCode cmd_features_get_supported(const struct cxl_cmd *cmd, 1079 uint8_t *payload_in, 1080 size_t len_in, 1081 uint8_t *payload_out, 1082 size_t *len_out, 1083 CXLCCI *cci) 1084 { 1085 struct { 1086 uint32_t count; 1087 uint16_t start_index; 1088 uint16_t reserved; 1089 } QEMU_PACKED QEMU_ALIGNED(16) * get_feats_in = (void *)payload_in; 1090 1091 struct { 1092 CXLSupportedFeatureHeader hdr; 1093 CXLSupportedFeatureEntry feat_entries[]; 1094 } QEMU_PACKED QEMU_ALIGNED(16) * get_feats_out = (void *)payload_out; 1095 uint16_t index, req_entries; 1096 uint16_t entry; 1097 1098 if (!object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) { 1099 return CXL_MBOX_UNSUPPORTED; 1100 } 1101 if (get_feats_in->count < sizeof(CXLSupportedFeatureHeader) || 1102 get_feats_in->start_index >= CXL_FEATURE_MAX) { 1103 return CXL_MBOX_INVALID_INPUT; 1104 } 1105 1106 req_entries = (get_feats_in->count - 1107 sizeof(CXLSupportedFeatureHeader)) / 1108 sizeof(CXLSupportedFeatureEntry); 1109 req_entries = MIN(req_entries, 1110 (CXL_FEATURE_MAX - get_feats_in->start_index)); 1111 1112 for (entry = 0, index = get_feats_in->start_index; 1113 entry < req_entries; index++) { 1114 switch (index) { 1115 case CXL_FEATURE_PATROL_SCRUB: 1116 /* Fill supported feature entry for device patrol scrub control */ 1117 get_feats_out->feat_entries[entry++] = 1118 (struct CXLSupportedFeatureEntry) { 1119 .uuid = patrol_scrub_uuid, 1120 .feat_index = index, 1121 .get_feat_size = sizeof(CXLMemPatrolScrubReadAttrs), 1122 .set_feat_size = sizeof(CXLMemPatrolScrubWriteAttrs), 1123 .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE, 1124 .get_feat_version = CXL_MEMDEV_PS_GET_FEATURE_VERSION, 1125 .set_feat_version = CXL_MEMDEV_PS_SET_FEATURE_VERSION, 1126 .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE | 1127 CXL_FEAT_ENTRY_SFE_CEL_VALID, 1128 }; 1129 break; 1130 case CXL_FEATURE_ECS: 1131 /* Fill supported feature entry for device DDR5 ECS control */ 1132 get_feats_out->feat_entries[entry++] = 1133 (struct CXLSupportedFeatureEntry) { 1134 .uuid = ecs_uuid, 1135 .feat_index = index, 1136 .get_feat_size = CXL_ECS_NUM_MEDIA_FRUS * 1137 sizeof(CXLMemECSReadAttrs), 1138 .set_feat_size = CXL_ECS_NUM_MEDIA_FRUS * 1139 sizeof(CXLMemECSWriteAttrs), 1140 .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE, 1141 .get_feat_version = CXL_ECS_GET_FEATURE_VERSION, 1142 .set_feat_version = CXL_ECS_SET_FEATURE_VERSION, 1143 .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE | 1144 CXL_FEAT_ENTRY_SFE_CEL_VALID, 1145 }; 1146 break; 1147 default: 1148 __builtin_unreachable(); 1149 } 1150 } 1151 get_feats_out->hdr.nsuppfeats_dev = CXL_FEATURE_MAX; 1152 get_feats_out->hdr.entries = req_entries; 1153 *len_out = sizeof(CXLSupportedFeatureHeader) + 1154 req_entries * sizeof(CXLSupportedFeatureEntry); 1155 1156 return CXL_MBOX_SUCCESS; 1157 } 1158 1159 /* CXL r3.1 section 8.2.9.6.2: Get Feature (Opcode 0501h) */ 1160 static CXLRetCode cmd_features_get_feature(const struct cxl_cmd *cmd, 1161 uint8_t *payload_in, 1162 size_t len_in, 1163 uint8_t *payload_out, 1164 size_t *len_out, 1165 CXLCCI *cci) 1166 { 1167 struct { 1168 QemuUUID uuid; 1169 uint16_t offset; 1170 uint16_t count; 1171 uint8_t selection; 1172 } QEMU_PACKED QEMU_ALIGNED(16) * get_feature; 1173 uint16_t bytes_to_copy = 0; 1174 CXLType3Dev *ct3d; 1175 CXLSetFeatureInfo *set_feat_info; 1176 1177 if (!object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) { 1178 return CXL_MBOX_UNSUPPORTED; 1179 } 1180 1181 ct3d = CXL_TYPE3(cci->d); 1182 get_feature = (void *)payload_in; 1183 1184 set_feat_info = &ct3d->set_feat_info; 1185 if (qemu_uuid_is_equal(&get_feature->uuid, &set_feat_info->uuid)) { 1186 return CXL_MBOX_FEATURE_TRANSFER_IN_PROGRESS; 1187 } 1188 1189 if (get_feature->selection != CXL_GET_FEATURE_SEL_CURRENT_VALUE) { 1190 return CXL_MBOX_UNSUPPORTED; 1191 } 1192 if (get_feature->offset + get_feature->count > cci->payload_max) { 1193 return CXL_MBOX_INVALID_INPUT; 1194 } 1195 1196 if (qemu_uuid_is_equal(&get_feature->uuid, &patrol_scrub_uuid)) { 1197 if (get_feature->offset >= sizeof(CXLMemPatrolScrubReadAttrs)) { 1198 return CXL_MBOX_INVALID_INPUT; 1199 } 1200 bytes_to_copy = sizeof(CXLMemPatrolScrubReadAttrs) - 1201 get_feature->offset; 1202 bytes_to_copy = MIN(bytes_to_copy, get_feature->count); 1203 memcpy(payload_out, 1204 (uint8_t *)&ct3d->patrol_scrub_attrs + get_feature->offset, 1205 bytes_to_copy); 1206 } else if (qemu_uuid_is_equal(&get_feature->uuid, &ecs_uuid)) { 1207 if (get_feature->offset >= CXL_ECS_NUM_MEDIA_FRUS * 1208 sizeof(CXLMemECSReadAttrs)) { 1209 return CXL_MBOX_INVALID_INPUT; 1210 } 1211 bytes_to_copy = CXL_ECS_NUM_MEDIA_FRUS * 1212 sizeof(CXLMemECSReadAttrs) - 1213 get_feature->offset; 1214 bytes_to_copy = MIN(bytes_to_copy, get_feature->count); 1215 memcpy(payload_out, 1216 (uint8_t *)&ct3d->ecs_attrs + get_feature->offset, 1217 bytes_to_copy); 1218 } else { 1219 return CXL_MBOX_UNSUPPORTED; 1220 } 1221 1222 *len_out = bytes_to_copy; 1223 1224 return CXL_MBOX_SUCCESS; 1225 } 1226 1227 /* CXL r3.1 section 8.2.9.6.3: Set Feature (Opcode 0502h) */ 1228 static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd, 1229 uint8_t *payload_in, 1230 size_t len_in, 1231 uint8_t *payload_out, 1232 size_t *len_out, 1233 CXLCCI *cci) 1234 { 1235 CXLSetFeatureInHeader *hdr = (void *)payload_in; 1236 CXLMemPatrolScrubWriteAttrs *ps_write_attrs; 1237 CXLMemPatrolScrubSetFeature *ps_set_feature; 1238 CXLMemECSWriteAttrs *ecs_write_attrs; 1239 CXLMemECSSetFeature *ecs_set_feature; 1240 CXLSetFeatureInfo *set_feat_info; 1241 uint16_t bytes_to_copy = 0; 1242 uint8_t data_transfer_flag; 1243 CXLType3Dev *ct3d; 1244 uint16_t count; 1245 1246 1247 if (!object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) { 1248 return CXL_MBOX_UNSUPPORTED; 1249 } 1250 ct3d = CXL_TYPE3(cci->d); 1251 set_feat_info = &ct3d->set_feat_info; 1252 1253 if (!qemu_uuid_is_null(&set_feat_info->uuid) && 1254 !qemu_uuid_is_equal(&hdr->uuid, &set_feat_info->uuid)) { 1255 return CXL_MBOX_FEATURE_TRANSFER_IN_PROGRESS; 1256 } 1257 if (hdr->flags & CXL_SET_FEAT_DATA_SAVED_ACROSS_RESET) { 1258 set_feat_info->data_saved_across_reset = true; 1259 } else { 1260 set_feat_info->data_saved_across_reset = false; 1261 } 1262 1263 data_transfer_flag = 1264 hdr->flags & CXL_SET_FEATURE_FLAG_DATA_TRANSFER_MASK; 1265 if (data_transfer_flag == CXL_SET_FEATURE_FLAG_INITIATE_DATA_TRANSFER) { 1266 set_feat_info->uuid = hdr->uuid; 1267 set_feat_info->data_size = 0; 1268 } 1269 set_feat_info->data_transfer_flag = data_transfer_flag; 1270 set_feat_info->data_offset = hdr->offset; 1271 bytes_to_copy = len_in - sizeof(CXLSetFeatureInHeader); 1272 1273 if (qemu_uuid_is_equal(&hdr->uuid, &patrol_scrub_uuid)) { 1274 if (hdr->version != CXL_MEMDEV_PS_SET_FEATURE_VERSION) { 1275 return CXL_MBOX_UNSUPPORTED; 1276 } 1277 1278 ps_set_feature = (void *)payload_in; 1279 ps_write_attrs = &ps_set_feature->feat_data; 1280 memcpy((uint8_t *)&ct3d->patrol_scrub_wr_attrs + hdr->offset, 1281 ps_write_attrs, 1282 bytes_to_copy); 1283 set_feat_info->data_size += bytes_to_copy; 1284 1285 if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER || 1286 data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) { 1287 ct3d->patrol_scrub_attrs.scrub_cycle &= ~0xFF; 1288 ct3d->patrol_scrub_attrs.scrub_cycle |= 1289 ct3d->patrol_scrub_wr_attrs.scrub_cycle_hr & 0xFF; 1290 ct3d->patrol_scrub_attrs.scrub_flags &= ~0x1; 1291 ct3d->patrol_scrub_attrs.scrub_flags |= 1292 ct3d->patrol_scrub_wr_attrs.scrub_flags & 0x1; 1293 } 1294 } else if (qemu_uuid_is_equal(&hdr->uuid, 1295 &ecs_uuid)) { 1296 if (hdr->version != CXL_ECS_SET_FEATURE_VERSION) { 1297 return CXL_MBOX_UNSUPPORTED; 1298 } 1299 1300 ecs_set_feature = (void *)payload_in; 1301 ecs_write_attrs = ecs_set_feature->feat_data; 1302 memcpy((uint8_t *)ct3d->ecs_wr_attrs + hdr->offset, 1303 ecs_write_attrs, 1304 bytes_to_copy); 1305 set_feat_info->data_size += bytes_to_copy; 1306 1307 if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER || 1308 data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) { 1309 for (count = 0; count < CXL_ECS_NUM_MEDIA_FRUS; count++) { 1310 ct3d->ecs_attrs[count].ecs_log_cap = 1311 ct3d->ecs_wr_attrs[count].ecs_log_cap; 1312 ct3d->ecs_attrs[count].ecs_config = 1313 ct3d->ecs_wr_attrs[count].ecs_config & 0x1F; 1314 } 1315 } 1316 } else { 1317 return CXL_MBOX_UNSUPPORTED; 1318 } 1319 1320 if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER || 1321 data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER || 1322 data_transfer_flag == CXL_SET_FEATURE_FLAG_ABORT_DATA_TRANSFER) { 1323 memset(&set_feat_info->uuid, 0, sizeof(QemuUUID)); 1324 if (qemu_uuid_is_equal(&hdr->uuid, &patrol_scrub_uuid)) { 1325 memset(&ct3d->patrol_scrub_wr_attrs, 0, set_feat_info->data_size); 1326 } else if (qemu_uuid_is_equal(&hdr->uuid, &ecs_uuid)) { 1327 memset(ct3d->ecs_wr_attrs, 0, set_feat_info->data_size); 1328 } 1329 set_feat_info->data_transfer_flag = 0; 1330 set_feat_info->data_saved_across_reset = false; 1331 set_feat_info->data_offset = 0; 1332 set_feat_info->data_size = 0; 1333 } 1334 1335 return CXL_MBOX_SUCCESS; 1336 } 1337 1338 /* CXL r3.1 Section 8.2.9.9.1.1: Identify Memory Device (Opcode 4000h) */ 1339 static CXLRetCode cmd_identify_memory_device(const struct cxl_cmd *cmd, 1340 uint8_t *payload_in, 1341 size_t len_in, 1342 uint8_t *payload_out, 1343 size_t *len_out, 1344 CXLCCI *cci) 1345 { 1346 struct { 1347 char fw_revision[0x10]; 1348 uint64_t total_capacity; 1349 uint64_t volatile_capacity; 1350 uint64_t persistent_capacity; 1351 uint64_t partition_align; 1352 uint16_t info_event_log_size; 1353 uint16_t warning_event_log_size; 1354 uint16_t failure_event_log_size; 1355 uint16_t fatal_event_log_size; 1356 uint32_t lsa_size; 1357 uint8_t poison_list_max_mer[3]; 1358 uint16_t inject_poison_limit; 1359 uint8_t poison_caps; 1360 uint8_t qos_telemetry_caps; 1361 uint16_t dc_event_log_size; 1362 } QEMU_PACKED *id; 1363 QEMU_BUILD_BUG_ON(sizeof(*id) != 0x45); 1364 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 1365 CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); 1366 CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; 1367 1368 if ((!QEMU_IS_ALIGNED(cxl_dstate->vmem_size, CXL_CAPACITY_MULTIPLIER)) || 1369 (!QEMU_IS_ALIGNED(cxl_dstate->pmem_size, CXL_CAPACITY_MULTIPLIER)) || 1370 (!QEMU_IS_ALIGNED(ct3d->dc.total_capacity, CXL_CAPACITY_MULTIPLIER))) { 1371 return CXL_MBOX_INTERNAL_ERROR; 1372 } 1373 1374 id = (void *)payload_out; 1375 1376 snprintf(id->fw_revision, 0x10, "BWFW VERSION %02d", 0); 1377 1378 stq_le_p(&id->total_capacity, 1379 cxl_dstate->static_mem_size / CXL_CAPACITY_MULTIPLIER); 1380 stq_le_p(&id->persistent_capacity, 1381 cxl_dstate->pmem_size / CXL_CAPACITY_MULTIPLIER); 1382 stq_le_p(&id->volatile_capacity, 1383 cxl_dstate->vmem_size / CXL_CAPACITY_MULTIPLIER); 1384 stl_le_p(&id->lsa_size, cvc->get_lsa_size(ct3d)); 1385 /* 256 poison records */ 1386 st24_le_p(id->poison_list_max_mer, 256); 1387 /* No limit - so limited by main poison record limit */ 1388 stw_le_p(&id->inject_poison_limit, 0); 1389 stw_le_p(&id->dc_event_log_size, CXL_DC_EVENT_LOG_SIZE); 1390 1391 *len_out = sizeof(*id); 1392 return CXL_MBOX_SUCCESS; 1393 } 1394 1395 /* CXL r3.1 Section 8.2.9.9.2.1: Get Partition Info (Opcode 4100h) */ 1396 static CXLRetCode cmd_ccls_get_partition_info(const struct cxl_cmd *cmd, 1397 uint8_t *payload_in, 1398 size_t len_in, 1399 uint8_t *payload_out, 1400 size_t *len_out, 1401 CXLCCI *cci) 1402 { 1403 CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; 1404 struct { 1405 uint64_t active_vmem; 1406 uint64_t active_pmem; 1407 uint64_t next_vmem; 1408 uint64_t next_pmem; 1409 } QEMU_PACKED *part_info = (void *)payload_out; 1410 QEMU_BUILD_BUG_ON(sizeof(*part_info) != 0x20); 1411 CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); 1412 1413 if ((!QEMU_IS_ALIGNED(cxl_dstate->vmem_size, CXL_CAPACITY_MULTIPLIER)) || 1414 (!QEMU_IS_ALIGNED(cxl_dstate->pmem_size, CXL_CAPACITY_MULTIPLIER)) || 1415 (!QEMU_IS_ALIGNED(ct3d->dc.total_capacity, CXL_CAPACITY_MULTIPLIER))) { 1416 return CXL_MBOX_INTERNAL_ERROR; 1417 } 1418 1419 stq_le_p(&part_info->active_vmem, 1420 cxl_dstate->vmem_size / CXL_CAPACITY_MULTIPLIER); 1421 /* 1422 * When both next_vmem and next_pmem are 0, there is no pending change to 1423 * partitioning. 1424 */ 1425 stq_le_p(&part_info->next_vmem, 0); 1426 stq_le_p(&part_info->active_pmem, 1427 cxl_dstate->pmem_size / CXL_CAPACITY_MULTIPLIER); 1428 stq_le_p(&part_info->next_pmem, 0); 1429 1430 *len_out = sizeof(*part_info); 1431 return CXL_MBOX_SUCCESS; 1432 } 1433 1434 /* CXL r3.1 Section 8.2.9.9.2.3: Get LSA (Opcode 4102h) */ 1435 static CXLRetCode cmd_ccls_get_lsa(const struct cxl_cmd *cmd, 1436 uint8_t *payload_in, 1437 size_t len_in, 1438 uint8_t *payload_out, 1439 size_t *len_out, 1440 CXLCCI *cci) 1441 { 1442 struct { 1443 uint32_t offset; 1444 uint32_t length; 1445 } QEMU_PACKED *get_lsa; 1446 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 1447 CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); 1448 uint32_t offset, length; 1449 1450 get_lsa = (void *)payload_in; 1451 offset = get_lsa->offset; 1452 length = get_lsa->length; 1453 1454 if (offset + length > cvc->get_lsa_size(ct3d)) { 1455 *len_out = 0; 1456 return CXL_MBOX_INVALID_INPUT; 1457 } 1458 1459 *len_out = cvc->get_lsa(ct3d, payload_out, length, offset); 1460 return CXL_MBOX_SUCCESS; 1461 } 1462 1463 /* CXL r3.1 Section 8.2.9.9.2.4: Set LSA (Opcode 4103h) */ 1464 static CXLRetCode cmd_ccls_set_lsa(const struct cxl_cmd *cmd, 1465 uint8_t *payload_in, 1466 size_t len_in, 1467 uint8_t *payload_out, 1468 size_t *len_out, 1469 CXLCCI *cci) 1470 { 1471 struct set_lsa_pl { 1472 uint32_t offset; 1473 uint32_t rsvd; 1474 uint8_t data[]; 1475 } QEMU_PACKED; 1476 struct set_lsa_pl *set_lsa_payload = (void *)payload_in; 1477 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 1478 CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); 1479 const size_t hdr_len = offsetof(struct set_lsa_pl, data); 1480 1481 *len_out = 0; 1482 if (!len_in) { 1483 return CXL_MBOX_SUCCESS; 1484 } 1485 1486 if (set_lsa_payload->offset + len_in > cvc->get_lsa_size(ct3d) + hdr_len) { 1487 return CXL_MBOX_INVALID_INPUT; 1488 } 1489 len_in -= hdr_len; 1490 1491 cvc->set_lsa(ct3d, set_lsa_payload->data, len_in, set_lsa_payload->offset); 1492 return CXL_MBOX_SUCCESS; 1493 } 1494 1495 /* Perform the actual device zeroing */ 1496 static void __do_sanitization(CXLType3Dev *ct3d) 1497 { 1498 MemoryRegion *mr; 1499 1500 if (ct3d->hostvmem) { 1501 mr = host_memory_backend_get_memory(ct3d->hostvmem); 1502 if (mr) { 1503 void *hostmem = memory_region_get_ram_ptr(mr); 1504 memset(hostmem, 0, memory_region_size(mr)); 1505 } 1506 } 1507 1508 if (ct3d->hostpmem) { 1509 mr = host_memory_backend_get_memory(ct3d->hostpmem); 1510 if (mr) { 1511 void *hostmem = memory_region_get_ram_ptr(mr); 1512 memset(hostmem, 0, memory_region_size(mr)); 1513 } 1514 } 1515 if (ct3d->lsa) { 1516 mr = host_memory_backend_get_memory(ct3d->lsa); 1517 if (mr) { 1518 void *lsa = memory_region_get_ram_ptr(mr); 1519 memset(lsa, 0, memory_region_size(mr)); 1520 } 1521 } 1522 cxl_discard_all_event_records(&ct3d->cxl_dstate); 1523 } 1524 1525 /* 1526 * CXL r3.1 Section 8.2.9.9.5.1: Sanitize (Opcode 4400h) 1527 * 1528 * Once the Sanitize command has started successfully, the device shall be 1529 * placed in the media disabled state. If the command fails or is interrupted 1530 * by a reset or power failure, it shall remain in the media disabled state 1531 * until a successful Sanitize command has been completed. During this state: 1532 * 1533 * 1. Memory writes to the device will have no effect, and all memory reads 1534 * will return random values (no user data returned, even for locations that 1535 * the failed Sanitize operation didn’t sanitize yet). 1536 * 1537 * 2. Mailbox commands shall still be processed in the disabled state, except 1538 * that commands that access Sanitized areas shall fail with the Media Disabled 1539 * error code. 1540 */ 1541 static CXLRetCode cmd_sanitize_overwrite(const struct cxl_cmd *cmd, 1542 uint8_t *payload_in, 1543 size_t len_in, 1544 uint8_t *payload_out, 1545 size_t *len_out, 1546 CXLCCI *cci) 1547 { 1548 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 1549 uint64_t total_mem; /* in Mb */ 1550 int secs; 1551 1552 total_mem = (ct3d->cxl_dstate.vmem_size + ct3d->cxl_dstate.pmem_size) >> 20; 1553 if (total_mem <= 512) { 1554 secs = 4; 1555 } else if (total_mem <= 1024) { 1556 secs = 8; 1557 } else if (total_mem <= 2 * 1024) { 1558 secs = 15; 1559 } else if (total_mem <= 4 * 1024) { 1560 secs = 30; 1561 } else if (total_mem <= 8 * 1024) { 1562 secs = 60; 1563 } else if (total_mem <= 16 * 1024) { 1564 secs = 2 * 60; 1565 } else if (total_mem <= 32 * 1024) { 1566 secs = 4 * 60; 1567 } else if (total_mem <= 64 * 1024) { 1568 secs = 8 * 60; 1569 } else if (total_mem <= 128 * 1024) { 1570 secs = 15 * 60; 1571 } else if (total_mem <= 256 * 1024) { 1572 secs = 30 * 60; 1573 } else if (total_mem <= 512 * 1024) { 1574 secs = 60 * 60; 1575 } else if (total_mem <= 1024 * 1024) { 1576 secs = 120 * 60; 1577 } else { 1578 secs = 240 * 60; /* max 4 hrs */ 1579 } 1580 1581 /* EBUSY other bg cmds as of now */ 1582 cci->bg.runtime = secs * 1000UL; 1583 *len_out = 0; 1584 1585 cxl_dev_disable_media(&ct3d->cxl_dstate); 1586 1587 /* sanitize when done */ 1588 return CXL_MBOX_BG_STARTED; 1589 } 1590 1591 static CXLRetCode cmd_get_security_state(const struct cxl_cmd *cmd, 1592 uint8_t *payload_in, 1593 size_t len_in, 1594 uint8_t *payload_out, 1595 size_t *len_out, 1596 CXLCCI *cci) 1597 { 1598 uint32_t *state = (uint32_t *)payload_out; 1599 1600 *state = 0; 1601 *len_out = 4; 1602 return CXL_MBOX_SUCCESS; 1603 } 1604 1605 /* 1606 * CXL r3.1 Section 8.2.9.9.4.1: Get Poison List (Opcode 4300h) 1607 * 1608 * This is very inefficient, but good enough for now! 1609 * Also the payload will always fit, so no need to handle the MORE flag and 1610 * make this stateful. We may want to allow longer poison lists to aid 1611 * testing that kernel functionality. 1612 */ 1613 static CXLRetCode cmd_media_get_poison_list(const struct cxl_cmd *cmd, 1614 uint8_t *payload_in, 1615 size_t len_in, 1616 uint8_t *payload_out, 1617 size_t *len_out, 1618 CXLCCI *cci) 1619 { 1620 struct get_poison_list_pl { 1621 uint64_t pa; 1622 uint64_t length; 1623 } QEMU_PACKED; 1624 1625 struct get_poison_list_out_pl { 1626 uint8_t flags; 1627 uint8_t rsvd1; 1628 uint64_t overflow_timestamp; 1629 uint16_t count; 1630 uint8_t rsvd2[0x14]; 1631 struct { 1632 uint64_t addr; 1633 uint32_t length; 1634 uint32_t resv; 1635 } QEMU_PACKED records[]; 1636 } QEMU_PACKED; 1637 1638 struct get_poison_list_pl *in = (void *)payload_in; 1639 struct get_poison_list_out_pl *out = (void *)payload_out; 1640 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 1641 uint16_t record_count = 0, i = 0; 1642 uint64_t query_start, query_length; 1643 CXLPoisonList *poison_list = &ct3d->poison_list; 1644 CXLPoison *ent; 1645 uint16_t out_pl_len; 1646 1647 query_start = ldq_le_p(&in->pa); 1648 /* 64 byte alignment required */ 1649 if (query_start & 0x3f) { 1650 return CXL_MBOX_INVALID_INPUT; 1651 } 1652 query_length = ldq_le_p(&in->length) * CXL_CACHE_LINE_SIZE; 1653 1654 QLIST_FOREACH(ent, poison_list, node) { 1655 /* Check for no overlap */ 1656 if (!ranges_overlap(ent->start, ent->length, 1657 query_start, query_length)) { 1658 continue; 1659 } 1660 record_count++; 1661 } 1662 out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]); 1663 assert(out_pl_len <= CXL_MAILBOX_MAX_PAYLOAD_SIZE); 1664 1665 QLIST_FOREACH(ent, poison_list, node) { 1666 uint64_t start, stop; 1667 1668 /* Check for no overlap */ 1669 if (!ranges_overlap(ent->start, ent->length, 1670 query_start, query_length)) { 1671 continue; 1672 } 1673 1674 /* Deal with overlap */ 1675 start = MAX(ROUND_DOWN(ent->start, 64ull), query_start); 1676 stop = MIN(ROUND_DOWN(ent->start, 64ull) + ent->length, 1677 query_start + query_length); 1678 stq_le_p(&out->records[i].addr, start | (ent->type & 0x7)); 1679 stl_le_p(&out->records[i].length, (stop - start) / CXL_CACHE_LINE_SIZE); 1680 i++; 1681 } 1682 if (ct3d->poison_list_overflowed) { 1683 out->flags = (1 << 1); 1684 stq_le_p(&out->overflow_timestamp, ct3d->poison_list_overflow_ts); 1685 } 1686 if (scan_media_running(cci)) { 1687 out->flags |= (1 << 2); 1688 } 1689 1690 stw_le_p(&out->count, record_count); 1691 *len_out = out_pl_len; 1692 return CXL_MBOX_SUCCESS; 1693 } 1694 1695 /* CXL r3.1 Section 8.2.9.9.4.2: Inject Poison (Opcode 4301h) */ 1696 static CXLRetCode cmd_media_inject_poison(const struct cxl_cmd *cmd, 1697 uint8_t *payload_in, 1698 size_t len_in, 1699 uint8_t *payload_out, 1700 size_t *len_out, 1701 CXLCCI *cci) 1702 { 1703 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 1704 CXLPoisonList *poison_list = &ct3d->poison_list; 1705 CXLPoison *ent; 1706 struct inject_poison_pl { 1707 uint64_t dpa; 1708 }; 1709 struct inject_poison_pl *in = (void *)payload_in; 1710 uint64_t dpa = ldq_le_p(&in->dpa); 1711 CXLPoison *p; 1712 1713 QLIST_FOREACH(ent, poison_list, node) { 1714 if (dpa >= ent->start && 1715 dpa + CXL_CACHE_LINE_SIZE <= ent->start + ent->length) { 1716 return CXL_MBOX_SUCCESS; 1717 } 1718 } 1719 /* 1720 * Freeze the list if there is an on-going scan media operation. 1721 */ 1722 if (scan_media_running(cci)) { 1723 /* 1724 * XXX: Spec is ambiguous - is this case considered 1725 * a successful return despite not adding to the list? 1726 */ 1727 goto success; 1728 } 1729 1730 if (ct3d->poison_list_cnt == CXL_POISON_LIST_LIMIT) { 1731 return CXL_MBOX_INJECT_POISON_LIMIT; 1732 } 1733 p = g_new0(CXLPoison, 1); 1734 1735 p->length = CXL_CACHE_LINE_SIZE; 1736 p->start = dpa; 1737 p->type = CXL_POISON_TYPE_INJECTED; 1738 1739 /* 1740 * Possible todo: Merge with existing entry if next to it and if same type 1741 */ 1742 QLIST_INSERT_HEAD(poison_list, p, node); 1743 ct3d->poison_list_cnt++; 1744 success: 1745 *len_out = 0; 1746 1747 return CXL_MBOX_SUCCESS; 1748 } 1749 1750 /* CXL r3.1 Section 8.2.9.9.4.3: Clear Poison (Opcode 4302h */ 1751 static CXLRetCode cmd_media_clear_poison(const struct cxl_cmd *cmd, 1752 uint8_t *payload_in, 1753 size_t len_in, 1754 uint8_t *payload_out, 1755 size_t *len_out, 1756 CXLCCI *cci) 1757 { 1758 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 1759 CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; 1760 CXLPoisonList *poison_list = &ct3d->poison_list; 1761 CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d); 1762 struct clear_poison_pl { 1763 uint64_t dpa; 1764 uint8_t data[64]; 1765 }; 1766 CXLPoison *ent; 1767 uint64_t dpa; 1768 1769 struct clear_poison_pl *in = (void *)payload_in; 1770 1771 dpa = ldq_le_p(&in->dpa); 1772 if (dpa + CXL_CACHE_LINE_SIZE > cxl_dstate->static_mem_size + 1773 ct3d->dc.total_capacity) { 1774 return CXL_MBOX_INVALID_PA; 1775 } 1776 1777 /* Clearing a region with no poison is not an error so always do so */ 1778 if (cvc->set_cacheline) { 1779 if (!cvc->set_cacheline(ct3d, dpa, in->data)) { 1780 return CXL_MBOX_INTERNAL_ERROR; 1781 } 1782 } 1783 1784 /* 1785 * Freeze the list if there is an on-going scan media operation. 1786 */ 1787 if (scan_media_running(cci)) { 1788 /* 1789 * XXX: Spec is ambiguous - is this case considered 1790 * a successful return despite not removing from the list? 1791 */ 1792 goto success; 1793 } 1794 1795 QLIST_FOREACH(ent, poison_list, node) { 1796 /* 1797 * Test for contained in entry. Simpler than general case 1798 * as clearing 64 bytes and entries 64 byte aligned 1799 */ 1800 if ((dpa >= ent->start) && (dpa < ent->start + ent->length)) { 1801 break; 1802 } 1803 } 1804 if (!ent) { 1805 goto success; 1806 } 1807 1808 QLIST_REMOVE(ent, node); 1809 ct3d->poison_list_cnt--; 1810 1811 if (dpa > ent->start) { 1812 CXLPoison *frag; 1813 /* Cannot overflow as replacing existing entry */ 1814 1815 frag = g_new0(CXLPoison, 1); 1816 1817 frag->start = ent->start; 1818 frag->length = dpa - ent->start; 1819 frag->type = ent->type; 1820 1821 QLIST_INSERT_HEAD(poison_list, frag, node); 1822 ct3d->poison_list_cnt++; 1823 } 1824 1825 if (dpa + CXL_CACHE_LINE_SIZE < ent->start + ent->length) { 1826 CXLPoison *frag; 1827 1828 if (ct3d->poison_list_cnt == CXL_POISON_LIST_LIMIT) { 1829 cxl_set_poison_list_overflowed(ct3d); 1830 } else { 1831 frag = g_new0(CXLPoison, 1); 1832 1833 frag->start = dpa + CXL_CACHE_LINE_SIZE; 1834 frag->length = ent->start + ent->length - frag->start; 1835 frag->type = ent->type; 1836 QLIST_INSERT_HEAD(poison_list, frag, node); 1837 ct3d->poison_list_cnt++; 1838 } 1839 } 1840 /* Any fragments have been added, free original entry */ 1841 g_free(ent); 1842 success: 1843 *len_out = 0; 1844 1845 return CXL_MBOX_SUCCESS; 1846 } 1847 1848 /* 1849 * CXL r3.1 section 8.2.9.9.4.4: Get Scan Media Capabilities 1850 */ 1851 static CXLRetCode 1852 cmd_media_get_scan_media_capabilities(const struct cxl_cmd *cmd, 1853 uint8_t *payload_in, 1854 size_t len_in, 1855 uint8_t *payload_out, 1856 size_t *len_out, 1857 CXLCCI *cci) 1858 { 1859 struct get_scan_media_capabilities_pl { 1860 uint64_t pa; 1861 uint64_t length; 1862 } QEMU_PACKED; 1863 1864 struct get_scan_media_capabilities_out_pl { 1865 uint32_t estimated_runtime_ms; 1866 }; 1867 1868 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 1869 CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; 1870 struct get_scan_media_capabilities_pl *in = (void *)payload_in; 1871 struct get_scan_media_capabilities_out_pl *out = (void *)payload_out; 1872 uint64_t query_start; 1873 uint64_t query_length; 1874 1875 query_start = ldq_le_p(&in->pa); 1876 /* 64 byte alignment required */ 1877 if (query_start & 0x3f) { 1878 return CXL_MBOX_INVALID_INPUT; 1879 } 1880 query_length = ldq_le_p(&in->length) * CXL_CACHE_LINE_SIZE; 1881 1882 if (query_start + query_length > cxl_dstate->static_mem_size) { 1883 return CXL_MBOX_INVALID_PA; 1884 } 1885 1886 /* 1887 * Just use 400 nanosecond access/read latency + 100 ns for 1888 * the cost of updating the poison list. For small enough 1889 * chunks return at least 1 ms. 1890 */ 1891 stl_le_p(&out->estimated_runtime_ms, 1892 MAX(1, query_length * (0.0005L / 64))); 1893 1894 *len_out = sizeof(*out); 1895 return CXL_MBOX_SUCCESS; 1896 } 1897 1898 static void __do_scan_media(CXLType3Dev *ct3d) 1899 { 1900 CXLPoison *ent; 1901 unsigned int results_cnt = 0; 1902 1903 QLIST_FOREACH(ent, &ct3d->scan_media_results, node) { 1904 results_cnt++; 1905 } 1906 1907 /* only scan media may clear the overflow */ 1908 if (ct3d->poison_list_overflowed && 1909 ct3d->poison_list_cnt == results_cnt) { 1910 cxl_clear_poison_list_overflowed(ct3d); 1911 } 1912 /* scan media has run since last conventional reset */ 1913 ct3d->scan_media_hasrun = true; 1914 } 1915 1916 /* 1917 * CXL r3.1 section 8.2.9.9.4.5: Scan Media 1918 */ 1919 static CXLRetCode cmd_media_scan_media(const struct cxl_cmd *cmd, 1920 uint8_t *payload_in, 1921 size_t len_in, 1922 uint8_t *payload_out, 1923 size_t *len_out, 1924 CXLCCI *cci) 1925 { 1926 struct scan_media_pl { 1927 uint64_t pa; 1928 uint64_t length; 1929 uint8_t flags; 1930 } QEMU_PACKED; 1931 1932 struct scan_media_pl *in = (void *)payload_in; 1933 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 1934 CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; 1935 uint64_t query_start; 1936 uint64_t query_length; 1937 CXLPoison *ent, *next; 1938 1939 query_start = ldq_le_p(&in->pa); 1940 /* 64 byte alignment required */ 1941 if (query_start & 0x3f) { 1942 return CXL_MBOX_INVALID_INPUT; 1943 } 1944 query_length = ldq_le_p(&in->length) * CXL_CACHE_LINE_SIZE; 1945 1946 if (query_start + query_length > cxl_dstate->static_mem_size) { 1947 return CXL_MBOX_INVALID_PA; 1948 } 1949 if (ct3d->dc.num_regions && query_start + query_length >= 1950 cxl_dstate->static_mem_size + ct3d->dc.total_capacity) { 1951 return CXL_MBOX_INVALID_PA; 1952 } 1953 1954 if (in->flags == 0) { /* TODO */ 1955 qemu_log_mask(LOG_UNIMP, 1956 "Scan Media Event Log is unsupported\n"); 1957 } 1958 1959 /* any previous results are discarded upon a new Scan Media */ 1960 QLIST_FOREACH_SAFE(ent, &ct3d->scan_media_results, node, next) { 1961 QLIST_REMOVE(ent, node); 1962 g_free(ent); 1963 } 1964 1965 /* kill the poison list - it will be recreated */ 1966 if (ct3d->poison_list_overflowed) { 1967 QLIST_FOREACH_SAFE(ent, &ct3d->poison_list, node, next) { 1968 QLIST_REMOVE(ent, node); 1969 g_free(ent); 1970 ct3d->poison_list_cnt--; 1971 } 1972 } 1973 1974 /* 1975 * Scan the backup list and move corresponding entries 1976 * into the results list, updating the poison list 1977 * when possible. 1978 */ 1979 QLIST_FOREACH_SAFE(ent, &ct3d->poison_list_bkp, node, next) { 1980 CXLPoison *res; 1981 1982 if (ent->start >= query_start + query_length || 1983 ent->start + ent->length <= query_start) { 1984 continue; 1985 } 1986 1987 /* 1988 * If a Get Poison List cmd comes in while this 1989 * scan is being done, it will see the new complete 1990 * list, while setting the respective flag. 1991 */ 1992 if (ct3d->poison_list_cnt < CXL_POISON_LIST_LIMIT) { 1993 CXLPoison *p = g_new0(CXLPoison, 1); 1994 1995 p->start = ent->start; 1996 p->length = ent->length; 1997 p->type = ent->type; 1998 QLIST_INSERT_HEAD(&ct3d->poison_list, p, node); 1999 ct3d->poison_list_cnt++; 2000 } 2001 2002 res = g_new0(CXLPoison, 1); 2003 res->start = ent->start; 2004 res->length = ent->length; 2005 res->type = ent->type; 2006 QLIST_INSERT_HEAD(&ct3d->scan_media_results, res, node); 2007 2008 QLIST_REMOVE(ent, node); 2009 g_free(ent); 2010 } 2011 2012 cci->bg.runtime = MAX(1, query_length * (0.0005L / 64)); 2013 *len_out = 0; 2014 2015 return CXL_MBOX_BG_STARTED; 2016 } 2017 2018 /* 2019 * CXL r3.1 section 8.2.9.9.4.6: Get Scan Media Results 2020 */ 2021 static CXLRetCode cmd_media_get_scan_media_results(const struct cxl_cmd *cmd, 2022 uint8_t *payload_in, 2023 size_t len_in, 2024 uint8_t *payload_out, 2025 size_t *len_out, 2026 CXLCCI *cci) 2027 { 2028 struct get_scan_media_results_out_pl { 2029 uint64_t dpa_restart; 2030 uint64_t length; 2031 uint8_t flags; 2032 uint8_t rsvd1; 2033 uint16_t count; 2034 uint8_t rsvd2[0xc]; 2035 struct { 2036 uint64_t addr; 2037 uint32_t length; 2038 uint32_t resv; 2039 } QEMU_PACKED records[]; 2040 } QEMU_PACKED; 2041 2042 struct get_scan_media_results_out_pl *out = (void *)payload_out; 2043 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 2044 CXLPoisonList *scan_media_results = &ct3d->scan_media_results; 2045 CXLPoison *ent, *next; 2046 uint16_t total_count = 0, record_count = 0, i = 0; 2047 uint16_t out_pl_len; 2048 2049 if (!ct3d->scan_media_hasrun) { 2050 return CXL_MBOX_UNSUPPORTED; 2051 } 2052 2053 /* 2054 * Calculate limits, all entries are within the same address range of the 2055 * last scan media call. 2056 */ 2057 QLIST_FOREACH(ent, scan_media_results, node) { 2058 size_t rec_size = record_count * sizeof(out->records[0]); 2059 2060 if (sizeof(*out) + rec_size < CXL_MAILBOX_MAX_PAYLOAD_SIZE) { 2061 record_count++; 2062 } 2063 total_count++; 2064 } 2065 2066 out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]); 2067 assert(out_pl_len <= CXL_MAILBOX_MAX_PAYLOAD_SIZE); 2068 2069 memset(out, 0, out_pl_len); 2070 QLIST_FOREACH_SAFE(ent, scan_media_results, node, next) { 2071 uint64_t start, stop; 2072 2073 if (i == record_count) { 2074 break; 2075 } 2076 2077 start = ROUND_DOWN(ent->start, 64ull); 2078 stop = ROUND_DOWN(ent->start, 64ull) + ent->length; 2079 stq_le_p(&out->records[i].addr, start | (ent->type & 0x7)); 2080 stl_le_p(&out->records[i].length, (stop - start) / CXL_CACHE_LINE_SIZE); 2081 i++; 2082 2083 /* consume the returning entry */ 2084 QLIST_REMOVE(ent, node); 2085 g_free(ent); 2086 } 2087 2088 stw_le_p(&out->count, record_count); 2089 if (total_count > record_count) { 2090 out->flags = (1 << 0); /* More Media Error Records */ 2091 } 2092 2093 *len_out = out_pl_len; 2094 return CXL_MBOX_SUCCESS; 2095 } 2096 2097 /* 2098 * CXL r3.1 section 8.2.9.9.9.1: Get Dynamic Capacity Configuration 2099 * (Opcode: 4800h) 2100 */ 2101 static CXLRetCode cmd_dcd_get_dyn_cap_config(const struct cxl_cmd *cmd, 2102 uint8_t *payload_in, 2103 size_t len_in, 2104 uint8_t *payload_out, 2105 size_t *len_out, 2106 CXLCCI *cci) 2107 { 2108 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 2109 struct { 2110 uint8_t region_cnt; 2111 uint8_t start_rid; 2112 } QEMU_PACKED *in = (void *)payload_in; 2113 struct { 2114 uint8_t num_regions; 2115 uint8_t regions_returned; 2116 uint8_t rsvd1[6]; 2117 struct { 2118 uint64_t base; 2119 uint64_t decode_len; 2120 uint64_t region_len; 2121 uint64_t block_size; 2122 uint32_t dsmadhandle; 2123 uint8_t flags; 2124 uint8_t rsvd2[3]; 2125 } QEMU_PACKED records[]; 2126 } QEMU_PACKED *out = (void *)payload_out; 2127 struct { 2128 uint32_t num_extents_supported; 2129 uint32_t num_extents_available; 2130 uint32_t num_tags_supported; 2131 uint32_t num_tags_available; 2132 } QEMU_PACKED *extra_out; 2133 uint16_t record_count; 2134 uint16_t i; 2135 uint16_t out_pl_len; 2136 uint8_t start_rid; 2137 2138 start_rid = in->start_rid; 2139 if (start_rid >= ct3d->dc.num_regions) { 2140 return CXL_MBOX_INVALID_INPUT; 2141 } 2142 2143 record_count = MIN(ct3d->dc.num_regions - in->start_rid, in->region_cnt); 2144 2145 out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]); 2146 extra_out = (void *)(payload_out + out_pl_len); 2147 out_pl_len += sizeof(*extra_out); 2148 assert(out_pl_len <= CXL_MAILBOX_MAX_PAYLOAD_SIZE); 2149 2150 out->num_regions = ct3d->dc.num_regions; 2151 out->regions_returned = record_count; 2152 for (i = 0; i < record_count; i++) { 2153 stq_le_p(&out->records[i].base, 2154 ct3d->dc.regions[start_rid + i].base); 2155 stq_le_p(&out->records[i].decode_len, 2156 ct3d->dc.regions[start_rid + i].decode_len / 2157 CXL_CAPACITY_MULTIPLIER); 2158 stq_le_p(&out->records[i].region_len, 2159 ct3d->dc.regions[start_rid + i].len); 2160 stq_le_p(&out->records[i].block_size, 2161 ct3d->dc.regions[start_rid + i].block_size); 2162 stl_le_p(&out->records[i].dsmadhandle, 2163 ct3d->dc.regions[start_rid + i].dsmadhandle); 2164 out->records[i].flags = ct3d->dc.regions[start_rid + i].flags; 2165 } 2166 /* 2167 * TODO: Assign values once extents and tags are introduced 2168 * to use. 2169 */ 2170 stl_le_p(&extra_out->num_extents_supported, CXL_NUM_EXTENTS_SUPPORTED); 2171 stl_le_p(&extra_out->num_extents_available, CXL_NUM_EXTENTS_SUPPORTED - 2172 ct3d->dc.total_extent_count); 2173 stl_le_p(&extra_out->num_tags_supported, CXL_NUM_TAGS_SUPPORTED); 2174 stl_le_p(&extra_out->num_tags_available, CXL_NUM_TAGS_SUPPORTED); 2175 2176 *len_out = out_pl_len; 2177 return CXL_MBOX_SUCCESS; 2178 } 2179 2180 /* 2181 * CXL r3.1 section 8.2.9.9.9.2: 2182 * Get Dynamic Capacity Extent List (Opcode 4801h) 2183 */ 2184 static CXLRetCode cmd_dcd_get_dyn_cap_ext_list(const struct cxl_cmd *cmd, 2185 uint8_t *payload_in, 2186 size_t len_in, 2187 uint8_t *payload_out, 2188 size_t *len_out, 2189 CXLCCI *cci) 2190 { 2191 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 2192 struct { 2193 uint32_t extent_cnt; 2194 uint32_t start_extent_id; 2195 } QEMU_PACKED *in = (void *)payload_in; 2196 struct { 2197 uint32_t count; 2198 uint32_t total_extents; 2199 uint32_t generation_num; 2200 uint8_t rsvd[4]; 2201 CXLDCExtentRaw records[]; 2202 } QEMU_PACKED *out = (void *)payload_out; 2203 uint32_t start_extent_id = in->start_extent_id; 2204 CXLDCExtentList *extent_list = &ct3d->dc.extents; 2205 uint16_t record_count = 0, i = 0, record_done = 0; 2206 uint16_t out_pl_len, size; 2207 CXLDCExtent *ent; 2208 2209 if (start_extent_id > ct3d->dc.total_extent_count) { 2210 return CXL_MBOX_INVALID_INPUT; 2211 } 2212 2213 record_count = MIN(in->extent_cnt, 2214 ct3d->dc.total_extent_count - start_extent_id); 2215 size = CXL_MAILBOX_MAX_PAYLOAD_SIZE - sizeof(*out); 2216 record_count = MIN(record_count, size / sizeof(out->records[0])); 2217 out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]); 2218 2219 stl_le_p(&out->count, record_count); 2220 stl_le_p(&out->total_extents, ct3d->dc.total_extent_count); 2221 stl_le_p(&out->generation_num, ct3d->dc.ext_list_gen_seq); 2222 2223 if (record_count > 0) { 2224 CXLDCExtentRaw *out_rec = &out->records[record_done]; 2225 2226 QTAILQ_FOREACH(ent, extent_list, node) { 2227 if (i++ < start_extent_id) { 2228 continue; 2229 } 2230 stq_le_p(&out_rec->start_dpa, ent->start_dpa); 2231 stq_le_p(&out_rec->len, ent->len); 2232 memcpy(&out_rec->tag, ent->tag, 0x10); 2233 stw_le_p(&out_rec->shared_seq, ent->shared_seq); 2234 2235 record_done++; 2236 if (record_done == record_count) { 2237 break; 2238 } 2239 } 2240 } 2241 2242 *len_out = out_pl_len; 2243 return CXL_MBOX_SUCCESS; 2244 } 2245 2246 /* 2247 * Check whether any bit between addr[nr, nr+size) is set, 2248 * return true if any bit is set, otherwise return false 2249 */ 2250 bool test_any_bits_set(const unsigned long *addr, unsigned long nr, 2251 unsigned long size) 2252 { 2253 unsigned long res = find_next_bit(addr, size + nr, nr); 2254 2255 return res < nr + size; 2256 } 2257 2258 CXLDCRegion *cxl_find_dc_region(CXLType3Dev *ct3d, uint64_t dpa, uint64_t len) 2259 { 2260 int i; 2261 CXLDCRegion *region = &ct3d->dc.regions[0]; 2262 2263 if (dpa < region->base || 2264 dpa >= region->base + ct3d->dc.total_capacity) { 2265 return NULL; 2266 } 2267 2268 /* 2269 * CXL r3.1 section 9.13.3: Dynamic Capacity Device (DCD) 2270 * 2271 * Regions are used in increasing-DPA order, with Region 0 being used for 2272 * the lowest DPA of Dynamic Capacity and Region 7 for the highest DPA. 2273 * So check from the last region to find where the dpa belongs. Extents that 2274 * cross multiple regions are not allowed. 2275 */ 2276 for (i = ct3d->dc.num_regions - 1; i >= 0; i--) { 2277 region = &ct3d->dc.regions[i]; 2278 if (dpa >= region->base) { 2279 if (dpa + len > region->base + region->len) { 2280 return NULL; 2281 } 2282 return region; 2283 } 2284 } 2285 2286 return NULL; 2287 } 2288 2289 void cxl_insert_extent_to_extent_list(CXLDCExtentList *list, 2290 uint64_t dpa, 2291 uint64_t len, 2292 uint8_t *tag, 2293 uint16_t shared_seq) 2294 { 2295 CXLDCExtent *extent; 2296 2297 extent = g_new0(CXLDCExtent, 1); 2298 extent->start_dpa = dpa; 2299 extent->len = len; 2300 if (tag) { 2301 memcpy(extent->tag, tag, 0x10); 2302 } 2303 extent->shared_seq = shared_seq; 2304 2305 QTAILQ_INSERT_TAIL(list, extent, node); 2306 } 2307 2308 void cxl_remove_extent_from_extent_list(CXLDCExtentList *list, 2309 CXLDCExtent *extent) 2310 { 2311 QTAILQ_REMOVE(list, extent, node); 2312 g_free(extent); 2313 } 2314 2315 /* 2316 * Add a new extent to the extent "group" if group exists; 2317 * otherwise, create a new group 2318 * Return value: the extent group where the extent is inserted. 2319 */ 2320 CXLDCExtentGroup *cxl_insert_extent_to_extent_group(CXLDCExtentGroup *group, 2321 uint64_t dpa, 2322 uint64_t len, 2323 uint8_t *tag, 2324 uint16_t shared_seq) 2325 { 2326 if (!group) { 2327 group = g_new0(CXLDCExtentGroup, 1); 2328 QTAILQ_INIT(&group->list); 2329 } 2330 cxl_insert_extent_to_extent_list(&group->list, dpa, len, 2331 tag, shared_seq); 2332 return group; 2333 } 2334 2335 void cxl_extent_group_list_insert_tail(CXLDCExtentGroupList *list, 2336 CXLDCExtentGroup *group) 2337 { 2338 QTAILQ_INSERT_TAIL(list, group, node); 2339 } 2340 2341 void cxl_extent_group_list_delete_front(CXLDCExtentGroupList *list) 2342 { 2343 CXLDCExtent *ent, *ent_next; 2344 CXLDCExtentGroup *group = QTAILQ_FIRST(list); 2345 2346 QTAILQ_REMOVE(list, group, node); 2347 QTAILQ_FOREACH_SAFE(ent, &group->list, node, ent_next) { 2348 cxl_remove_extent_from_extent_list(&group->list, ent); 2349 } 2350 g_free(group); 2351 } 2352 2353 /* 2354 * CXL r3.1 Table 8-168: Add Dynamic Capacity Response Input Payload 2355 * CXL r3.1 Table 8-170: Release Dynamic Capacity Input Payload 2356 */ 2357 typedef struct CXLUpdateDCExtentListInPl { 2358 uint32_t num_entries_updated; 2359 uint8_t flags; 2360 uint8_t rsvd[3]; 2361 /* CXL r3.1 Table 8-169: Updated Extent */ 2362 struct { 2363 uint64_t start_dpa; 2364 uint64_t len; 2365 uint8_t rsvd[8]; 2366 } QEMU_PACKED updated_entries[]; 2367 } QEMU_PACKED CXLUpdateDCExtentListInPl; 2368 2369 /* 2370 * For the extents in the extent list to operate, check whether they are valid 2371 * 1. The extent should be in the range of a valid DC region; 2372 * 2. The extent should not cross multiple regions; 2373 * 3. The start DPA and the length of the extent should align with the block 2374 * size of the region; 2375 * 4. The address range of multiple extents in the list should not overlap. 2376 */ 2377 static CXLRetCode cxl_detect_malformed_extent_list(CXLType3Dev *ct3d, 2378 const CXLUpdateDCExtentListInPl *in) 2379 { 2380 uint64_t min_block_size = UINT64_MAX; 2381 CXLDCRegion *region; 2382 CXLDCRegion *lastregion = &ct3d->dc.regions[ct3d->dc.num_regions - 1]; 2383 g_autofree unsigned long *blk_bitmap = NULL; 2384 uint64_t dpa, len; 2385 uint32_t i; 2386 2387 for (i = 0; i < ct3d->dc.num_regions; i++) { 2388 region = &ct3d->dc.regions[i]; 2389 min_block_size = MIN(min_block_size, region->block_size); 2390 } 2391 2392 blk_bitmap = bitmap_new((lastregion->base + lastregion->len - 2393 ct3d->dc.regions[0].base) / min_block_size); 2394 2395 for (i = 0; i < in->num_entries_updated; i++) { 2396 dpa = in->updated_entries[i].start_dpa; 2397 len = in->updated_entries[i].len; 2398 2399 region = cxl_find_dc_region(ct3d, dpa, len); 2400 if (!region) { 2401 return CXL_MBOX_INVALID_PA; 2402 } 2403 2404 dpa -= ct3d->dc.regions[0].base; 2405 if (dpa % region->block_size || len % region->block_size) { 2406 return CXL_MBOX_INVALID_EXTENT_LIST; 2407 } 2408 /* the dpa range already covered by some other extents in the list */ 2409 if (test_any_bits_set(blk_bitmap, dpa / min_block_size, 2410 len / min_block_size)) { 2411 return CXL_MBOX_INVALID_EXTENT_LIST; 2412 } 2413 bitmap_set(blk_bitmap, dpa / min_block_size, len / min_block_size); 2414 } 2415 2416 return CXL_MBOX_SUCCESS; 2417 } 2418 2419 static CXLRetCode cxl_dcd_add_dyn_cap_rsp_dry_run(CXLType3Dev *ct3d, 2420 const CXLUpdateDCExtentListInPl *in) 2421 { 2422 uint32_t i; 2423 CXLDCExtent *ent; 2424 CXLDCExtentGroup *ext_group; 2425 uint64_t dpa, len; 2426 Range range1, range2; 2427 2428 for (i = 0; i < in->num_entries_updated; i++) { 2429 dpa = in->updated_entries[i].start_dpa; 2430 len = in->updated_entries[i].len; 2431 2432 range_init_nofail(&range1, dpa, len); 2433 2434 /* 2435 * The host-accepted DPA range must be contained by the first extent 2436 * group in the pending list 2437 */ 2438 ext_group = QTAILQ_FIRST(&ct3d->dc.extents_pending); 2439 if (!cxl_extents_contains_dpa_range(&ext_group->list, dpa, len)) { 2440 return CXL_MBOX_INVALID_PA; 2441 } 2442 2443 /* to-be-added range should not overlap with range already accepted */ 2444 QTAILQ_FOREACH(ent, &ct3d->dc.extents, node) { 2445 range_init_nofail(&range2, ent->start_dpa, ent->len); 2446 if (range_overlaps_range(&range1, &range2)) { 2447 return CXL_MBOX_INVALID_PA; 2448 } 2449 } 2450 } 2451 return CXL_MBOX_SUCCESS; 2452 } 2453 2454 /* 2455 * CXL r3.1 section 8.2.9.9.9.3: Add Dynamic Capacity Response (Opcode 4802h) 2456 * An extent is added to the extent list and becomes usable only after the 2457 * response is processed successfully. 2458 */ 2459 static CXLRetCode cmd_dcd_add_dyn_cap_rsp(const struct cxl_cmd *cmd, 2460 uint8_t *payload_in, 2461 size_t len_in, 2462 uint8_t *payload_out, 2463 size_t *len_out, 2464 CXLCCI *cci) 2465 { 2466 CXLUpdateDCExtentListInPl *in = (void *)payload_in; 2467 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 2468 CXLDCExtentList *extent_list = &ct3d->dc.extents; 2469 uint32_t i; 2470 uint64_t dpa, len; 2471 CXLRetCode ret; 2472 2473 if (in->num_entries_updated == 0) { 2474 cxl_extent_group_list_delete_front(&ct3d->dc.extents_pending); 2475 return CXL_MBOX_SUCCESS; 2476 } 2477 2478 /* Adding extents causes exceeding device's extent tracking ability. */ 2479 if (in->num_entries_updated + ct3d->dc.total_extent_count > 2480 CXL_NUM_EXTENTS_SUPPORTED) { 2481 return CXL_MBOX_RESOURCES_EXHAUSTED; 2482 } 2483 2484 ret = cxl_detect_malformed_extent_list(ct3d, in); 2485 if (ret != CXL_MBOX_SUCCESS) { 2486 return ret; 2487 } 2488 2489 ret = cxl_dcd_add_dyn_cap_rsp_dry_run(ct3d, in); 2490 if (ret != CXL_MBOX_SUCCESS) { 2491 return ret; 2492 } 2493 2494 for (i = 0; i < in->num_entries_updated; i++) { 2495 dpa = in->updated_entries[i].start_dpa; 2496 len = in->updated_entries[i].len; 2497 2498 cxl_insert_extent_to_extent_list(extent_list, dpa, len, NULL, 0); 2499 ct3d->dc.total_extent_count += 1; 2500 ct3_set_region_block_backed(ct3d, dpa, len); 2501 } 2502 /* Remove the first extent group in the pending list */ 2503 cxl_extent_group_list_delete_front(&ct3d->dc.extents_pending); 2504 2505 return CXL_MBOX_SUCCESS; 2506 } 2507 2508 /* 2509 * Copy extent list from src to dst 2510 * Return value: number of extents copied 2511 */ 2512 static uint32_t copy_extent_list(CXLDCExtentList *dst, 2513 const CXLDCExtentList *src) 2514 { 2515 uint32_t cnt = 0; 2516 CXLDCExtent *ent; 2517 2518 if (!dst || !src) { 2519 return 0; 2520 } 2521 2522 QTAILQ_FOREACH(ent, src, node) { 2523 cxl_insert_extent_to_extent_list(dst, ent->start_dpa, ent->len, 2524 ent->tag, ent->shared_seq); 2525 cnt++; 2526 } 2527 return cnt; 2528 } 2529 2530 static CXLRetCode cxl_dc_extent_release_dry_run(CXLType3Dev *ct3d, 2531 const CXLUpdateDCExtentListInPl *in, CXLDCExtentList *updated_list, 2532 uint32_t *updated_list_size) 2533 { 2534 CXLDCExtent *ent, *ent_next; 2535 uint64_t dpa, len; 2536 uint32_t i; 2537 int cnt_delta = 0; 2538 CXLRetCode ret = CXL_MBOX_SUCCESS; 2539 2540 QTAILQ_INIT(updated_list); 2541 copy_extent_list(updated_list, &ct3d->dc.extents); 2542 2543 for (i = 0; i < in->num_entries_updated; i++) { 2544 Range range; 2545 2546 dpa = in->updated_entries[i].start_dpa; 2547 len = in->updated_entries[i].len; 2548 2549 /* Check if the DPA range is not fully backed with valid extents */ 2550 if (!ct3_test_region_block_backed(ct3d, dpa, len)) { 2551 ret = CXL_MBOX_INVALID_PA; 2552 goto free_and_exit; 2553 } 2554 2555 /* After this point, extent overflow is the only error can happen */ 2556 while (len > 0) { 2557 QTAILQ_FOREACH(ent, updated_list, node) { 2558 range_init_nofail(&range, ent->start_dpa, ent->len); 2559 2560 if (range_contains(&range, dpa)) { 2561 uint64_t len1, len2 = 0, len_done = 0; 2562 uint64_t ent_start_dpa = ent->start_dpa; 2563 uint64_t ent_len = ent->len; 2564 2565 len1 = dpa - ent->start_dpa; 2566 /* Found the extent or the subset of an existing extent */ 2567 if (range_contains(&range, dpa + len - 1)) { 2568 len2 = ent_start_dpa + ent_len - dpa - len; 2569 } else { 2570 dpa = ent_start_dpa + ent_len; 2571 } 2572 len_done = ent_len - len1 - len2; 2573 2574 cxl_remove_extent_from_extent_list(updated_list, ent); 2575 cnt_delta--; 2576 2577 if (len1) { 2578 cxl_insert_extent_to_extent_list(updated_list, 2579 ent_start_dpa, 2580 len1, NULL, 0); 2581 cnt_delta++; 2582 } 2583 if (len2) { 2584 cxl_insert_extent_to_extent_list(updated_list, 2585 dpa + len, 2586 len2, NULL, 0); 2587 cnt_delta++; 2588 } 2589 2590 if (cnt_delta + ct3d->dc.total_extent_count > 2591 CXL_NUM_EXTENTS_SUPPORTED) { 2592 ret = CXL_MBOX_RESOURCES_EXHAUSTED; 2593 goto free_and_exit; 2594 } 2595 2596 len -= len_done; 2597 break; 2598 } 2599 } 2600 } 2601 } 2602 free_and_exit: 2603 if (ret != CXL_MBOX_SUCCESS) { 2604 QTAILQ_FOREACH_SAFE(ent, updated_list, node, ent_next) { 2605 cxl_remove_extent_from_extent_list(updated_list, ent); 2606 } 2607 *updated_list_size = 0; 2608 } else { 2609 *updated_list_size = ct3d->dc.total_extent_count + cnt_delta; 2610 } 2611 2612 return ret; 2613 } 2614 2615 /* 2616 * CXL r3.1 section 8.2.9.9.9.4: Release Dynamic Capacity (Opcode 4803h) 2617 */ 2618 static CXLRetCode cmd_dcd_release_dyn_cap(const struct cxl_cmd *cmd, 2619 uint8_t *payload_in, 2620 size_t len_in, 2621 uint8_t *payload_out, 2622 size_t *len_out, 2623 CXLCCI *cci) 2624 { 2625 CXLUpdateDCExtentListInPl *in = (void *)payload_in; 2626 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 2627 CXLDCExtentList updated_list; 2628 CXLDCExtent *ent, *ent_next; 2629 uint32_t updated_list_size; 2630 CXLRetCode ret; 2631 2632 if (in->num_entries_updated == 0) { 2633 return CXL_MBOX_INVALID_INPUT; 2634 } 2635 2636 ret = cxl_detect_malformed_extent_list(ct3d, in); 2637 if (ret != CXL_MBOX_SUCCESS) { 2638 return ret; 2639 } 2640 2641 ret = cxl_dc_extent_release_dry_run(ct3d, in, &updated_list, 2642 &updated_list_size); 2643 if (ret != CXL_MBOX_SUCCESS) { 2644 return ret; 2645 } 2646 2647 /* 2648 * If the dry run release passes, the returned updated_list will 2649 * be the updated extent list and we just need to clear the extents 2650 * in the accepted list and copy extents in the updated_list to accepted 2651 * list and update the extent count; 2652 */ 2653 QTAILQ_FOREACH_SAFE(ent, &ct3d->dc.extents, node, ent_next) { 2654 ct3_clear_region_block_backed(ct3d, ent->start_dpa, ent->len); 2655 cxl_remove_extent_from_extent_list(&ct3d->dc.extents, ent); 2656 } 2657 copy_extent_list(&ct3d->dc.extents, &updated_list); 2658 QTAILQ_FOREACH_SAFE(ent, &updated_list, node, ent_next) { 2659 ct3_set_region_block_backed(ct3d, ent->start_dpa, ent->len); 2660 cxl_remove_extent_from_extent_list(&updated_list, ent); 2661 } 2662 ct3d->dc.total_extent_count = updated_list_size; 2663 2664 return CXL_MBOX_SUCCESS; 2665 } 2666 2667 static const struct cxl_cmd cxl_cmd_set[256][256] = { 2668 [EVENTS][GET_RECORDS] = { "EVENTS_GET_RECORDS", 2669 cmd_events_get_records, 1, 0 }, 2670 [EVENTS][CLEAR_RECORDS] = { "EVENTS_CLEAR_RECORDS", 2671 cmd_events_clear_records, ~0, CXL_MBOX_IMMEDIATE_LOG_CHANGE }, 2672 [EVENTS][GET_INTERRUPT_POLICY] = { "EVENTS_GET_INTERRUPT_POLICY", 2673 cmd_events_get_interrupt_policy, 0, 0 }, 2674 [EVENTS][SET_INTERRUPT_POLICY] = { "EVENTS_SET_INTERRUPT_POLICY", 2675 cmd_events_set_interrupt_policy, 2676 ~0, CXL_MBOX_IMMEDIATE_CONFIG_CHANGE }, 2677 [FIRMWARE_UPDATE][GET_INFO] = { "FIRMWARE_UPDATE_GET_INFO", 2678 cmd_firmware_update_get_info, 0, 0 }, 2679 [FIRMWARE_UPDATE][TRANSFER] = { "FIRMWARE_UPDATE_TRANSFER", 2680 cmd_firmware_update_transfer, ~0, CXL_MBOX_BACKGROUND_OPERATION }, 2681 [FIRMWARE_UPDATE][ACTIVATE] = { "FIRMWARE_UPDATE_ACTIVATE", 2682 cmd_firmware_update_activate, 2, CXL_MBOX_BACKGROUND_OPERATION }, 2683 [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 }, 2684 [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set, 2685 8, CXL_MBOX_IMMEDIATE_POLICY_CHANGE }, 2686 [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 2687 0, 0 }, 2688 [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, 2689 [FEATURES][GET_SUPPORTED] = { "FEATURES_GET_SUPPORTED", 2690 cmd_features_get_supported, 0x8, 0 }, 2691 [FEATURES][GET_FEATURE] = { "FEATURES_GET_FEATURE", 2692 cmd_features_get_feature, 0x15, 0 }, 2693 [FEATURES][SET_FEATURE] = { "FEATURES_SET_FEATURE", 2694 cmd_features_set_feature, 2695 ~0, 2696 (CXL_MBOX_IMMEDIATE_CONFIG_CHANGE | 2697 CXL_MBOX_IMMEDIATE_DATA_CHANGE | 2698 CXL_MBOX_IMMEDIATE_POLICY_CHANGE | 2699 CXL_MBOX_IMMEDIATE_LOG_CHANGE | 2700 CXL_MBOX_SECURITY_STATE_CHANGE)}, 2701 [IDENTIFY][MEMORY_DEVICE] = { "IDENTIFY_MEMORY_DEVICE", 2702 cmd_identify_memory_device, 0, 0 }, 2703 [CCLS][GET_PARTITION_INFO] = { "CCLS_GET_PARTITION_INFO", 2704 cmd_ccls_get_partition_info, 0, 0 }, 2705 [CCLS][GET_LSA] = { "CCLS_GET_LSA", cmd_ccls_get_lsa, 8, 0 }, 2706 [CCLS][SET_LSA] = { "CCLS_SET_LSA", cmd_ccls_set_lsa, 2707 ~0, CXL_MBOX_IMMEDIATE_CONFIG_CHANGE | CXL_MBOX_IMMEDIATE_DATA_CHANGE }, 2708 [SANITIZE][OVERWRITE] = { "SANITIZE_OVERWRITE", cmd_sanitize_overwrite, 0, 2709 (CXL_MBOX_IMMEDIATE_DATA_CHANGE | 2710 CXL_MBOX_SECURITY_STATE_CHANGE | 2711 CXL_MBOX_BACKGROUND_OPERATION)}, 2712 [PERSISTENT_MEM][GET_SECURITY_STATE] = { "GET_SECURITY_STATE", 2713 cmd_get_security_state, 0, 0 }, 2714 [MEDIA_AND_POISON][GET_POISON_LIST] = { "MEDIA_AND_POISON_GET_POISON_LIST", 2715 cmd_media_get_poison_list, 16, 0 }, 2716 [MEDIA_AND_POISON][INJECT_POISON] = { "MEDIA_AND_POISON_INJECT_POISON", 2717 cmd_media_inject_poison, 8, 0 }, 2718 [MEDIA_AND_POISON][CLEAR_POISON] = { "MEDIA_AND_POISON_CLEAR_POISON", 2719 cmd_media_clear_poison, 72, 0 }, 2720 [MEDIA_AND_POISON][GET_SCAN_MEDIA_CAPABILITIES] = { 2721 "MEDIA_AND_POISON_GET_SCAN_MEDIA_CAPABILITIES", 2722 cmd_media_get_scan_media_capabilities, 16, 0 }, 2723 [MEDIA_AND_POISON][SCAN_MEDIA] = { "MEDIA_AND_POISON_SCAN_MEDIA", 2724 cmd_media_scan_media, 17, CXL_MBOX_BACKGROUND_OPERATION }, 2725 [MEDIA_AND_POISON][GET_SCAN_MEDIA_RESULTS] = { 2726 "MEDIA_AND_POISON_GET_SCAN_MEDIA_RESULTS", 2727 cmd_media_get_scan_media_results, 0, 0 }, 2728 }; 2729 2730 static const struct cxl_cmd cxl_cmd_set_dcd[256][256] = { 2731 [DCD_CONFIG][GET_DC_CONFIG] = { "DCD_GET_DC_CONFIG", 2732 cmd_dcd_get_dyn_cap_config, 2, 0 }, 2733 [DCD_CONFIG][GET_DYN_CAP_EXT_LIST] = { 2734 "DCD_GET_DYNAMIC_CAPACITY_EXTENT_LIST", cmd_dcd_get_dyn_cap_ext_list, 2735 8, 0 }, 2736 [DCD_CONFIG][ADD_DYN_CAP_RSP] = { 2737 "DCD_ADD_DYNAMIC_CAPACITY_RESPONSE", cmd_dcd_add_dyn_cap_rsp, 2738 ~0, CXL_MBOX_IMMEDIATE_DATA_CHANGE }, 2739 [DCD_CONFIG][RELEASE_DYN_CAP] = { 2740 "DCD_RELEASE_DYNAMIC_CAPACITY", cmd_dcd_release_dyn_cap, 2741 ~0, CXL_MBOX_IMMEDIATE_DATA_CHANGE }, 2742 }; 2743 2744 static const struct cxl_cmd cxl_cmd_set_sw[256][256] = { 2745 [INFOSTAT][IS_IDENTIFY] = { "IDENTIFY", cmd_infostat_identify, 0, 0 }, 2746 [INFOSTAT][BACKGROUND_OPERATION_STATUS] = { "BACKGROUND_OPERATION_STATUS", 2747 cmd_infostat_bg_op_sts, 0, 0 }, 2748 [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 }, 2749 [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set, 8, 2750 CXL_MBOX_IMMEDIATE_POLICY_CHANGE }, 2751 [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0, 2752 0 }, 2753 [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, 2754 [PHYSICAL_SWITCH][IDENTIFY_SWITCH_DEVICE] = { "IDENTIFY_SWITCH_DEVICE", 2755 cmd_identify_switch_device, 0, 0 }, 2756 [PHYSICAL_SWITCH][GET_PHYSICAL_PORT_STATE] = { "SWITCH_PHYSICAL_PORT_STATS", 2757 cmd_get_physical_port_state, ~0, 0 }, 2758 [TUNNEL][MANAGEMENT_COMMAND] = { "TUNNEL_MANAGEMENT_COMMAND", 2759 cmd_tunnel_management_cmd, ~0, 0 }, 2760 }; 2761 2762 /* 2763 * While the command is executing in the background, the device should 2764 * update the percentage complete in the Background Command Status Register 2765 * at least once per second. 2766 */ 2767 2768 #define CXL_MBOX_BG_UPDATE_FREQ 1000UL 2769 2770 int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, 2771 size_t len_in, uint8_t *pl_in, size_t *len_out, 2772 uint8_t *pl_out, bool *bg_started) 2773 { 2774 int ret; 2775 const struct cxl_cmd *cxl_cmd; 2776 opcode_handler h; 2777 CXLDeviceState *cxl_dstate; 2778 2779 *len_out = 0; 2780 cxl_cmd = &cci->cxl_cmd_set[set][cmd]; 2781 h = cxl_cmd->handler; 2782 if (!h) { 2783 qemu_log_mask(LOG_UNIMP, "Command %04xh not implemented\n", 2784 set << 8 | cmd); 2785 return CXL_MBOX_UNSUPPORTED; 2786 } 2787 2788 if (len_in != cxl_cmd->in && cxl_cmd->in != ~0) { 2789 return CXL_MBOX_INVALID_PAYLOAD_LENGTH; 2790 } 2791 2792 /* Only one bg command at a time */ 2793 if ((cxl_cmd->effect & CXL_MBOX_BACKGROUND_OPERATION) && 2794 cci->bg.runtime > 0) { 2795 return CXL_MBOX_BUSY; 2796 } 2797 2798 /* forbid any selected commands while the media is disabled */ 2799 if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) { 2800 cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; 2801 2802 if (cxl_dev_media_disabled(cxl_dstate)) { 2803 if (h == cmd_events_get_records || 2804 h == cmd_ccls_get_partition_info || 2805 h == cmd_ccls_set_lsa || 2806 h == cmd_ccls_get_lsa || 2807 h == cmd_logs_get_log || 2808 h == cmd_media_get_poison_list || 2809 h == cmd_media_inject_poison || 2810 h == cmd_media_clear_poison || 2811 h == cmd_sanitize_overwrite || 2812 h == cmd_firmware_update_transfer || 2813 h == cmd_firmware_update_activate) { 2814 return CXL_MBOX_MEDIA_DISABLED; 2815 } 2816 } 2817 } 2818 2819 ret = (*h)(cxl_cmd, pl_in, len_in, pl_out, len_out, cci); 2820 if ((cxl_cmd->effect & CXL_MBOX_BACKGROUND_OPERATION) && 2821 ret == CXL_MBOX_BG_STARTED) { 2822 *bg_started = true; 2823 } else { 2824 *bg_started = false; 2825 } 2826 2827 /* Set bg and the return code */ 2828 if (*bg_started) { 2829 uint64_t now; 2830 2831 cci->bg.opcode = (set << 8) | cmd; 2832 2833 cci->bg.complete_pct = 0; 2834 cci->bg.ret_code = 0; 2835 2836 now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); 2837 cci->bg.starttime = now; 2838 timer_mod(cci->bg.timer, now + CXL_MBOX_BG_UPDATE_FREQ); 2839 } 2840 2841 return ret; 2842 } 2843 2844 static void bg_timercb(void *opaque) 2845 { 2846 CXLCCI *cci = opaque; 2847 uint64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); 2848 uint64_t total_time = cci->bg.starttime + cci->bg.runtime; 2849 2850 assert(cci->bg.runtime > 0); 2851 2852 if (now >= total_time) { /* we are done */ 2853 uint16_t ret = CXL_MBOX_SUCCESS; 2854 2855 cci->bg.complete_pct = 100; 2856 cci->bg.ret_code = ret; 2857 switch (cci->bg.opcode) { 2858 case 0x0201: /* fw transfer */ 2859 __do_firmware_xfer(cci); 2860 break; 2861 case 0x4400: /* sanitize */ 2862 { 2863 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 2864 2865 __do_sanitization(ct3d); 2866 cxl_dev_enable_media(&ct3d->cxl_dstate); 2867 } 2868 break; 2869 case 0x4304: /* scan media */ 2870 { 2871 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 2872 2873 __do_scan_media(ct3d); 2874 break; 2875 } 2876 default: 2877 __builtin_unreachable(); 2878 break; 2879 } 2880 } else { 2881 /* estimate only */ 2882 cci->bg.complete_pct = 100 * now / total_time; 2883 timer_mod(cci->bg.timer, now + CXL_MBOX_BG_UPDATE_FREQ); 2884 } 2885 2886 if (cci->bg.complete_pct == 100) { 2887 /* TODO: generalize to switch CCI */ 2888 CXLType3Dev *ct3d = CXL_TYPE3(cci->d); 2889 CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; 2890 PCIDevice *pdev = PCI_DEVICE(cci->d); 2891 2892 cci->bg.starttime = 0; 2893 /* registers are updated, allow new bg-capable cmds */ 2894 cci->bg.runtime = 0; 2895 2896 if (msix_enabled(pdev)) { 2897 msix_notify(pdev, cxl_dstate->mbox_msi_n); 2898 } else if (msi_enabled(pdev)) { 2899 msi_notify(pdev, cxl_dstate->mbox_msi_n); 2900 } 2901 } 2902 } 2903 2904 static void cxl_rebuild_cel(CXLCCI *cci) 2905 { 2906 cci->cel_size = 0; /* Reset for a fresh build */ 2907 for (int set = 0; set < 256; set++) { 2908 for (int cmd = 0; cmd < 256; cmd++) { 2909 if (cci->cxl_cmd_set[set][cmd].handler) { 2910 const struct cxl_cmd *c = &cci->cxl_cmd_set[set][cmd]; 2911 struct cel_log *log = 2912 &cci->cel_log[cci->cel_size]; 2913 2914 log->opcode = (set << 8) | cmd; 2915 log->effect = c->effect; 2916 cci->cel_size++; 2917 } 2918 } 2919 } 2920 } 2921 2922 void cxl_init_cci(CXLCCI *cci, size_t payload_max) 2923 { 2924 cci->payload_max = payload_max; 2925 cxl_rebuild_cel(cci); 2926 2927 cci->bg.complete_pct = 0; 2928 cci->bg.starttime = 0; 2929 cci->bg.runtime = 0; 2930 cci->bg.timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, 2931 bg_timercb, cci); 2932 2933 memset(&cci->fw, 0, sizeof(cci->fw)); 2934 cci->fw.active_slot = 1; 2935 cci->fw.slot[cci->fw.active_slot - 1] = true; 2936 } 2937 2938 static void cxl_copy_cci_commands(CXLCCI *cci, const struct cxl_cmd (*cxl_cmds)[256]) 2939 { 2940 for (int set = 0; set < 256; set++) { 2941 for (int cmd = 0; cmd < 256; cmd++) { 2942 if (cxl_cmds[set][cmd].handler) { 2943 cci->cxl_cmd_set[set][cmd] = cxl_cmds[set][cmd]; 2944 } 2945 } 2946 } 2947 } 2948 2949 void cxl_add_cci_commands(CXLCCI *cci, const struct cxl_cmd (*cxl_cmd_set)[256], 2950 size_t payload_max) 2951 { 2952 cci->payload_max = MAX(payload_max, cci->payload_max); 2953 cxl_copy_cci_commands(cci, cxl_cmd_set); 2954 cxl_rebuild_cel(cci); 2955 } 2956 2957 void cxl_initialize_mailbox_swcci(CXLCCI *cci, DeviceState *intf, 2958 DeviceState *d, size_t payload_max) 2959 { 2960 cxl_copy_cci_commands(cci, cxl_cmd_set_sw); 2961 cci->d = d; 2962 cci->intf = intf; 2963 cxl_init_cci(cci, payload_max); 2964 } 2965 2966 void cxl_initialize_mailbox_t3(CXLCCI *cci, DeviceState *d, size_t payload_max) 2967 { 2968 CXLType3Dev *ct3d = CXL_TYPE3(d); 2969 2970 cxl_copy_cci_commands(cci, cxl_cmd_set); 2971 if (ct3d->dc.num_regions) { 2972 cxl_copy_cci_commands(cci, cxl_cmd_set_dcd); 2973 } 2974 cci->d = d; 2975 2976 /* No separation for PCI MB as protocol handled in PCI device */ 2977 cci->intf = d; 2978 cxl_init_cci(cci, payload_max); 2979 } 2980 2981 static const struct cxl_cmd cxl_cmd_set_t3_ld[256][256] = { 2982 [INFOSTAT][IS_IDENTIFY] = { "IDENTIFY", cmd_infostat_identify, 0, 0 }, 2983 [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0, 2984 0 }, 2985 [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, 2986 }; 2987 2988 void cxl_initialize_t3_ld_cci(CXLCCI *cci, DeviceState *d, DeviceState *intf, 2989 size_t payload_max) 2990 { 2991 cxl_copy_cci_commands(cci, cxl_cmd_set_t3_ld); 2992 cci->d = d; 2993 cci->intf = intf; 2994 cxl_init_cci(cci, payload_max); 2995 } 2996 2997 static const struct cxl_cmd cxl_cmd_set_t3_fm_owned_ld_mctp[256][256] = { 2998 [INFOSTAT][IS_IDENTIFY] = { "IDENTIFY", cmd_infostat_identify, 0, 0}, 2999 [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0, 3000 0 }, 3001 [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, 3002 [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 }, 3003 [TUNNEL][MANAGEMENT_COMMAND] = { "TUNNEL_MANAGEMENT_COMMAND", 3004 cmd_tunnel_management_cmd, ~0, 0 }, 3005 }; 3006 3007 void cxl_initialize_t3_fm_owned_ld_mctpcci(CXLCCI *cci, DeviceState *d, 3008 DeviceState *intf, 3009 size_t payload_max) 3010 { 3011 cxl_copy_cci_commands(cci, cxl_cmd_set_t3_fm_owned_ld_mctp); 3012 cci->d = d; 3013 cci->intf = intf; 3014 cxl_init_cci(cci, payload_max); 3015 } 3016