master
c 4,887 lines 173 KB
Raw
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 <math.h>
12
13 #include "hw/pci/msi.h"
14 #include "hw/pci/msix.h"
15 #include "hw/cxl/cxl.h"
16 #include "hw/cxl/cxl_events.h"
17 #include "hw/cxl/cxl_mailbox.h"
18 #include "hw/cxl/cxl_port.h"
19 #include "hw/pci/pci.h"
20 #include "hw/pci-bridge/cxl_downstream_port.h"
21 #include "hw/pci-bridge/cxl_upstream_port.h"
22 #include "qemu/cutils.h"
23 #include "qemu/host-utils.h"
24 #include "qemu/log.h"
25 #include "qemu/units.h"
26 #include "qemu/uuid.h"
27 #include "system/hostmem.h"
28 #include "qemu/range.h"
29 #include "qapi/qapi-types-cxl.h"
30
31 #define CXL_CAPACITY_MULTIPLIER (256 * MiB)
32 #define CXL_DC_EVENT_LOG_SIZE 8
33 #define CXL_NUM_TAGS_SUPPORTED 0
34 #define CXL_ALERTS_LIFE_USED_WARN_THRESH (1 << 0)
35 #define CXL_ALERTS_OVER_TEMP_WARN_THRESH (1 << 1)
36 #define CXL_ALERTS_UNDER_TEMP_WARN_THRESH (1 << 2)
37 #define CXL_ALERTS_COR_VMEM_ERR_WARN_THRESH (1 << 3)
38 #define CXL_ALERTS_COR_PMEM_ERR_WARN_THRESH (1 << 4)
39
40 /*
41 * How to add a new command, example. The command set FOO, with cmd BAR.
42 * 1. Add the command set and cmd to the enum.
43 * FOO = 0x7f,
44 * #define BAR 0
45 * 2. Implement the handler
46 * static CXLRetCode cmd_foo_bar(struct cxl_cmd *cmd,
47 * CXLDeviceState *cxl_dstate, uint16_t *len)
48 * 3. Add the command to the cxl_cmd_set[][]
49 * [FOO][BAR] = { "FOO_BAR", cmd_foo_bar, x, y },
50 * 4. Implement your handler
51 * define_mailbox_handler(FOO_BAR) { ... return CXL_MBOX_SUCCESS; }
52 *
53 *
54 * Writing the handler:
55 * The handler will provide the &struct cxl_cmd, the &CXLDeviceState, and the
56 * in/out length of the payload. The handler is responsible for consuming the
57 * payload from cmd->payload and operating upon it as necessary. It must then
58 * fill the output data into cmd->payload (overwriting what was there),
59 * setting the length, and returning a valid return code.
60 *
61 * XXX: The handler need not worry about endianness. The payload is read out of
62 * a register interface that already deals with it.
63 */
64
65 enum {
66 INFOSTAT = 0x00,
67 #define IS_IDENTIFY 0x1
68 #define BACKGROUND_OPERATION_STATUS 0x2
69 #define GET_RESPONSE_MSG_LIMIT 0x3
70 #define SET_RESPONSE_MSG_LIMIT 0x4
71 #define BACKGROUND_OPERATION_ABORT 0x5
72 EVENTS = 0x01,
73 #define GET_RECORDS 0x0
74 #define CLEAR_RECORDS 0x1
75 #define GET_INTERRUPT_POLICY 0x2
76 #define SET_INTERRUPT_POLICY 0x3
77 FIRMWARE_UPDATE = 0x02,
78 #define GET_INFO 0x0
79 #define TRANSFER 0x1
80 #define ACTIVATE 0x2
81 TIMESTAMP = 0x03,
82 #define GET 0x0
83 #define SET 0x1
84 LOGS = 0x04,
85 #define GET_SUPPORTED 0x0
86 #define GET_LOG 0x1
87 FEATURES = 0x05,
88 #define GET_SUPPORTED 0x0
89 #define GET_FEATURE 0x1
90 #define SET_FEATURE 0x2
91 MAINTENANCE = 0x06,
92 #define PERFORM 0x0
93 IDENTIFY = 0x40,
94 #define MEMORY_DEVICE 0x0
95 CCLS = 0x41,
96 #define GET_PARTITION_INFO 0x0
97 #define GET_LSA 0x2
98 #define SET_LSA 0x3
99 HEALTH_INFO_ALERTS = 0x42,
100 #define GET_ALERT_CONFIG 0x1
101 #define SET_ALERT_CONFIG 0x2
102 SANITIZE = 0x44,
103 #define OVERWRITE 0x0
104 #define SECURE_ERASE 0x1
105 #define MEDIA_OPERATIONS 0x2
106 PERSISTENT_MEM = 0x45,
107 #define GET_SECURITY_STATE 0x0
108 MEDIA_AND_POISON = 0x43,
109 #define GET_POISON_LIST 0x0
110 #define INJECT_POISON 0x1
111 #define CLEAR_POISON 0x2
112 #define GET_SCAN_MEDIA_CAPABILITIES 0x3
113 #define SCAN_MEDIA 0x4
114 #define GET_SCAN_MEDIA_RESULTS 0x5
115 DCD_CONFIG = 0x48,
116 #define GET_DC_CONFIG 0x0
117 #define GET_DYN_CAP_EXT_LIST 0x1
118 #define ADD_DYN_CAP_RSP 0x2
119 #define RELEASE_DYN_CAP 0x3
120 PHYSICAL_SWITCH = 0x51,
121 #define IDENTIFY_SWITCH_DEVICE 0x0
122 #define GET_PHYSICAL_PORT_STATE 0x1
123 #define PHYSICAL_PORT_CONTROL 0x2
124 TUNNEL = 0x53,
125 #define MANAGEMENT_COMMAND 0x0
126 FMAPI_DCD_MGMT = 0x56,
127 #define GET_DCD_INFO 0x0
128 #define GET_HOST_DC_REGION_CONFIG 0x1
129 #define SET_DC_REGION_CONFIG 0x2
130 #define GET_DC_REGION_EXTENT_LIST 0x3
131 #define INITIATE_DC_ADD 0x4
132 #define INITIATE_DC_RELEASE 0x5
133 };
134
135 /* CCI Message Format CXL r3.1 Figure 7-19 */
136 typedef struct CXLCCIMessage {
137 uint8_t category;
138 #define CXL_CCI_CAT_REQ 0
139 #define CXL_CCI_CAT_RSP 1
140 uint8_t tag;
141 uint8_t resv1;
142 uint8_t command;
143 uint8_t command_set;
144 uint8_t pl_length[3];
145 uint16_t rc;
146 uint16_t vendor_specific;
147 uint8_t payload[];
148 } QEMU_PACKED CXLCCIMessage;
149
150 /* This command is only defined to an MLD FM Owned LD or an MHD */
151 static CXLRetCode cmd_tunnel_management_cmd(const struct cxl_cmd *cmd,
152 uint8_t *payload_in,
153 size_t len_in,
154 uint8_t *payload_out,
155 size_t *len_out,
156 CXLCCI *cci)
157 {
158 PCIDevice *tunnel_target;
159 CXLCCI *target_cci;
160 struct {
161 uint8_t port_or_ld_id;
162 uint8_t target_type;
163 uint16_t size;
164 CXLCCIMessage ccimessage;
165 } QEMU_PACKED *in;
166 struct {
167 uint16_t resp_len;
168 uint8_t resv[2];
169 CXLCCIMessage ccimessage;
170 } QEMU_PACKED *out;
171 size_t pl_length, length_out;
172 bool bg_started;
173 int rc;
174
175 if (cmd->in < sizeof(*in)) {
176 return CXL_MBOX_INVALID_INPUT;
177 }
178 in = (void *)payload_in;
179 out = (void *)payload_out;
180
181 if (len_in < sizeof(*in)) {
182 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
183 }
184 /* Enough room for minimum sized message - no payload */
185 if (in->size < sizeof(in->ccimessage)) {
186 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
187 }
188 /* Length of input payload should be in->size + a wrapping tunnel header */
189 if (in->size != len_in - offsetof(typeof(*out), ccimessage)) {
190 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
191 }
192 if (in->ccimessage.category != CXL_CCI_CAT_REQ) {
193 return CXL_MBOX_INVALID_INPUT;
194 }
195
196 if (in->target_type != 0) {
197 qemu_log_mask(LOG_UNIMP,
198 "Tunneled Command sent to non existent FM-LD");
199 return CXL_MBOX_INVALID_INPUT;
200 }
201
202 /*
203 * Target of a tunnel unfortunately depends on type of CCI readint
204 * the message.
205 * If in a switch, then it's the port number.
206 * If in an MLD it is the ld number.
207 * If in an MHD target type indicate where we are going.
208 */
209 if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) {
210 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
211 if (in->port_or_ld_id != 0) {
212 /* Only pretending to have one for now! */
213 return CXL_MBOX_INVALID_INPUT;
214 }
215 target_cci = &ct3d->ld0_cci;
216 } else if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_USP)) {
217 CXLUpstreamPort *usp = CXL_USP(cci->d);
218
219 tunnel_target = pcie_find_port_by_pn(&PCI_BRIDGE(usp)->sec_bus,
220 in->port_or_ld_id);
221 if (!tunnel_target) {
222 return CXL_MBOX_INVALID_INPUT;
223 }
224 tunnel_target =
225 pci_bridge_get_sec_bus(PCI_BRIDGE(tunnel_target))->devices[0];
226 if (!tunnel_target) {
227 return CXL_MBOX_INVALID_INPUT;
228 }
229 if (object_dynamic_cast(OBJECT(tunnel_target), TYPE_CXL_TYPE3)) {
230 CXLType3Dev *ct3d = CXL_TYPE3(tunnel_target);
231 /* Tunneled VDMs always land on FM Owned LD */
232 target_cci = &ct3d->vdm_fm_owned_ld_mctp_cci;
233 } else {
234 return CXL_MBOX_INVALID_INPUT;
235 }
236 } else {
237 return CXL_MBOX_INVALID_INPUT;
238 }
239
240 pl_length = in->ccimessage.pl_length[2] << 16 |
241 in->ccimessage.pl_length[1] << 8 | in->ccimessage.pl_length[0];
242 rc = cxl_process_cci_message(target_cci,
243 in->ccimessage.command_set,
244 in->ccimessage.command,
245 pl_length, in->ccimessage.payload,
246 &length_out, out->ccimessage.payload,
247 &bg_started);
248 /* Payload should be in place. Rest of CCI header and needs filling */
249 out->resp_len = length_out + sizeof(CXLCCIMessage);
250 st24_le_p(out->ccimessage.pl_length, length_out);
251 out->ccimessage.rc = rc;
252 out->ccimessage.category = CXL_CCI_CAT_RSP;
253 out->ccimessage.command = in->ccimessage.command;
254 out->ccimessage.command_set = in->ccimessage.command_set;
255 out->ccimessage.tag = in->ccimessage.tag;
256 *len_out = length_out + sizeof(*out);
257
258 return CXL_MBOX_SUCCESS;
259 }
260
261 static CXLRetCode cmd_events_get_records(const struct cxl_cmd *cmd,
262 uint8_t *payload_in, size_t len_in,
263 uint8_t *payload_out, size_t *len_out,
264 CXLCCI *cci)
265 {
266 CXLDeviceState *cxlds = &CXL_TYPE3(cci->d)->cxl_dstate;
267 CXLGetEventPayload *pl;
268 uint8_t log_type;
269 int max_recs;
270
271 if (cmd->in < sizeof(log_type)) {
272 return CXL_MBOX_INVALID_INPUT;
273 }
274
275 log_type = payload_in[0];
276
277 pl = (CXLGetEventPayload *)payload_out;
278
279 max_recs = (cxlds->payload_size - CXL_EVENT_PAYLOAD_HDR_SIZE) /
280 CXL_EVENT_RECORD_SIZE;
281 if (max_recs > 0xFFFF) {
282 max_recs = 0xFFFF;
283 }
284
285 return cxl_event_get_records(cxlds, pl, log_type, max_recs, len_out);
286 }
287
288 static CXLRetCode cmd_events_clear_records(const struct cxl_cmd *cmd,
289 uint8_t *payload_in,
290 size_t len_in,
291 uint8_t *payload_out,
292 size_t *len_out,
293 CXLCCI *cci)
294 {
295 CXLDeviceState *cxlds = &CXL_TYPE3(cci->d)->cxl_dstate;
296 CXLClearEventPayload *pl;
297
298 pl = (CXLClearEventPayload *)payload_in;
299
300 if (len_in < sizeof(*pl) ||
301 len_in < sizeof(*pl) + sizeof(*pl->handle) * pl->nr_recs) {
302 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
303 }
304
305 *len_out = 0;
306 return cxl_event_clear_records(cxlds, pl);
307 }
308
309 static CXLRetCode cmd_events_get_interrupt_policy(const struct cxl_cmd *cmd,
310 uint8_t *payload_in,
311 size_t len_in,
312 uint8_t *payload_out,
313 size_t *len_out,
314 CXLCCI *cci)
315 {
316 CXLDeviceState *cxlds = &CXL_TYPE3(cci->d)->cxl_dstate;
317 CXLEventInterruptPolicy *policy;
318 CXLEventLog *log;
319
320 policy = (CXLEventInterruptPolicy *)payload_out;
321
322 log = &cxlds->event_logs[CXL_EVENT_TYPE_INFO];
323 if (log->irq_enabled) {
324 policy->info_settings = CXL_EVENT_INT_SETTING(log->irq_vec);
325 }
326
327 log = &cxlds->event_logs[CXL_EVENT_TYPE_WARN];
328 if (log->irq_enabled) {
329 policy->warn_settings = CXL_EVENT_INT_SETTING(log->irq_vec);
330 }
331
332 log = &cxlds->event_logs[CXL_EVENT_TYPE_FAIL];
333 if (log->irq_enabled) {
334 policy->failure_settings = CXL_EVENT_INT_SETTING(log->irq_vec);
335 }
336
337 log = &cxlds->event_logs[CXL_EVENT_TYPE_FATAL];
338 if (log->irq_enabled) {
339 policy->fatal_settings = CXL_EVENT_INT_SETTING(log->irq_vec);
340 }
341
342 log = &cxlds->event_logs[CXL_EVENT_TYPE_DYNAMIC_CAP];
343 if (log->irq_enabled) {
344 /* Dynamic Capacity borrows the same vector as info */
345 policy->dyn_cap_settings = CXL_INT_MSI_MSIX;
346 }
347
348 *len_out = sizeof(*policy);
349 return CXL_MBOX_SUCCESS;
350 }
351
352 static CXLRetCode cmd_events_set_interrupt_policy(const struct cxl_cmd *cmd,
353 uint8_t *payload_in,
354 size_t len_in,
355 uint8_t *payload_out,
356 size_t *len_out,
357 CXLCCI *cci)
358 {
359 CXLDeviceState *cxlds = &CXL_TYPE3(cci->d)->cxl_dstate;
360 CXLEventInterruptPolicy *policy;
361 CXLEventLog *log;
362
363 if (len_in < CXL_EVENT_INT_SETTING_MIN_LEN) {
364 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
365 }
366
367 policy = (CXLEventInterruptPolicy *)payload_in;
368
369 log = &cxlds->event_logs[CXL_EVENT_TYPE_INFO];
370 log->irq_enabled = (policy->info_settings & CXL_EVENT_INT_MODE_MASK) ==
371 CXL_INT_MSI_MSIX;
372
373 log = &cxlds->event_logs[CXL_EVENT_TYPE_WARN];
374 log->irq_enabled = (policy->warn_settings & CXL_EVENT_INT_MODE_MASK) ==
375 CXL_INT_MSI_MSIX;
376
377 log = &cxlds->event_logs[CXL_EVENT_TYPE_FAIL];
378 log->irq_enabled = (policy->failure_settings & CXL_EVENT_INT_MODE_MASK) ==
379 CXL_INT_MSI_MSIX;
380
381 log = &cxlds->event_logs[CXL_EVENT_TYPE_FATAL];
382 log->irq_enabled = (policy->fatal_settings & CXL_EVENT_INT_MODE_MASK) ==
383 CXL_INT_MSI_MSIX;
384
385 /* DCD is optional */
386 if (len_in < sizeof(*policy)) {
387 return CXL_MBOX_SUCCESS;
388 }
389
390 log = &cxlds->event_logs[CXL_EVENT_TYPE_DYNAMIC_CAP];
391 log->irq_enabled = (policy->dyn_cap_settings & CXL_EVENT_INT_MODE_MASK) ==
392 CXL_INT_MSI_MSIX;
393
394 *len_out = 0;
395 return CXL_MBOX_SUCCESS;
396 }
397
398 /* CXL r3.1 section 8.2.9.1.1: Identify (Opcode 0001h) */
399 static CXLRetCode cmd_infostat_identify(const struct cxl_cmd *cmd,
400 uint8_t *payload_in,
401 size_t len_in,
402 uint8_t *payload_out,
403 size_t *len_out,
404 CXLCCI *cci)
405 {
406 PCIDeviceClass *class = PCI_DEVICE_GET_CLASS(cci->d);
407 struct {
408 uint16_t pcie_vid;
409 uint16_t pcie_did;
410 uint16_t pcie_subsys_vid;
411 uint16_t pcie_subsys_id;
412 uint64_t sn;
413 uint8_t max_message_size;
414 uint8_t component_type;
415 } QEMU_PACKED *is_identify;
416 QEMU_BUILD_BUG_ON(sizeof(*is_identify) != 18);
417
418 is_identify = (void *)payload_out;
419 is_identify->pcie_vid = class->vendor_id;
420 is_identify->pcie_did = class->device_id;
421 if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_USP)) {
422 is_identify->sn = CXL_USP(cci->d)->sn;
423 /* Subsystem info not defined for a USP */
424 is_identify->pcie_subsys_vid = 0;
425 is_identify->pcie_subsys_id = 0;
426 is_identify->component_type = 0x0; /* Switch */
427 } else if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) {
428 PCIDevice *pci_dev = PCI_DEVICE(cci->d);
429
430 is_identify->sn = CXL_TYPE3(cci->d)->sn;
431 /*
432 * We can't always use class->subsystem_vendor_id as
433 * it is not set if the defaults are used.
434 */
435 is_identify->pcie_subsys_vid =
436 pci_get_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID);
437 is_identify->pcie_subsys_id =
438 pci_get_word(pci_dev->config + PCI_SUBSYSTEM_ID);
439 is_identify->component_type = 0x3; /* Type 3 */
440 }
441
442 is_identify->max_message_size = (uint8_t)log2(cci->payload_max);
443 *len_out = sizeof(*is_identify);
444 return CXL_MBOX_SUCCESS;
445 }
446
447 /* CXL r3.1 section 8.2.9.1.3: Get Response Message Limit (Opcode 0003h) */
448 static CXLRetCode cmd_get_response_msg_limit(const struct cxl_cmd *cmd,
449 uint8_t *payload_in,
450 size_t len_in,
451 uint8_t *payload_out,
452 size_t *len_out,
453 CXLCCI *cci)
454 {
455 struct {
456 uint8_t rsp_limit;
457 } QEMU_PACKED *get_rsp_msg_limit = (void *)payload_out;
458 QEMU_BUILD_BUG_ON(sizeof(*get_rsp_msg_limit) != 1);
459
460 get_rsp_msg_limit->rsp_limit = (uint8_t)log2(cci->payload_max);
461
462 *len_out = sizeof(*get_rsp_msg_limit);
463 return CXL_MBOX_SUCCESS;
464 }
465
466 /* CXL r3.1 section 8.2.9.1.4: Set Response Message Limit (Opcode 0004h) */
467 static CXLRetCode cmd_set_response_msg_limit(const struct cxl_cmd *cmd,
468 uint8_t *payload_in,
469 size_t len_in,
470 uint8_t *payload_out,
471 size_t *len_out,
472 CXLCCI *cci)
473 {
474 struct {
475 uint8_t rsp_limit;
476 } QEMU_PACKED *in = (void *)payload_in;
477 QEMU_BUILD_BUG_ON(sizeof(*in) != 1);
478 struct {
479 uint8_t rsp_limit;
480 } QEMU_PACKED *out = (void *)payload_out;
481 QEMU_BUILD_BUG_ON(sizeof(*out) != 1);
482
483 if (in->rsp_limit < 8 || in->rsp_limit > 10) {
484 return CXL_MBOX_INVALID_INPUT;
485 }
486
487 cci->payload_max = 1 << in->rsp_limit;
488 out->rsp_limit = in->rsp_limit;
489
490 *len_out = sizeof(*out);
491 return CXL_MBOX_SUCCESS;
492 }
493
494 static void cxl_set_dsp_active_bm(PCIBus *b, PCIDevice *d,
495 void *private)
496 {
497 uint8_t *bm = private;
498 if (object_dynamic_cast(OBJECT(d), TYPE_CXL_DSP)) {
499 uint8_t port = PCIE_PORT(d)->port;
500 bm[port / 8] |= 1 << (port % 8);
501 }
502 }
503
504 /* CXL r3.1 Section 7.6.7.1.1: Identify Switch Device (Opcode 5100h) */
505 static CXLRetCode cmd_identify_switch_device(const struct cxl_cmd *cmd,
506 uint8_t *payload_in,
507 size_t len_in,
508 uint8_t *payload_out,
509 size_t *len_out,
510 CXLCCI *cci)
511 {
512 PCIEPort *usp = PCIE_PORT(cci->d);
513 PCIBus *bus = &PCI_BRIDGE(cci->d)->sec_bus;
514 int num_phys_ports = pcie_count_ds_ports(bus);
515
516 struct cxl_fmapi_ident_switch_dev_resp_pl {
517 uint8_t ingress_port_id;
518 uint8_t rsvd;
519 uint8_t num_physical_ports;
520 uint8_t num_vcss;
521 uint8_t active_port_bitmask[0x20];
522 uint8_t active_vcs_bitmask[0x20];
523 uint16_t total_vppbs;
524 uint16_t bound_vppbs;
525 uint8_t num_hdm_decoders_per_usp;
526 } QEMU_PACKED *out;
527 QEMU_BUILD_BUG_ON(sizeof(*out) != 0x49);
528
529 out = (struct cxl_fmapi_ident_switch_dev_resp_pl *)payload_out;
530 *out = (struct cxl_fmapi_ident_switch_dev_resp_pl) {
531 .num_physical_ports = num_phys_ports + 1, /* 1 USP */
532 .num_vcss = 1, /* Not yet support multiple VCS - potentially tricky */
533 .active_vcs_bitmask[0] = 0x1,
534 .total_vppbs = num_phys_ports + 1,
535 .bound_vppbs = num_phys_ports + 1,
536 .num_hdm_decoders_per_usp = 4,
537 };
538
539 /* Depends on the CCI type */
540 if (object_dynamic_cast(OBJECT(cci->intf), TYPE_PCIE_PORT)) {
541 out->ingress_port_id = PCIE_PORT(cci->intf)->port;
542 } else {
543 /* MCTP? */
544 out->ingress_port_id = 0;
545 }
546
547 pci_for_each_device_under_bus(bus, cxl_set_dsp_active_bm,
548 out->active_port_bitmask);
549 out->active_port_bitmask[usp->port / 8] |= (1 << usp->port % 8);
550
551 *len_out = sizeof(*out);
552
553 return CXL_MBOX_SUCCESS;
554 }
555
556 /* CXL r3.1 Section 7.6.7.1.2: Get Physical Port State (Opcode 5101h) */
557 static CXLRetCode cmd_get_physical_port_state(const struct cxl_cmd *cmd,
558 uint8_t *payload_in,
559 size_t len_in,
560 uint8_t *payload_out,
561 size_t *len_out,
562 CXLCCI *cci)
563 {
564 /* CXL r3.1 Table 7-17: Get Physical Port State Request Payload */
565 struct cxl_fmapi_get_phys_port_state_req_pl {
566 uint8_t num_ports;
567 uint8_t ports[];
568 } QEMU_PACKED *in;
569
570 /*
571 * CXL r3.2 Table 7-19: Get Physical Port State Port Information Block
572 * Format
573 */
574 struct cxl_fmapi_port_state_info_block {
575 uint8_t port_id;
576 uint8_t config_state;
577 uint8_t connected_device_mode;
578 uint8_t rsv1;
579 uint8_t connected_device_type;
580 uint8_t supported_cxl_mode_bitmask;
581 uint8_t max_link_width;
582 uint8_t negotiated_link_width;
583 uint8_t supported_link_speeds_vector;
584 uint8_t max_link_speed;
585 uint8_t current_link_speed;
586 uint8_t ltssm_state;
587 uint8_t first_lane_num;
588 uint16_t link_state;
589 uint8_t supported_ld_count;
590 } QEMU_PACKED;
591
592 /* CXL r3.1 Table 7-18: Get Physical Port State Response Payload */
593 struct cxl_fmapi_get_phys_port_state_resp_pl {
594 uint8_t num_ports;
595 uint8_t rsv1[3];
596 struct cxl_fmapi_port_state_info_block ports[];
597 } QEMU_PACKED *out;
598 PCIBus *bus = &PCI_BRIDGE(cci->d)->sec_bus;
599 PCIEPort *usp = PCIE_PORT(cci->d);
600 size_t pl_size;
601 int i;
602
603 in = (struct cxl_fmapi_get_phys_port_state_req_pl *)payload_in;
604 out = (struct cxl_fmapi_get_phys_port_state_resp_pl *)payload_out;
605
606 if (len_in < sizeof(*in)) {
607 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
608 }
609 /* Check if what was requested can fit */
610 if (sizeof(*out) + sizeof(*out->ports) * in->num_ports > cci->payload_max) {
611 return CXL_MBOX_INVALID_INPUT;
612 }
613
614 /* For success there should be a match for each requested */
615 out->num_ports = in->num_ports;
616
617 for (i = 0; i < in->num_ports; i++) {
618 struct cxl_fmapi_port_state_info_block *port;
619 /* First try to match on downstream port */
620 PCIDevice *port_dev;
621 CXLPhyPortPerst *perst;
622 uint16_t lnkcap, lnkcap2, lnksta;
623
624 port = &out->ports[i];
625
626 port_dev = pcie_find_port_by_pn(bus, in->ports[i]);
627 if (port_dev) { /* DSP */
628 PCIDevice *ds_dev = pci_bridge_get_sec_bus(PCI_BRIDGE(port_dev))
629 ->devices[0];
630 port->config_state = CXL_PORT_CONFIG_STATE_DSP;
631 if (ds_dev) {
632 if (object_dynamic_cast(OBJECT(ds_dev), TYPE_CXL_TYPE3)) {
633 uint16_t lnksta2;
634
635 if (!port_dev->exp.exp_cap) {
636 return CXL_MBOX_INTERNAL_ERROR;
637 }
638
639 lnksta2 = port_dev->config_read(port_dev,
640 port_dev->exp.exp_cap + PCI_EXP_LNKSTA2,
641 sizeof(lnksta2));
642
643 /* Assume MLD for now */
644 port->connected_device_type =
645 CXL_PORT_CONNECTED_DEV_TYPE_3_MLD;
646 if (lnksta2 & PCI_EXP_LNKSTA2_FLIT) {
647 port->connected_device_mode =
648 CXL_PORT_CONNECTED_DEV_MODE_256B;
649 } else {
650 port->connected_device_mode =
651 CXL_PORT_CONNECTED_DEV_MODE_68B_VH;
652 }
653 } else {
654 port->connected_device_type =
655 CXL_PORT_CONNECTED_DEV_TYPE_PCIE;
656 port->connected_device_mode =
657 CXL_PORT_CONNECTED_DEV_MODE_NOT_CXL_OR_DISCONN;
658
659 }
660 } else {
661 port->connected_device_type = CXL_PORT_CONNECTED_DEV_TYPE_NONE;
662 port->connected_device_mode =
663 CXL_PORT_CONNECTED_DEV_MODE_NOT_CXL_OR_DISCONN;
664 }
665 /* DSP currently always support modes implemented in QEMU */
666 port->supported_cxl_mode_bitmask = CXL_PORT_SUPPORTS_68B_VH |
667 CXL_PORT_SUPPORTS_256B;
668 port->supported_ld_count = 3;
669 perst = cxl_dsp_get_perst(CXL_DSP(port_dev));
670 } else if (usp->port == in->ports[i]) { /* USP */
671 port_dev = PCI_DEVICE(usp);
672 port->config_state = CXL_PORT_CONFIG_STATE_USP;
673 port->connected_device_type = 0; /* Reserved for USP */
674 port->connected_device_mode = 0; /* Reserved for USP */
675 port->supported_cxl_mode_bitmask = CXL_PORT_SUPPORTS_68B_VH |
676 (CXL_USP(usp)->flitmode ? CXL_PORT_SUPPORTS_256B : 0);
677 perst = &CXL_USP(usp)->perst;
678 } else {
679 return CXL_MBOX_INVALID_INPUT;
680 }
681
682 port->port_id = in->ports[i];
683 /* Information on status of this port in lnksta, lnkcap */
684 if (!port_dev->exp.exp_cap) {
685 return CXL_MBOX_INTERNAL_ERROR;
686 }
687 lnksta = port_dev->config_read(port_dev,
688 port_dev->exp.exp_cap + PCI_EXP_LNKSTA,
689 sizeof(lnksta));
690 lnkcap = port_dev->config_read(port_dev,
691 port_dev->exp.exp_cap + PCI_EXP_LNKCAP,
692 sizeof(lnkcap));
693 lnkcap2 = port_dev->config_read(port_dev,
694 port_dev->exp.exp_cap + PCI_EXP_LNKCAP2,
695 sizeof(lnkcap2));
696
697 port->max_link_width = (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4;
698 port->negotiated_link_width = (lnksta & PCI_EXP_LNKSTA_NLW) >> 4;
699 /* No definition for SLS field in linux/pci_regs.h */
700 port->supported_link_speeds_vector = (lnkcap2 & 0xFE) >> 1;
701 port->max_link_speed = lnkcap & PCI_EXP_LNKCAP_SLS;
702 port->current_link_speed = lnksta & PCI_EXP_LNKSTA_CLS;
703 /* TODO: Track down if we can get the rest of the info */
704 port->ltssm_state = 0x7;
705 port->first_lane_num = 0;
706 port->link_state = perst ? CXL_PORT_LINK_STATE_FLAG_PERST_ASSERTED : 0;
707 }
708
709 pl_size = sizeof(*out) + sizeof(*out->ports) * in->num_ports;
710 *len_out = pl_size;
711
712 return CXL_MBOX_SUCCESS;
713 }
714
715 static void *bg_assertcb(void *opaque)
716 {
717 CXLPhyPortPerst *perst = opaque;
718
719 /* holding reset phase for 100ms */
720 while (perst->asrt_time--) {
721 usleep(1000);
722 }
723 perst->issued_assert_perst = true;
724 return NULL;
725 }
726
727 static CXLRetCode cxl_deassert_perst(Object *obj, CXLPhyPortPerst *perst)
728 {
729 if (!perst->issued_assert_perst) {
730 return CXL_MBOX_INTERNAL_ERROR;
731 }
732
733 QEMU_LOCK_GUARD(&perst->lock);
734 resettable_release_reset(obj, RESET_TYPE_COLD);
735 perst->issued_assert_perst = false;
736 perst->asrt_time = ASSERT_WAIT_TIME_MS;
737
738 return CXL_MBOX_SUCCESS;
739 }
740
741 static CXLRetCode cxl_assert_perst(Object *obj, CXLPhyPortPerst *perst)
742 {
743 if (cxl_perst_asserted(perst)) {
744 return CXL_MBOX_INTERNAL_ERROR;
745 }
746
747 QEMU_LOCK_GUARD(&perst->lock);
748 resettable_assert_reset(obj, RESET_TYPE_COLD);
749 qemu_thread_create(&perst->asrt_thread, "assert_thread", bg_assertcb,
750 perst, QEMU_THREAD_DETACHED);
751
752 return CXL_MBOX_SUCCESS;
753 }
754
755 static CXLDownstreamPort *cxl_find_dsp_on_bus(PCIBus *bus, uint8_t pn)
756 {
757
758 PCIDevice *port_dev = pcie_find_port_by_pn(bus, pn);
759
760 if (object_dynamic_cast(OBJECT(port_dev), TYPE_CXL_DSP)) {
761 return CXL_DSP(port_dev);
762 }
763
764 return NULL;
765 }
766
767 /* CXL r3.2 Section 7.6.7.1.3: Get Physical Port Control (Opcode 5102h) */
768 static CXLRetCode cmd_physical_port_control(const struct cxl_cmd *cmd,
769 uint8_t *payload_in,
770 size_t len_in,
771 uint8_t *payload_out,
772 size_t *len_out,
773 CXLCCI *cci)
774 {
775 CXLUpstreamPort *pp = CXL_USP(cci->d);
776 CXLPhyPortPerst *perst;
777 PCIDevice *dev;
778
779 struct cxl_fmapi_get_physical_port_control_req_pl {
780 uint8_t ppb_id;
781 uint8_t ports_op;
782 } QEMU_PACKED *in = (void *)payload_in;
783
784 if (len_in < sizeof(*in)) {
785 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
786 }
787
788 if (PCIE_PORT(pp)->port == in->ppb_id) {
789 dev = PCI_DEVICE(pp);
790 perst = &pp->perst;
791 } else {
792 CXLDownstreamPort *dsp =
793 cxl_find_dsp_on_bus(&PCI_BRIDGE(pp)->sec_bus, in->ppb_id);
794
795 if (!dsp) {
796 return CXL_MBOX_INVALID_INPUT;
797 }
798 dev = PCI_DEVICE(dsp);
799 perst = cxl_dsp_get_perst(dsp);
800 }
801
802 switch (in->ports_op) {
803 case 0:
804 return cxl_assert_perst(OBJECT(&dev->qdev), perst);
805 case 1:
806 return cxl_deassert_perst(OBJECT(&dev->qdev), perst);
807 case 2: {
808 if (!perst) {
809 return CXL_MBOX_INVALID_INPUT;
810 }
811
812 if (perst->issued_assert_perst ||
813 perst->asrt_time < ASSERT_WAIT_TIME_MS) {
814 return CXL_MBOX_INTERNAL_ERROR;
815 }
816 device_cold_reset(&dev->qdev);
817 return CXL_MBOX_SUCCESS;
818 }
819 default:
820 return CXL_MBOX_INVALID_INPUT;
821 }
822 }
823
824 /* CXL r3.1 Section 8.2.9.1.2: Background Operation Status (Opcode 0002h) */
825 static CXLRetCode cmd_infostat_bg_op_sts(const struct cxl_cmd *cmd,
826 uint8_t *payload_in,
827 size_t len_in,
828 uint8_t *payload_out,
829 size_t *len_out,
830 CXLCCI *cci)
831 {
832 struct {
833 uint8_t status;
834 uint8_t rsvd;
835 uint16_t opcode;
836 uint16_t returncode;
837 uint16_t vendor_ext_status;
838 } QEMU_PACKED *bg_op_status;
839 QEMU_BUILD_BUG_ON(sizeof(*bg_op_status) != 8);
840
841 bg_op_status = (void *)payload_out;
842 bg_op_status->status = cci->bg.complete_pct << 1;
843 if (cci->bg.runtime > 0) {
844 bg_op_status->status |= 1U << 0;
845 }
846 bg_op_status->opcode = cci->bg.opcode;
847 bg_op_status->returncode = cci->bg.ret_code;
848 *len_out = sizeof(*bg_op_status);
849
850 return CXL_MBOX_SUCCESS;
851 }
852
853 /*
854 * CXL r3.1 Section 8.2.9.1.5:
855 * Request Abort Background Operation (Opcode 0005h)
856 */
857 static CXLRetCode cmd_infostat_bg_op_abort(const struct cxl_cmd *cmd,
858 uint8_t *payload_in,
859 size_t len_in,
860 uint8_t *payload_out,
861 size_t *len_out,
862 CXLCCI *cci)
863 {
864 int bg_set = cci->bg.opcode >> 8;
865 int bg_cmd = cci->bg.opcode & 0xff;
866 const struct cxl_cmd *bg_c = &cci->cxl_cmd_set[bg_set][bg_cmd];
867
868 if (!(bg_c->effect & CXL_MBOX_BACKGROUND_OPERATION_ABORT)) {
869 return CXL_MBOX_REQUEST_ABORT_NOTSUP;
870 }
871
872 qemu_mutex_lock(&cci->bg.lock);
873 if (cci->bg.runtime) {
874 /* operation is near complete, let it finish */
875 if (cci->bg.complete_pct < 85) {
876 timer_del(cci->bg.timer);
877 cci->bg.ret_code = CXL_MBOX_ABORTED;
878 cci->bg.starttime = 0;
879 cci->bg.runtime = 0;
880 cci->bg.aborted = true;
881 }
882 }
883 qemu_mutex_unlock(&cci->bg.lock);
884
885 return CXL_MBOX_SUCCESS;
886 }
887
888 #define CXL_FW_SLOTS 2
889 #define CXL_FW_SIZE 0x02000000 /* 32 mb */
890
891 /* CXL r3.1 Section 8.2.9.3.1: Get FW Info (Opcode 0200h) */
892 static CXLRetCode cmd_firmware_update_get_info(const struct cxl_cmd *cmd,
893 uint8_t *payload_in,
894 size_t len,
895 uint8_t *payload_out,
896 size_t *len_out,
897 CXLCCI *cci)
898 {
899 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
900 CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate;
901 struct {
902 uint8_t slots_supported;
903 uint8_t slot_info;
904 uint8_t caps;
905 uint8_t rsvd[0xd];
906 char fw_rev1[0x10];
907 char fw_rev2[0x10];
908 char fw_rev3[0x10];
909 char fw_rev4[0x10];
910 } QEMU_PACKED *fw_info;
911 QEMU_BUILD_BUG_ON(sizeof(*fw_info) != 0x50);
912
913 if (!QEMU_IS_ALIGNED(cxl_dstate->vmem_size, CXL_CAPACITY_MULTIPLIER) ||
914 !QEMU_IS_ALIGNED(cxl_dstate->pmem_size, CXL_CAPACITY_MULTIPLIER) ||
915 !QEMU_IS_ALIGNED(ct3d->dc.total_capacity, CXL_CAPACITY_MULTIPLIER)) {
916 return CXL_MBOX_INTERNAL_ERROR;
917 }
918
919 fw_info = (void *)payload_out;
920
921 fw_info->slots_supported = CXL_FW_SLOTS;
922 fw_info->slot_info = (cci->fw.active_slot & 0x7) |
923 ((cci->fw.staged_slot & 0x7) << 3);
924 fw_info->caps = BIT(0); /* online update supported */
925
926 if (cci->fw.slot[0]) {
927 pstrcpy(fw_info->fw_rev1, sizeof(fw_info->fw_rev1), "BWFW VERSION 0");
928 }
929 if (cci->fw.slot[1]) {
930 pstrcpy(fw_info->fw_rev2, sizeof(fw_info->fw_rev2), "BWFW VERSION 1");
931 }
932
933 *len_out = sizeof(*fw_info);
934 return CXL_MBOX_SUCCESS;
935 }
936
937 /* CXL r3.1 section 8.2.9.3.2: Transfer FW (Opcode 0201h) */
938 #define CXL_FW_XFER_ALIGNMENT 128
939
940 #define CXL_FW_XFER_ACTION_FULL 0x0
941 #define CXL_FW_XFER_ACTION_INIT 0x1
942 #define CXL_FW_XFER_ACTION_CONTINUE 0x2
943 #define CXL_FW_XFER_ACTION_END 0x3
944 #define CXL_FW_XFER_ACTION_ABORT 0x4
945
946 static CXLRetCode cmd_firmware_update_transfer(const struct cxl_cmd *cmd,
947 uint8_t *payload_in,
948 size_t len,
949 uint8_t *payload_out,
950 size_t *len_out,
951 CXLCCI *cci)
952 {
953 struct {
954 uint8_t action;
955 uint8_t slot;
956 uint8_t rsvd1[2];
957 uint32_t offset;
958 uint8_t rsvd2[0x78];
959 uint8_t data[];
960 } QEMU_PACKED *fw_transfer = (void *)payload_in;
961 size_t offset, length;
962
963 if (len < sizeof(*fw_transfer)) {
964 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
965 }
966
967 if (fw_transfer->action == CXL_FW_XFER_ACTION_ABORT) {
968 /*
969 * At this point there aren't any on-going transfers
970 * running in the bg - this is serialized before this
971 * call altogether. Just mark the state machine and
972 * disregard any other input.
973 */
974 cci->fw.transferring = false;
975 return CXL_MBOX_SUCCESS;
976 }
977
978 offset = fw_transfer->offset * CXL_FW_XFER_ALIGNMENT;
979 length = len - sizeof(*fw_transfer);
980 if (offset + length > CXL_FW_SIZE) {
981 return CXL_MBOX_INVALID_INPUT;
982 }
983
984 if (cci->fw.transferring) {
985 if (fw_transfer->action == CXL_FW_XFER_ACTION_FULL ||
986 fw_transfer->action == CXL_FW_XFER_ACTION_INIT) {
987 return CXL_MBOX_FW_XFER_IN_PROGRESS;
988 }
989 /*
990 * Abort partitioned package transfer if over 30 secs
991 * between parts. As opposed to the explicit ABORT action,
992 * semantically treat this condition as an error - as
993 * if a part action were passed without a previous INIT.
994 */
995 if (difftime(time(NULL), cci->fw.last_partxfer) > 30.0) {
996 cci->fw.transferring = false;
997 return CXL_MBOX_INVALID_INPUT;
998 }
999 } else if (fw_transfer->action == CXL_FW_XFER_ACTION_CONTINUE ||
1000 fw_transfer->action == CXL_FW_XFER_ACTION_END) {
1001 return CXL_MBOX_INVALID_INPUT;
1002 }
1003
1004 /* allow back-to-back retransmission */
1005 if ((offset != cci->fw.prev_offset || length != cci->fw.prev_len) &&
1006 (fw_transfer->action == CXL_FW_XFER_ACTION_CONTINUE ||
1007 fw_transfer->action == CXL_FW_XFER_ACTION_END)) {
1008 /* verify no overlaps */
1009 if (offset < cci->fw.prev_offset + cci->fw.prev_len) {
1010 return CXL_MBOX_FW_XFER_OUT_OF_ORDER;
1011 }
1012 }
1013
1014 switch (fw_transfer->action) {
1015 case CXL_FW_XFER_ACTION_FULL: /* ignores offset */
1016 case CXL_FW_XFER_ACTION_END:
1017 if (fw_transfer->slot == 0 ||
1018 fw_transfer->slot == cci->fw.active_slot ||
1019 fw_transfer->slot > CXL_FW_SLOTS) {
1020 return CXL_MBOX_FW_INVALID_SLOT;
1021 }
1022
1023 /* mark the slot used upon bg completion */
1024 break;
1025 case CXL_FW_XFER_ACTION_INIT:
1026 if (offset != 0) {
1027 return CXL_MBOX_INVALID_INPUT;
1028 }
1029
1030 cci->fw.transferring = true;
1031 cci->fw.prev_offset = offset;
1032 cci->fw.prev_len = length;
1033 break;
1034 case CXL_FW_XFER_ACTION_CONTINUE:
1035 cci->fw.prev_offset = offset;
1036 cci->fw.prev_len = length;
1037 break;
1038 default:
1039 return CXL_MBOX_INVALID_INPUT;
1040 }
1041
1042 if (fw_transfer->action == CXL_FW_XFER_ACTION_FULL) {
1043 cci->bg.runtime = 10 * 1000UL;
1044 } else {
1045 cci->bg.runtime = 2 * 1000UL;
1046 }
1047 /* keep relevant context for bg completion */
1048 cci->fw.curr_action = fw_transfer->action;
1049 cci->fw.curr_slot = fw_transfer->slot;
1050 *len_out = 0;
1051
1052 return CXL_MBOX_BG_STARTED;
1053 }
1054
1055 static void __do_firmware_xfer(CXLCCI *cci)
1056 {
1057 switch (cci->fw.curr_action) {
1058 case CXL_FW_XFER_ACTION_FULL:
1059 case CXL_FW_XFER_ACTION_END:
1060 cci->fw.slot[cci->fw.curr_slot - 1] = true;
1061 cci->fw.transferring = false;
1062 break;
1063 case CXL_FW_XFER_ACTION_INIT:
1064 case CXL_FW_XFER_ACTION_CONTINUE:
1065 time(&cci->fw.last_partxfer);
1066 break;
1067 default:
1068 break;
1069 }
1070 }
1071
1072 /* CXL r3.1 section 8.2.9.3.3: Activate FW (Opcode 0202h) */
1073 static CXLRetCode cmd_firmware_update_activate(const struct cxl_cmd *cmd,
1074 uint8_t *payload_in,
1075 size_t len,
1076 uint8_t *payload_out,
1077 size_t *len_out,
1078 CXLCCI *cci)
1079 {
1080 struct {
1081 uint8_t action;
1082 uint8_t slot;
1083 } QEMU_PACKED *fw_activate = (void *)payload_in;
1084 QEMU_BUILD_BUG_ON(sizeof(*fw_activate) != 0x2);
1085
1086 if (fw_activate->slot == 0 ||
1087 fw_activate->slot == cci->fw.active_slot ||
1088 fw_activate->slot > CXL_FW_SLOTS) {
1089 return CXL_MBOX_FW_INVALID_SLOT;
1090 }
1091
1092 /* ensure that an actual fw package is there */
1093 if (!cci->fw.slot[fw_activate->slot - 1]) {
1094 return CXL_MBOX_FW_INVALID_SLOT;
1095 }
1096
1097 switch (fw_activate->action) {
1098 case 0: /* online */
1099 cci->fw.active_slot = fw_activate->slot;
1100 break;
1101 case 1: /* reset */
1102 cci->fw.staged_slot = fw_activate->slot;
1103 break;
1104 default:
1105 return CXL_MBOX_INVALID_INPUT;
1106 }
1107
1108 return CXL_MBOX_SUCCESS;
1109 }
1110
1111 /* CXL r3.1 Section 8.2.9.4.1: Get Timestamp (Opcode 0300h) */
1112 static CXLRetCode cmd_timestamp_get(const struct cxl_cmd *cmd,
1113 uint8_t *payload_in,
1114 size_t len_in,
1115 uint8_t *payload_out,
1116 size_t *len_out,
1117 CXLCCI *cci)
1118 {
1119 CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate;
1120 uint64_t final_time = cxl_device_get_timestamp(cxl_dstate);
1121
1122 stq_le_p(payload_out, final_time);
1123 *len_out = 8;
1124
1125 return CXL_MBOX_SUCCESS;
1126 }
1127
1128 /* CXL r3.1 Section 8.2.9.4.2: Set Timestamp (Opcode 0301h) */
1129 static CXLRetCode cmd_timestamp_set(const struct cxl_cmd *cmd,
1130 uint8_t *payload_in,
1131 size_t len_in,
1132 uint8_t *payload_out,
1133 size_t *len_out,
1134 CXLCCI *cci)
1135 {
1136 CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate;
1137
1138 cxl_dstate->timestamp.set = true;
1139 cxl_dstate->timestamp.last_set = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
1140
1141 cxl_dstate->timestamp.host_set = le64_to_cpu(*(uint64_t *)payload_in);
1142
1143 *len_out = 0;
1144 return CXL_MBOX_SUCCESS;
1145 }
1146
1147 /* CXL r3.1 Section 8.2.9.5.2.1: Command Effects Log (CEL) */
1148 static const QemuUUID cel_uuid = {
1149 .data = UUID(0x0da9c0b5, 0xbf41, 0x4b78, 0x8f, 0x79,
1150 0x96, 0xb1, 0x62, 0x3b, 0x3f, 0x17)
1151 };
1152
1153 /* CXL r3.1 Section 8.2.9.5.1: Get Supported Logs (Opcode 0400h) */
1154 static CXLRetCode cmd_logs_get_supported(const struct cxl_cmd *cmd,
1155 uint8_t *payload_in,
1156 size_t len_in,
1157 uint8_t *payload_out,
1158 size_t *len_out,
1159 CXLCCI *cci)
1160 {
1161 struct {
1162 uint16_t entries;
1163 uint8_t rsvd[6];
1164 struct {
1165 QemuUUID uuid;
1166 uint32_t size;
1167 } log_entries[1];
1168 } QEMU_PACKED *supported_logs = (void *)payload_out;
1169 QEMU_BUILD_BUG_ON(sizeof(*supported_logs) != 0x1c);
1170
1171 supported_logs->entries = 1;
1172 supported_logs->log_entries[0].uuid = cel_uuid;
1173 supported_logs->log_entries[0].size = 4 * cci->cel_size;
1174
1175 *len_out = sizeof(*supported_logs);
1176 return CXL_MBOX_SUCCESS;
1177 }
1178
1179 /* CXL r3.1 Section 8.2.9.5.2: Get Log (Opcode 0401h) */
1180 static CXLRetCode cmd_logs_get_log(const struct cxl_cmd *cmd,
1181 uint8_t *payload_in,
1182 size_t len_in,
1183 uint8_t *payload_out,
1184 size_t *len_out,
1185 CXLCCI *cci)
1186 {
1187 struct {
1188 QemuUUID uuid;
1189 uint32_t offset;
1190 uint32_t length;
1191 } QEMU_PACKED QEMU_ALIGNED(16) *get_log;
1192
1193 get_log = (void *)payload_in;
1194
1195 if (get_log->length > cci->payload_max) {
1196 return CXL_MBOX_INVALID_INPUT;
1197 }
1198
1199 if (!qemu_uuid_is_equal(&get_log->uuid, &cel_uuid)) {
1200 return CXL_MBOX_INVALID_LOG;
1201 }
1202
1203 /*
1204 * CXL r3.1 Section 8.2.9.5.2: Get Log (Opcode 0401h)
1205 * The device shall return Invalid Input if the Offset or Length
1206 * fields attempt to access beyond the size of the log as reported by Get
1207 * Supported Log.
1208 *
1209 * Only valid for there to be one entry per opcode, but the length + offset
1210 * may still be greater than that if the inputs are not valid and so access
1211 * beyond the end of cci->cel_log.
1212 */
1213 if ((uint64_t)get_log->offset + get_log->length >= sizeof(cci->cel_log)) {
1214 return CXL_MBOX_INVALID_INPUT;
1215 }
1216
1217 /* Store off everything to local variables so we can wipe out the payload */
1218 *len_out = get_log->length;
1219
1220 memmove(payload_out, cci->cel_log + get_log->offset, get_log->length);
1221
1222 return CXL_MBOX_SUCCESS;
1223 }
1224
1225 /* CXL r3.1 section 8.2.9.6: Features */
1226 /*
1227 * Get Supported Features output payload
1228 * CXL r3.1 section 8.2.9.6.1 Table 8-96
1229 */
1230 typedef struct CXLSupportedFeatureHeader {
1231 uint16_t entries;
1232 uint16_t nsuppfeats_dev;
1233 uint32_t reserved;
1234 } QEMU_PACKED CXLSupportedFeatureHeader;
1235
1236 /*
1237 * Get Supported Features Supported Feature Entry
1238 * CXL r3.1 section 8.2.9.6.1 Table 8-97
1239 */
1240 typedef struct CXLSupportedFeatureEntry {
1241 QemuUUID uuid;
1242 uint16_t feat_index;
1243 uint16_t get_feat_size;
1244 uint16_t set_feat_size;
1245 uint32_t attr_flags;
1246 uint8_t get_feat_version;
1247 uint8_t set_feat_version;
1248 uint16_t set_feat_effects;
1249 uint8_t rsvd[18];
1250 } QEMU_PACKED CXLSupportedFeatureEntry;
1251
1252 /*
1253 * Get Supported Features Supported Feature Entry
1254 * CXL rev 3.1 section 8.2.9.6.1 Table 8-97
1255 */
1256 /* Supported Feature Entry : attribute flags */
1257 #define CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE BIT(0)
1258 #define CXL_FEAT_ENTRY_ATTR_FLAG_DEEPEST_RESET_PERSISTENCE_MASK GENMASK(3, 1)
1259 #define CXL_FEAT_ENTRY_ATTR_FLAG_PERSIST_ACROSS_FIRMWARE_UPDATE BIT(4)
1260 #define CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL BIT(5)
1261 #define CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_SAVED_SEL BIT(6)
1262
1263 /* Supported Feature Entry : set feature effects */
1264 #define CXL_FEAT_ENTRY_SFE_CONFIG_CHANGE_COLD_RESET BIT(0)
1265 #define CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE BIT(1)
1266 #define CXL_FEAT_ENTRY_SFE_IMMEDIATE_DATA_CHANGE BIT(2)
1267 #define CXL_FEAT_ENTRY_SFE_IMMEDIATE_POLICY_CHANGE BIT(3)
1268 #define CXL_FEAT_ENTRY_SFE_IMMEDIATE_LOG_CHANGE BIT(4)
1269 #define CXL_FEAT_ENTRY_SFE_SECURITY_STATE_CHANGE BIT(5)
1270 #define CXL_FEAT_ENTRY_SFE_BACKGROUND_OPERATION BIT(6)
1271 #define CXL_FEAT_ENTRY_SFE_SUPPORT_SECONDARY_MAILBOX BIT(7)
1272 #define CXL_FEAT_ENTRY_SFE_SUPPORT_ABORT_BACKGROUND_OPERATION BIT(8)
1273 #define CXL_FEAT_ENTRY_SFE_CEL_VALID BIT(9)
1274 #define CXL_FEAT_ENTRY_SFE_CONFIG_CHANGE_CONV_RESET BIT(10)
1275 #define CXL_FEAT_ENTRY_SFE_CONFIG_CHANGE_CXL_RESET BIT(11)
1276
1277 enum CXL_SUPPORTED_FEATURES_LIST {
1278 CXL_FEATURE_PATROL_SCRUB = 0,
1279 CXL_FEATURE_ECS,
1280 CXL_FEATURE_SPPR,
1281 CXL_FEATURE_HPPR,
1282 CXL_FEATURE_CACHELINE_SPARING,
1283 CXL_FEATURE_ROW_SPARING,
1284 CXL_FEATURE_BANK_SPARING,
1285 CXL_FEATURE_RANK_SPARING,
1286 CXL_FEATURE_MAX
1287 };
1288
1289 /* Get Feature CXL 3.1 Spec 8.2.9.6.2 */
1290 /*
1291 * Get Feature input payload
1292 * CXL r3.1 section 8.2.9.6.2 Table 8-99
1293 */
1294 /* Get Feature : Payload in selection */
1295 enum CXL_GET_FEATURE_SELECTION {
1296 CXL_GET_FEATURE_SEL_CURRENT_VALUE,
1297 CXL_GET_FEATURE_SEL_DEFAULT_VALUE,
1298 CXL_GET_FEATURE_SEL_SAVED_VALUE,
1299 CXL_GET_FEATURE_SEL_MAX
1300 };
1301
1302 /* Set Feature CXL 3.1 Spec 8.2.9.6.3 */
1303 /*
1304 * Set Feature input payload
1305 * CXL r3.1 section 8.2.9.6.3 Table 8-101
1306 */
1307 typedef struct CXLSetFeatureInHeader {
1308 QemuUUID uuid;
1309 uint32_t flags;
1310 uint16_t offset;
1311 uint8_t version;
1312 uint8_t rsvd[9];
1313 } QEMU_PACKED QEMU_ALIGNED(16) CXLSetFeatureInHeader;
1314
1315 /* Set Feature : Payload in flags */
1316 #define CXL_SET_FEATURE_FLAG_DATA_TRANSFER_MASK 0x7
1317 enum CXL_SET_FEATURE_FLAG_DATA_TRANSFER {
1318 CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER,
1319 CXL_SET_FEATURE_FLAG_INITIATE_DATA_TRANSFER,
1320 CXL_SET_FEATURE_FLAG_CONTINUE_DATA_TRANSFER,
1321 CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER,
1322 CXL_SET_FEATURE_FLAG_ABORT_DATA_TRANSFER,
1323 CXL_SET_FEATURE_FLAG_DATA_TRANSFER_MAX
1324 };
1325 #define CXL_SET_FEAT_DATA_SAVED_ACROSS_RESET BIT(3)
1326
1327 /* CXL r3.2 section 8.2.10.7.2.1: sPPR Feature Discovery and Configuration */
1328 static const QemuUUID soft_ppr_uuid = {
1329 .data = UUID(0x892ba475, 0xfad8, 0x474e, 0x9d, 0x3e,
1330 0x69, 0x2c, 0x91, 0x75, 0x68, 0xbb)
1331 };
1332
1333 typedef struct CXLMemSoftPPRSetFeature {
1334 CXLSetFeatureInHeader hdr;
1335 CXLMemSoftPPRWriteAttrs feat_data;
1336 } QEMU_PACKED QEMU_ALIGNED(16) CXLMemSoftPPRSetFeature;
1337
1338 /* CXL r3.2 section 8.2.10.7.2.2: hPPR Feature Discovery and Configuration */
1339 static const QemuUUID hard_ppr_uuid = {
1340 .data = UUID(0x80ea4521, 0x786f, 0x4127, 0xaf, 0xb1,
1341 0xec, 0x74, 0x59, 0xfb, 0x0e, 0x24)
1342 };
1343
1344 typedef struct CXLMemHardPPRSetFeature {
1345 CXLSetFeatureInHeader hdr;
1346 CXLMemHardPPRWriteAttrs feat_data;
1347 } QEMU_PACKED QEMU_ALIGNED(16) CXLMemHardPPRSetFeature;
1348
1349 /* CXL r3.1 section 8.2.9.9.11.1: Device Patrol Scrub Control Feature */
1350 static const QemuUUID patrol_scrub_uuid = {
1351 .data = UUID(0x96dad7d6, 0xfde8, 0x482b, 0xa7, 0x33,
1352 0x75, 0x77, 0x4e, 0x06, 0xdb, 0x8a)
1353 };
1354
1355 typedef struct CXLMemPatrolScrubSetFeature {
1356 CXLSetFeatureInHeader hdr;
1357 CXLMemPatrolScrubWriteAttrs feat_data;
1358 } QEMU_PACKED QEMU_ALIGNED(16) CXLMemPatrolScrubSetFeature;
1359
1360 /*
1361 * CXL r3.1 section 8.2.9.9.11.2:
1362 * DDR5 Error Check Scrub (ECS) Control Feature
1363 */
1364 static const QemuUUID ecs_uuid = {
1365 .data = UUID(0xe5b13f22, 0x2328, 0x4a14, 0xb8, 0xba,
1366 0xb9, 0x69, 0x1e, 0x89, 0x33, 0x86)
1367 };
1368
1369 typedef struct CXLMemECSSetFeature {
1370 CXLSetFeatureInHeader hdr;
1371 CXLMemECSWriteAttrs feat_data[];
1372 } QEMU_PACKED QEMU_ALIGNED(16) CXLMemECSSetFeature;
1373
1374 /*
1375 * CXL r3.2 section 8.2.10.7.2.3:
1376 * Memory Sparing Features Discovery and Configuration
1377 */
1378 static const QemuUUID cacheline_sparing_uuid = {
1379 .data = UUID(0x96C33386, 0x91dd, 0x44c7, 0x9e, 0xcb,
1380 0xfd, 0xaf, 0x65, 0x03, 0xba, 0xc4)
1381 };
1382
1383 static const QemuUUID row_sparing_uuid = {
1384 .data = UUID(0x450ebf67, 0xb135, 0x4f97, 0xa4, 0x98,
1385 0xc2, 0xd5, 0x7f, 0x27, 0x9b, 0xed)
1386 };
1387
1388 static const QemuUUID bank_sparing_uuid = {
1389 .data = UUID(0x78b79636, 0x90ac, 0x4b64, 0xa4, 0xef,
1390 0xfa, 0xac, 0x5d, 0x18, 0xa8, 0x63)
1391 };
1392
1393 static const QemuUUID rank_sparing_uuid = {
1394 .data = UUID(0x34dbaff5, 0x0552, 0x4281, 0x8f, 0x76,
1395 0xda, 0x0b, 0x5e, 0x7a, 0x76, 0xa7)
1396 };
1397
1398 typedef struct CXLMemSparingSetFeature {
1399 CXLSetFeatureInHeader hdr;
1400 CXLMemSparingWriteAttrs feat_data;
1401 } QEMU_PACKED QEMU_ALIGNED(16) CXLMemSparingSetFeature;
1402
1403 /* CXL r3.1 section 8.2.9.6.1: Get Supported Features (Opcode 0500h) */
1404 static CXLRetCode cmd_features_get_supported(const struct cxl_cmd *cmd,
1405 uint8_t *payload_in,
1406 size_t len_in,
1407 uint8_t *payload_out,
1408 size_t *len_out,
1409 CXLCCI *cci)
1410 {
1411 struct {
1412 uint32_t count;
1413 uint16_t start_index;
1414 uint16_t reserved;
1415 } QEMU_PACKED QEMU_ALIGNED(16) * get_feats_in = (void *)payload_in;
1416
1417 struct {
1418 CXLSupportedFeatureHeader hdr;
1419 CXLSupportedFeatureEntry feat_entries[];
1420 } QEMU_PACKED QEMU_ALIGNED(16) * get_feats_out = (void *)payload_out;
1421 uint16_t index, req_entries;
1422 uint16_t entry;
1423
1424 if (!object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) {
1425 return CXL_MBOX_UNSUPPORTED;
1426 }
1427 if (get_feats_in->count < sizeof(CXLSupportedFeatureHeader) ||
1428 get_feats_in->start_index >= CXL_FEATURE_MAX) {
1429 return CXL_MBOX_INVALID_INPUT;
1430 }
1431
1432 req_entries = (get_feats_in->count -
1433 sizeof(CXLSupportedFeatureHeader)) /
1434 sizeof(CXLSupportedFeatureEntry);
1435 req_entries = MIN(req_entries,
1436 (CXL_FEATURE_MAX - get_feats_in->start_index));
1437
1438 for (entry = 0, index = get_feats_in->start_index;
1439 entry < req_entries; index++) {
1440 switch (index) {
1441 case CXL_FEATURE_SPPR:
1442 /* Fill supported feature entry for soft-PPR */
1443 get_feats_out->feat_entries[entry++] =
1444 (struct CXLSupportedFeatureEntry) {
1445 .uuid = soft_ppr_uuid,
1446 .feat_index = index,
1447 .get_feat_size = sizeof(CXLMemSoftPPRReadAttrs),
1448 .set_feat_size = sizeof(CXLMemSoftPPRWriteAttrs),
1449 .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE |
1450 CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL,
1451 .get_feat_version = CXL_MEMDEV_SPPR_GET_FEATURE_VERSION,
1452 .set_feat_version = CXL_MEMDEV_SPPR_SET_FEATURE_VERSION,
1453 .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
1454 CXL_FEAT_ENTRY_SFE_CEL_VALID,
1455 };
1456 break;
1457 case CXL_FEATURE_HPPR:
1458 /* Fill supported feature entry for hard-PPR */
1459 get_feats_out->feat_entries[entry++] =
1460 (struct CXLSupportedFeatureEntry) {
1461 .uuid = hard_ppr_uuid,
1462 .feat_index = index,
1463 .get_feat_size = sizeof(CXLMemHardPPRReadAttrs),
1464 .set_feat_size = sizeof(CXLMemHardPPRWriteAttrs),
1465 .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE |
1466 CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL,
1467 .get_feat_version = CXL_MEMDEV_HPPR_GET_FEATURE_VERSION,
1468 .set_feat_version = CXL_MEMDEV_HPPR_SET_FEATURE_VERSION,
1469 .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
1470 CXL_FEAT_ENTRY_SFE_CEL_VALID,
1471 };
1472 break;
1473 case CXL_FEATURE_PATROL_SCRUB:
1474 /* Fill supported feature entry for device patrol scrub control */
1475 get_feats_out->feat_entries[entry++] =
1476 (struct CXLSupportedFeatureEntry) {
1477 .uuid = patrol_scrub_uuid,
1478 .feat_index = index,
1479 .get_feat_size = sizeof(CXLMemPatrolScrubReadAttrs),
1480 .set_feat_size = sizeof(CXLMemPatrolScrubWriteAttrs),
1481 .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE,
1482 .get_feat_version = CXL_MEMDEV_PS_GET_FEATURE_VERSION,
1483 .set_feat_version = CXL_MEMDEV_PS_SET_FEATURE_VERSION,
1484 .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
1485 CXL_FEAT_ENTRY_SFE_CEL_VALID,
1486 };
1487 break;
1488 case CXL_FEATURE_ECS:
1489 /* Fill supported feature entry for device DDR5 ECS control */
1490 get_feats_out->feat_entries[entry++] =
1491 (struct CXLSupportedFeatureEntry) {
1492 .uuid = ecs_uuid,
1493 .feat_index = index,
1494 .get_feat_size = sizeof(CXLMemECSReadAttrs),
1495 .set_feat_size = sizeof(CXLMemECSWriteAttrs),
1496 .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE,
1497 .get_feat_version = CXL_ECS_GET_FEATURE_VERSION,
1498 .set_feat_version = CXL_ECS_SET_FEATURE_VERSION,
1499 .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
1500 CXL_FEAT_ENTRY_SFE_CEL_VALID,
1501 };
1502 break;
1503 case CXL_FEATURE_CACHELINE_SPARING:
1504 /* Fill supported feature entry for Cacheline Memory Sparing */
1505 get_feats_out->feat_entries[entry++] =
1506 (struct CXLSupportedFeatureEntry) {
1507 .uuid = cacheline_sparing_uuid,
1508 .feat_index = index,
1509 .get_feat_size = sizeof(CXLMemSparingReadAttrs),
1510 .set_feat_size = sizeof(CXLMemSparingWriteAttrs),
1511 .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE |
1512 CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL,
1513 .get_feat_version = CXL_MEMDEV_SPARING_GET_FEATURE_VERSION,
1514 .set_feat_version = CXL_MEMDEV_SPARING_SET_FEATURE_VERSION,
1515 .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
1516 CXL_FEAT_ENTRY_SFE_CEL_VALID,
1517 };
1518 break;
1519 case CXL_FEATURE_ROW_SPARING:
1520 /* Fill supported feature entry for Row Memory Sparing */
1521 get_feats_out->feat_entries[entry++] =
1522 (struct CXLSupportedFeatureEntry) {
1523 .uuid = row_sparing_uuid,
1524 .feat_index = index,
1525 .get_feat_size = sizeof(CXLMemSparingReadAttrs),
1526 .set_feat_size = sizeof(CXLMemSparingWriteAttrs),
1527 .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE |
1528 CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL,
1529 .get_feat_version = CXL_MEMDEV_SPARING_GET_FEATURE_VERSION,
1530 .set_feat_version = CXL_MEMDEV_SPARING_SET_FEATURE_VERSION,
1531 .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
1532 CXL_FEAT_ENTRY_SFE_CEL_VALID,
1533 };
1534 break;
1535 case CXL_FEATURE_BANK_SPARING:
1536 /* Fill supported feature entry for Bank Memory Sparing */
1537 get_feats_out->feat_entries[entry++] =
1538 (struct CXLSupportedFeatureEntry) {
1539 .uuid = bank_sparing_uuid,
1540 .feat_index = index,
1541 .get_feat_size = sizeof(CXLMemSparingReadAttrs),
1542 .set_feat_size = sizeof(CXLMemSparingWriteAttrs),
1543 .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE |
1544 CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL,
1545 .get_feat_version = CXL_MEMDEV_SPARING_GET_FEATURE_VERSION,
1546 .set_feat_version = CXL_MEMDEV_SPARING_SET_FEATURE_VERSION,
1547 .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
1548 CXL_FEAT_ENTRY_SFE_CEL_VALID,
1549 };
1550 break;
1551 case CXL_FEATURE_RANK_SPARING:
1552 /* Fill supported feature entry for Rank Memory Sparing */
1553 get_feats_out->feat_entries[entry++] =
1554 (struct CXLSupportedFeatureEntry) {
1555 .uuid = rank_sparing_uuid,
1556 .feat_index = index,
1557 .get_feat_size = sizeof(CXLMemSparingReadAttrs),
1558 .set_feat_size = sizeof(CXLMemSparingWriteAttrs),
1559 .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE |
1560 CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL,
1561 .get_feat_version = CXL_MEMDEV_SPARING_GET_FEATURE_VERSION,
1562 .set_feat_version = CXL_MEMDEV_SPARING_SET_FEATURE_VERSION,
1563 .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
1564 CXL_FEAT_ENTRY_SFE_CEL_VALID,
1565 };
1566 break;
1567 default:
1568 __builtin_unreachable();
1569 }
1570 }
1571 get_feats_out->hdr.nsuppfeats_dev = CXL_FEATURE_MAX;
1572 get_feats_out->hdr.entries = req_entries;
1573 *len_out = sizeof(CXLSupportedFeatureHeader) +
1574 req_entries * sizeof(CXLSupportedFeatureEntry);
1575
1576 return CXL_MBOX_SUCCESS;
1577 }
1578
1579 /* CXL r3.1 section 8.2.9.6.2: Get Feature (Opcode 0501h) */
1580 static CXLRetCode cmd_features_get_feature(const struct cxl_cmd *cmd,
1581 uint8_t *payload_in,
1582 size_t len_in,
1583 uint8_t *payload_out,
1584 size_t *len_out,
1585 CXLCCI *cci)
1586 {
1587 struct {
1588 QemuUUID uuid;
1589 uint16_t offset;
1590 uint16_t count;
1591 uint8_t selection;
1592 } QEMU_PACKED QEMU_ALIGNED(16) * get_feature;
1593 uint16_t bytes_to_copy = 0;
1594 CXLType3Dev *ct3d;
1595 CXLSetFeatureInfo *set_feat_info;
1596
1597 if (!object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) {
1598 return CXL_MBOX_UNSUPPORTED;
1599 }
1600
1601 ct3d = CXL_TYPE3(cci->d);
1602 get_feature = (void *)payload_in;
1603
1604 set_feat_info = &ct3d->set_feat_info;
1605 if (qemu_uuid_is_equal(&get_feature->uuid, &set_feat_info->uuid)) {
1606 return CXL_MBOX_FEATURE_TRANSFER_IN_PROGRESS;
1607 }
1608
1609 if (get_feature->selection != CXL_GET_FEATURE_SEL_CURRENT_VALUE) {
1610 return CXL_MBOX_UNSUPPORTED;
1611 }
1612 if (get_feature->offset + get_feature->count > cci->payload_max) {
1613 return CXL_MBOX_INVALID_INPUT;
1614 }
1615
1616 if (qemu_uuid_is_equal(&get_feature->uuid, &patrol_scrub_uuid)) {
1617 if (get_feature->offset >= sizeof(CXLMemPatrolScrubReadAttrs)) {
1618 return CXL_MBOX_INVALID_INPUT;
1619 }
1620 bytes_to_copy = sizeof(CXLMemPatrolScrubReadAttrs) -
1621 get_feature->offset;
1622 bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
1623 memcpy(payload_out,
1624 (uint8_t *)&ct3d->patrol_scrub_attrs + get_feature->offset,
1625 bytes_to_copy);
1626 } else if (qemu_uuid_is_equal(&get_feature->uuid, &ecs_uuid)) {
1627 if (get_feature->offset >= sizeof(CXLMemECSReadAttrs)) {
1628 return CXL_MBOX_INVALID_INPUT;
1629 }
1630 bytes_to_copy = sizeof(CXLMemECSReadAttrs) - get_feature->offset;
1631 bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
1632 memcpy(payload_out,
1633 (uint8_t *)&ct3d->ecs_attrs + get_feature->offset,
1634 bytes_to_copy);
1635 } else if (qemu_uuid_is_equal(&get_feature->uuid, &soft_ppr_uuid)) {
1636 if (get_feature->offset >= sizeof(CXLMemSoftPPRReadAttrs)) {
1637 return CXL_MBOX_INVALID_INPUT;
1638 }
1639 bytes_to_copy = sizeof(CXLMemSoftPPRReadAttrs) -
1640 get_feature->offset;
1641 bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
1642 memcpy(payload_out,
1643 (uint8_t *)&ct3d->soft_ppr_attrs + get_feature->offset,
1644 bytes_to_copy);
1645 } else if (qemu_uuid_is_equal(&get_feature->uuid, &hard_ppr_uuid)) {
1646 if (get_feature->offset >= sizeof(CXLMemHardPPRReadAttrs)) {
1647 return CXL_MBOX_INVALID_INPUT;
1648 }
1649 bytes_to_copy = sizeof(CXLMemHardPPRReadAttrs) -
1650 get_feature->offset;
1651 bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
1652 memcpy(payload_out,
1653 (uint8_t *)&ct3d->hard_ppr_attrs + get_feature->offset,
1654 bytes_to_copy);
1655 } else if (qemu_uuid_is_equal(&get_feature->uuid,
1656 &cacheline_sparing_uuid)) {
1657 if (get_feature->offset >= sizeof(CXLMemSparingReadAttrs)) {
1658 return CXL_MBOX_INVALID_INPUT;
1659 }
1660 bytes_to_copy = sizeof(CXLMemSparingReadAttrs) -
1661 get_feature->offset;
1662 bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
1663 memcpy(payload_out,
1664 (uint8_t *)&ct3d->cacheline_sparing_attrs + get_feature->offset,
1665 bytes_to_copy);
1666 } else if (qemu_uuid_is_equal(&get_feature->uuid, &row_sparing_uuid)) {
1667 if (get_feature->offset >= sizeof(CXLMemSparingReadAttrs)) {
1668 return CXL_MBOX_INVALID_INPUT;
1669 }
1670 bytes_to_copy = sizeof(CXLMemSparingReadAttrs) -
1671 get_feature->offset;
1672 bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
1673 memcpy(payload_out,
1674 (uint8_t *)&ct3d->row_sparing_attrs + get_feature->offset,
1675 bytes_to_copy);
1676 } else if (qemu_uuid_is_equal(&get_feature->uuid, &bank_sparing_uuid)) {
1677 if (get_feature->offset >= sizeof(CXLMemSparingReadAttrs)) {
1678 return CXL_MBOX_INVALID_INPUT;
1679 }
1680 bytes_to_copy = sizeof(CXLMemSparingReadAttrs) -
1681 get_feature->offset;
1682 bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
1683 memcpy(payload_out,
1684 (uint8_t *)&ct3d->bank_sparing_attrs + get_feature->offset,
1685 bytes_to_copy);
1686 } else if (qemu_uuid_is_equal(&get_feature->uuid, &rank_sparing_uuid)) {
1687 if (get_feature->offset >= sizeof(CXLMemSparingReadAttrs)) {
1688 return CXL_MBOX_INVALID_INPUT;
1689 }
1690 bytes_to_copy = sizeof(CXLMemSparingReadAttrs) -
1691 get_feature->offset;
1692 bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
1693 memcpy(payload_out,
1694 (uint8_t *)&ct3d->rank_sparing_attrs + get_feature->offset,
1695 bytes_to_copy);
1696 } else {
1697 return CXL_MBOX_UNSUPPORTED;
1698 }
1699
1700 *len_out = bytes_to_copy;
1701
1702 return CXL_MBOX_SUCCESS;
1703 }
1704
1705 /* CXL r3.1 section 8.2.9.6.3: Set Feature (Opcode 0502h) */
1706 static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,
1707 uint8_t *payload_in,
1708 size_t len_in,
1709 uint8_t *payload_out,
1710 size_t *len_out,
1711 CXLCCI *cci)
1712 {
1713 CXLSetFeatureInHeader *hdr = (void *)payload_in;
1714 CXLSetFeatureInfo *set_feat_info;
1715 uint16_t bytes_to_copy = 0;
1716 uint8_t data_transfer_flag;
1717 CXLType3Dev *ct3d;
1718 uint16_t count;
1719
1720 if (len_in < sizeof(*hdr)) {
1721 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
1722 }
1723
1724 if (!object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) {
1725 return CXL_MBOX_UNSUPPORTED;
1726 }
1727 ct3d = CXL_TYPE3(cci->d);
1728 set_feat_info = &ct3d->set_feat_info;
1729
1730 if (!qemu_uuid_is_null(&set_feat_info->uuid) &&
1731 !qemu_uuid_is_equal(&hdr->uuid, &set_feat_info->uuid)) {
1732 return CXL_MBOX_FEATURE_TRANSFER_IN_PROGRESS;
1733 }
1734 if (hdr->flags & CXL_SET_FEAT_DATA_SAVED_ACROSS_RESET) {
1735 set_feat_info->data_saved_across_reset = true;
1736 } else {
1737 set_feat_info->data_saved_across_reset = false;
1738 }
1739
1740 data_transfer_flag =
1741 hdr->flags & CXL_SET_FEATURE_FLAG_DATA_TRANSFER_MASK;
1742 if (data_transfer_flag == CXL_SET_FEATURE_FLAG_INITIATE_DATA_TRANSFER) {
1743 set_feat_info->uuid = hdr->uuid;
1744 set_feat_info->data_size = 0;
1745 }
1746 set_feat_info->data_transfer_flag = data_transfer_flag;
1747 set_feat_info->data_offset = hdr->offset;
1748 bytes_to_copy = len_in - sizeof(CXLSetFeatureInHeader);
1749
1750 if (bytes_to_copy == 0) {
1751 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
1752 }
1753
1754 if (qemu_uuid_is_equal(&hdr->uuid, &patrol_scrub_uuid)) {
1755 CXLMemPatrolScrubSetFeature *ps_set_feature = (void *)payload_in;
1756 CXLMemPatrolScrubWriteAttrs *ps_write_attrs =
1757 &ps_set_feature->feat_data;
1758
1759 if (hdr->version != CXL_MEMDEV_PS_SET_FEATURE_VERSION) {
1760 return CXL_MBOX_UNSUPPORTED;
1761 }
1762
1763 if ((uint32_t)hdr->offset + bytes_to_copy >
1764 sizeof(ct3d->patrol_scrub_wr_attrs)) {
1765 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
1766 }
1767 memcpy((uint8_t *)&ct3d->patrol_scrub_wr_attrs + hdr->offset,
1768 ps_write_attrs,
1769 bytes_to_copy);
1770 set_feat_info->data_size += bytes_to_copy;
1771
1772 if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
1773 data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {
1774 ct3d->patrol_scrub_attrs.scrub_cycle &= ~0xFF;
1775 ct3d->patrol_scrub_attrs.scrub_cycle |=
1776 ct3d->patrol_scrub_wr_attrs.scrub_cycle_hr & 0xFF;
1777 ct3d->patrol_scrub_attrs.scrub_flags &= ~0x1;
1778 ct3d->patrol_scrub_attrs.scrub_flags |=
1779 ct3d->patrol_scrub_wr_attrs.scrub_flags & 0x1;
1780 }
1781 } else if (qemu_uuid_is_equal(&hdr->uuid,
1782 &ecs_uuid)) {
1783 CXLMemECSSetFeature *ecs_set_feature = (void *)payload_in;
1784 CXLMemECSWriteAttrs *ecs_write_attrs = ecs_set_feature->feat_data;
1785
1786 if (hdr->version != CXL_ECS_SET_FEATURE_VERSION) {
1787 return CXL_MBOX_UNSUPPORTED;
1788 }
1789
1790 if ((uint32_t)hdr->offset + bytes_to_copy >
1791 sizeof(ct3d->ecs_wr_attrs)) {
1792 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
1793 }
1794 memcpy((uint8_t *)&ct3d->ecs_wr_attrs + hdr->offset,
1795 ecs_write_attrs,
1796 bytes_to_copy);
1797 set_feat_info->data_size += bytes_to_copy;
1798
1799 if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
1800 data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {
1801 ct3d->ecs_attrs.ecs_log_cap = ct3d->ecs_wr_attrs.ecs_log_cap;
1802 for (count = 0; count < CXL_ECS_NUM_MEDIA_FRUS; count++) {
1803 ct3d->ecs_attrs.fru_attrs[count].ecs_config =
1804 ct3d->ecs_wr_attrs.fru_attrs[count].ecs_config & 0x1F;
1805 }
1806 }
1807 } else if (qemu_uuid_is_equal(&hdr->uuid, &soft_ppr_uuid)) {
1808 CXLMemSoftPPRSetFeature *sppr_set_feature = (void *)payload_in;
1809 CXLMemSoftPPRWriteAttrs *sppr_write_attrs =
1810 &sppr_set_feature->feat_data;
1811
1812 if (hdr->version != CXL_MEMDEV_SPPR_SET_FEATURE_VERSION) {
1813 return CXL_MBOX_UNSUPPORTED;
1814 }
1815
1816 if ((uint32_t)hdr->offset + bytes_to_copy >
1817 sizeof(ct3d->soft_ppr_wr_attrs)) {
1818 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
1819 }
1820 memcpy((uint8_t *)&ct3d->soft_ppr_wr_attrs + hdr->offset,
1821 sppr_write_attrs, bytes_to_copy);
1822 set_feat_info->data_size += bytes_to_copy;
1823
1824 if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
1825 data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {
1826 ct3d->soft_ppr_attrs.op_mode = ct3d->soft_ppr_wr_attrs.op_mode;
1827 ct3d->soft_ppr_attrs.sppr_op_mode =
1828 ct3d->soft_ppr_wr_attrs.sppr_op_mode;
1829 }
1830 } else if (qemu_uuid_is_equal(&hdr->uuid, &hard_ppr_uuid)) {
1831 CXLMemHardPPRSetFeature *hppr_set_feature = (void *)payload_in;
1832 CXLMemHardPPRWriteAttrs *hppr_write_attrs =
1833 &hppr_set_feature->feat_data;
1834
1835 if (hdr->version != CXL_MEMDEV_HPPR_SET_FEATURE_VERSION) {
1836 return CXL_MBOX_UNSUPPORTED;
1837 }
1838
1839 if ((uint32_t)hdr->offset + bytes_to_copy >
1840 sizeof(ct3d->hard_ppr_wr_attrs)) {
1841 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
1842 }
1843 memcpy((uint8_t *)&ct3d->hard_ppr_wr_attrs + hdr->offset,
1844 hppr_write_attrs, bytes_to_copy);
1845 set_feat_info->data_size += bytes_to_copy;
1846
1847 if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
1848 data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {
1849 ct3d->hard_ppr_attrs.op_mode = ct3d->hard_ppr_wr_attrs.op_mode;
1850 ct3d->hard_ppr_attrs.hppr_op_mode =
1851 ct3d->hard_ppr_wr_attrs.hppr_op_mode;
1852 }
1853 } else if (qemu_uuid_is_equal(&hdr->uuid, &cacheline_sparing_uuid)) {
1854 CXLMemSparingSetFeature *mem_sparing_set_feature = (void *)payload_in;
1855 CXLMemSparingWriteAttrs *mem_sparing_write_attrs =
1856 &mem_sparing_set_feature->feat_data;
1857
1858 if (hdr->version != CXL_MEMDEV_SPARING_SET_FEATURE_VERSION) {
1859 return CXL_MBOX_UNSUPPORTED;
1860 }
1861
1862 if ((uint32_t)hdr->offset + bytes_to_copy >
1863 sizeof(ct3d->cacheline_sparing_wr_attrs)) {
1864 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
1865 }
1866 memcpy((uint8_t *)&ct3d->cacheline_sparing_wr_attrs + hdr->offset,
1867 mem_sparing_write_attrs, bytes_to_copy);
1868 set_feat_info->data_size += bytes_to_copy;
1869
1870 if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
1871 data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {
1872 ct3d->cacheline_sparing_attrs.op_mode =
1873 ct3d->cacheline_sparing_wr_attrs.op_mode;
1874 }
1875 } else if (qemu_uuid_is_equal(&hdr->uuid, &row_sparing_uuid)) {
1876 CXLMemSparingSetFeature *mem_sparing_set_feature = (void *)payload_in;
1877 CXLMemSparingWriteAttrs *mem_sparing_write_attrs =
1878 &mem_sparing_set_feature->feat_data;
1879
1880 if (hdr->version != CXL_MEMDEV_SPARING_SET_FEATURE_VERSION) {
1881 return CXL_MBOX_UNSUPPORTED;
1882 }
1883
1884 if ((uint32_t)hdr->offset + bytes_to_copy >
1885 sizeof(ct3d->row_sparing_wr_attrs)) {
1886 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
1887 }
1888 memcpy((uint8_t *)&ct3d->row_sparing_wr_attrs + hdr->offset,
1889 mem_sparing_write_attrs, bytes_to_copy);
1890 set_feat_info->data_size += bytes_to_copy;
1891
1892 if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
1893 data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {
1894 ct3d->row_sparing_attrs.op_mode =
1895 ct3d->row_sparing_wr_attrs.op_mode;
1896 }
1897 } else if (qemu_uuid_is_equal(&hdr->uuid, &bank_sparing_uuid)) {
1898 CXLMemSparingSetFeature *mem_sparing_set_feature = (void *)payload_in;
1899 CXLMemSparingWriteAttrs *mem_sparing_write_attrs =
1900 &mem_sparing_set_feature->feat_data;
1901
1902 if (hdr->version != CXL_MEMDEV_SPARING_SET_FEATURE_VERSION) {
1903 return CXL_MBOX_UNSUPPORTED;
1904 }
1905
1906 if ((uint32_t)hdr->offset + bytes_to_copy >
1907 sizeof(ct3d->bank_sparing_wr_attrs)) {
1908 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
1909 }
1910 memcpy((uint8_t *)&ct3d->bank_sparing_wr_attrs + hdr->offset,
1911 mem_sparing_write_attrs, bytes_to_copy);
1912 set_feat_info->data_size += bytes_to_copy;
1913
1914 if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
1915 data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {
1916 ct3d->bank_sparing_attrs.op_mode =
1917 ct3d->bank_sparing_wr_attrs.op_mode;
1918 }
1919 } else if (qemu_uuid_is_equal(&hdr->uuid, &rank_sparing_uuid)) {
1920 CXLMemSparingSetFeature *mem_sparing_set_feature = (void *)payload_in;
1921 CXLMemSparingWriteAttrs *mem_sparing_write_attrs =
1922 &mem_sparing_set_feature->feat_data;
1923
1924 if (hdr->version != CXL_MEMDEV_SPARING_SET_FEATURE_VERSION) {
1925 return CXL_MBOX_UNSUPPORTED;
1926 }
1927
1928 if ((uint32_t)hdr->offset + bytes_to_copy >
1929 sizeof(ct3d->rank_sparing_wr_attrs)) {
1930 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
1931 }
1932 memcpy((uint8_t *)&ct3d->rank_sparing_wr_attrs + hdr->offset,
1933 mem_sparing_write_attrs, bytes_to_copy);
1934 set_feat_info->data_size += bytes_to_copy;
1935
1936 if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
1937 data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {
1938 ct3d->rank_sparing_attrs.op_mode =
1939 ct3d->rank_sparing_wr_attrs.op_mode;
1940 }
1941 } else {
1942 return CXL_MBOX_UNSUPPORTED;
1943 }
1944
1945 if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
1946 data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER ||
1947 data_transfer_flag == CXL_SET_FEATURE_FLAG_ABORT_DATA_TRANSFER) {
1948 memset(&set_feat_info->uuid, 0, sizeof(QemuUUID));
1949 if (qemu_uuid_is_equal(&hdr->uuid, &patrol_scrub_uuid)) {
1950 memset(&ct3d->patrol_scrub_wr_attrs, 0, set_feat_info->data_size);
1951 } else if (qemu_uuid_is_equal(&hdr->uuid, &ecs_uuid)) {
1952 memset(&ct3d->ecs_wr_attrs, 0, set_feat_info->data_size);
1953 } else if (qemu_uuid_is_equal(&hdr->uuid, &soft_ppr_uuid)) {
1954 memset(&ct3d->soft_ppr_wr_attrs, 0, set_feat_info->data_size);
1955 } else if (qemu_uuid_is_equal(&hdr->uuid, &hard_ppr_uuid)) {
1956 memset(&ct3d->hard_ppr_wr_attrs, 0, set_feat_info->data_size);
1957 } else if (qemu_uuid_is_equal(&hdr->uuid, &cacheline_sparing_uuid)) {
1958 memset(&ct3d->cacheline_sparing_wr_attrs, 0,
1959 set_feat_info->data_size);
1960 } else if (qemu_uuid_is_equal(&hdr->uuid, &row_sparing_uuid)) {
1961 memset(&ct3d->row_sparing_wr_attrs, 0, set_feat_info->data_size);
1962 } else if (qemu_uuid_is_equal(&hdr->uuid, &bank_sparing_uuid)) {
1963 memset(&ct3d->bank_sparing_wr_attrs, 0, set_feat_info->data_size);
1964 } else if (qemu_uuid_is_equal(&hdr->uuid, &rank_sparing_uuid)) {
1965 memset(&ct3d->rank_sparing_wr_attrs, 0, set_feat_info->data_size);
1966 }
1967 set_feat_info->data_transfer_flag = 0;
1968 set_feat_info->data_saved_across_reset = false;
1969 set_feat_info->data_offset = 0;
1970 set_feat_info->data_size = 0;
1971 }
1972
1973 return CXL_MBOX_SUCCESS;
1974 }
1975
1976 #define CXL_MEM_SPARING_FLAGS_QUERY_RESOURCES BIT(0)
1977 #define CXL_MEM_SPARING_FLAGS_HARD_SPARING BIT(1)
1978 #define CXL_MEM_SPARING_FLAGS_SUB_CHANNEL_VALID BIT(2)
1979 #define CXL_MEM_SPARING_FLAGS_NIB_MASK_VALID BIT(3)
1980
1981 typedef struct CXLMemSparingMaintInPayload {
1982 uint8_t flags;
1983 uint8_t channel;
1984 uint8_t rank;
1985 uint8_t nibble_mask[3];
1986 uint8_t bank_group;
1987 uint8_t bank;
1988 uint8_t row[3];
1989 uint16_t column;
1990 uint8_t sub_channel;
1991 } QEMU_PACKED CXLMemSparingMaintInPayload;
1992
1993 static void cxl_create_mem_sparing_event_records(CXLType3Dev *ct3d,
1994 uint8_t maint_op_class, uint8_t maint_op_sub_class,
1995 CXLMaintenance *ent,
1996 CXLMemSparingMaintInPayload *sparing_pi)
1997 {
1998 CXLEventSparing event_rec = {};
1999
2000 cxl_assign_event_header(&event_rec.hdr,
2001 &sparing_uuid,
2002 (1 << CXL_EVENT_TYPE_INFO),
2003 sizeof(event_rec),
2004 cxl_device_get_timestamp(&ct3d->cxl_dstate),
2005 1, maint_op_class, 1, maint_op_sub_class,
2006 0, 0, 0, 0);
2007 if (ent) {
2008 event_rec.flags = 0;
2009 event_rec.result = 0;
2010 stw_le_p(&event_rec.res_avail, 2);
2011 stw_le_p(&event_rec.validity_flags, ent->validity_flags);
2012 event_rec.channel = ent->channel;
2013 event_rec.rank = ent->rank;
2014 st24_le_p(event_rec.nibble_mask, ent->nibble_mask);
2015 event_rec.bank_group = ent->bank_group;
2016 event_rec.bank = ent->bank;
2017 st24_le_p(event_rec.row, ent->row);
2018 stw_le_p(&event_rec.column, ent->column);
2019 event_rec.sub_channel = ent->sub_channel;
2020 if (ent->validity_flags & CXL_MSER_VALID_COMP_ID) {
2021 memcpy(event_rec.component_id, ent->component_id,
2022 sizeof(event_rec.component_id));
2023 }
2024 } else if (sparing_pi) {
2025 event_rec.flags = CXL_MSER_FLAGS_QUERY_RESOURCES;
2026 event_rec.result = 0;
2027 event_rec.validity_flags = CXL_MSER_VALID_CHANNEL |
2028 CXL_MSER_VALID_RANK |
2029 CXL_MSER_VALID_NIB_MASK |
2030 CXL_MSER_VALID_BANK_GROUP |
2031 CXL_MSER_VALID_BANK |
2032 CXL_MSER_VALID_ROW |
2033 CXL_MSER_VALID_COLUMN;
2034 event_rec.res_avail = 1;
2035 event_rec.channel = sparing_pi->channel;
2036 event_rec.rank = sparing_pi->rank;
2037 if (sparing_pi->flags & CXL_MEM_SPARING_FLAGS_NIB_MASK_VALID) {
2038 memcpy(event_rec.nibble_mask, sparing_pi->nibble_mask,
2039 sizeof(sparing_pi->nibble_mask));
2040 }
2041 event_rec.bank_group = sparing_pi->bank_group;
2042 event_rec.bank = sparing_pi->bank;
2043 event_rec.column = sparing_pi->column;
2044 memcpy(event_rec.row, sparing_pi->row, sizeof(sparing_pi->row));
2045 if (sparing_pi->flags & CXL_MEM_SPARING_FLAGS_SUB_CHANNEL_VALID) {
2046 event_rec.sub_channel = sparing_pi->sub_channel;
2047 event_rec.validity_flags |= CXL_MSER_VALID_SUB_CHANNEL;
2048 }
2049 } else {
2050 return;
2051 }
2052
2053 if (cxl_event_insert(&ct3d->cxl_dstate,
2054 CXL_EVENT_TYPE_INFO,
2055 (CXLEventRecordRaw *)&event_rec)) {
2056 cxl_event_irq_assert(ct3d);
2057 }
2058 }
2059
2060 static CXLRetCode cxl_perform_mem_sparing(CXLType3Dev *ct3d, uint8_t sub_class,
2061 void *maint_pi)
2062 {
2063 switch (sub_class) {
2064 case CXL_MEMDEV_MAINT_SUBCLASS_CACHELINE_SPARING:
2065 qemu_log("Cacheline Memory Sparing\n");
2066 return CXL_MBOX_SUCCESS;
2067 case CXL_MEMDEV_MAINT_SUBCLASS_ROW_SPARING:
2068 qemu_log("Row Memory Sparing\n");
2069 return CXL_MBOX_SUCCESS;
2070 case CXL_MEMDEV_MAINT_SUBCLASS_BANK_SPARING:
2071 qemu_log("Bank Memory Sparing\n");
2072 return CXL_MBOX_SUCCESS;
2073 case CXL_MEMDEV_MAINT_SUBCLASS_RANK_SPARING:
2074 qemu_log("Rank Memory Sparing\n");
2075 return CXL_MBOX_SUCCESS;
2076 default:
2077 return CXL_MBOX_INVALID_INPUT;
2078 }
2079 }
2080
2081 static void cxl_perform_ppr(CXLType3Dev *ct3d, uint64_t dpa)
2082 {
2083 CXLMaintenance *ent, *next;
2084
2085 QLIST_FOREACH_SAFE(ent, &ct3d->maint_list, node, next) {
2086 if (dpa == ent->dpa) {
2087 /* Produce a Memory Sparing Event Record */
2088 if (ct3d->soft_ppr_attrs.sppr_op_mode &
2089 CXL_MEMDEV_SPPR_OP_MODE_MEM_SPARING_EV_REC_EN) {
2090 cxl_create_mem_sparing_event_records(ct3d,
2091 CXL_MEMDEV_MAINT_CLASS_SPARING,
2092 CXL_MEMDEV_MAINT_SUBCLASS_CACHELINE_SPARING,
2093 ent, NULL);
2094 }
2095 QLIST_REMOVE(ent, node);
2096 g_free(ent);
2097 break;
2098 }
2099 }
2100 }
2101
2102 /* CXL r3.2 section 8.2.10.7.1 - Perform Maintenance (Opcode 0600h) */
2103 #define MAINTENANCE_PPR_QUERY_RESOURCES BIT(0)
2104
2105 static CXLRetCode cmd_media_perform_maintenance(const struct cxl_cmd *cmd,
2106 uint8_t *payload_in, size_t len_in,
2107 uint8_t *payload_out, size_t *len_out,
2108 CXLCCI *cci)
2109 {
2110 struct {
2111 uint8_t class;
2112 uint8_t subclass;
2113 union {
2114 struct {
2115 uint8_t flags;
2116 uint64_t dpa;
2117 uint8_t nibble_mask[3];
2118 } QEMU_PACKED ppr;
2119 CXLMemSparingMaintInPayload mem_sparing_pi;
2120 };
2121 } QEMU_PACKED *maint_in = (void *)payload_in;
2122 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
2123
2124 if (maintenance_running(cci)) {
2125 return CXL_MBOX_BUSY;
2126 }
2127
2128 switch (maint_in->class) {
2129 case CXL_MEMDEV_MAINT_CLASS_NO_OP:
2130 return CXL_MBOX_SUCCESS; /* nop */
2131 case CXL_MEMDEV_MAINT_CLASS_PPR:
2132 if (maint_in->ppr.flags & MAINTENANCE_PPR_QUERY_RESOURCES) {
2133 return CXL_MBOX_SUCCESS;
2134 }
2135
2136 switch (maint_in->subclass) {
2137 case CXL_MEMDEV_MAINT_SUBCLASS_SPPR:
2138 case CXL_MEMDEV_MAINT_SUBCLASS_HPPR:
2139 cxl_perform_ppr(ct3d, ldq_le_p(&maint_in->ppr.dpa));
2140 return CXL_MBOX_SUCCESS;
2141 default:
2142 return CXL_MBOX_INVALID_INPUT;
2143 }
2144 break;
2145 case CXL_MEMDEV_MAINT_CLASS_SPARING:
2146 if (maint_in->mem_sparing_pi.flags &
2147 CXL_MEM_SPARING_FLAGS_QUERY_RESOURCES) {
2148 /*
2149 * CXL r3.2 sect 8.2.10.7.1.4 - Memory Sparing Maintenance Operation
2150 * Produce Memory Sparing Event record to report resources
2151 * availability.
2152 */
2153 cxl_create_mem_sparing_event_records(ct3d, maint_in->class,
2154 maint_in->subclass, NULL,
2155 &maint_in->mem_sparing_pi);
2156
2157 return CXL_MBOX_SUCCESS;
2158 }
2159
2160 switch (maint_in->subclass) {
2161 case CXL_MEMDEV_MAINT_SUBCLASS_CACHELINE_SPARING:
2162 case CXL_MEMDEV_MAINT_SUBCLASS_ROW_SPARING:
2163 case CXL_MEMDEV_MAINT_SUBCLASS_BANK_SPARING:
2164 case CXL_MEMDEV_MAINT_SUBCLASS_RANK_SPARING:
2165 return cxl_perform_mem_sparing(ct3d, maint_in->subclass,
2166 &maint_in->mem_sparing_pi);
2167 default:
2168 return CXL_MBOX_INVALID_INPUT;
2169 }
2170 break;
2171 default:
2172 return CXL_MBOX_INVALID_INPUT;
2173 }
2174
2175 return CXL_MBOX_SUCCESS;
2176 }
2177
2178 /* CXL r3.1 Section 8.2.9.9.1.1: Identify Memory Device (Opcode 4000h) */
2179 static CXLRetCode cmd_identify_memory_device(const struct cxl_cmd *cmd,
2180 uint8_t *payload_in,
2181 size_t len_in,
2182 uint8_t *payload_out,
2183 size_t *len_out,
2184 CXLCCI *cci)
2185 {
2186 struct {
2187 char fw_revision[0x10];
2188 uint64_t total_capacity;
2189 uint64_t volatile_capacity;
2190 uint64_t persistent_capacity;
2191 uint64_t partition_align;
2192 uint16_t info_event_log_size;
2193 uint16_t warning_event_log_size;
2194 uint16_t failure_event_log_size;
2195 uint16_t fatal_event_log_size;
2196 uint32_t lsa_size;
2197 uint8_t poison_list_max_mer[3];
2198 uint16_t inject_poison_limit;
2199 uint8_t poison_caps;
2200 uint8_t qos_telemetry_caps;
2201 uint16_t dc_event_log_size;
2202 } QEMU_PACKED *id;
2203 QEMU_BUILD_BUG_ON(sizeof(*id) != 0x45);
2204 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
2205 CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d);
2206 CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate;
2207
2208 if ((!QEMU_IS_ALIGNED(cxl_dstate->vmem_size, CXL_CAPACITY_MULTIPLIER)) ||
2209 (!QEMU_IS_ALIGNED(cxl_dstate->pmem_size, CXL_CAPACITY_MULTIPLIER)) ||
2210 (!QEMU_IS_ALIGNED(ct3d->dc.total_capacity, CXL_CAPACITY_MULTIPLIER))) {
2211 return CXL_MBOX_INTERNAL_ERROR;
2212 }
2213
2214 id = (void *)payload_out;
2215
2216 snprintf(id->fw_revision, 0x10, "BWFW VERSION %02d", 0);
2217
2218 stq_le_p(&id->total_capacity,
2219 cxl_dstate->static_mem_size / CXL_CAPACITY_MULTIPLIER);
2220 stq_le_p(&id->persistent_capacity,
2221 cxl_dstate->pmem_size / CXL_CAPACITY_MULTIPLIER);
2222 stq_le_p(&id->volatile_capacity,
2223 cxl_dstate->vmem_size / CXL_CAPACITY_MULTIPLIER);
2224 stl_le_p(&id->lsa_size, cvc->get_lsa_size(ct3d));
2225 /* 256 poison records */
2226 st24_le_p(id->poison_list_max_mer, 256);
2227 /* No limit - so limited by main poison record limit */
2228 stw_le_p(&id->inject_poison_limit, 0);
2229 stw_le_p(&id->dc_event_log_size, CXL_DC_EVENT_LOG_SIZE);
2230
2231 *len_out = sizeof(*id);
2232 return CXL_MBOX_SUCCESS;
2233 }
2234
2235 /* CXL r3.1 Section 8.2.9.9.2.1: Get Partition Info (Opcode 4100h) */
2236 static CXLRetCode cmd_ccls_get_partition_info(const struct cxl_cmd *cmd,
2237 uint8_t *payload_in,
2238 size_t len_in,
2239 uint8_t *payload_out,
2240 size_t *len_out,
2241 CXLCCI *cci)
2242 {
2243 CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate;
2244 struct {
2245 uint64_t active_vmem;
2246 uint64_t active_pmem;
2247 uint64_t next_vmem;
2248 uint64_t next_pmem;
2249 } QEMU_PACKED *part_info = (void *)payload_out;
2250 QEMU_BUILD_BUG_ON(sizeof(*part_info) != 0x20);
2251 CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate);
2252
2253 if ((!QEMU_IS_ALIGNED(cxl_dstate->vmem_size, CXL_CAPACITY_MULTIPLIER)) ||
2254 (!QEMU_IS_ALIGNED(cxl_dstate->pmem_size, CXL_CAPACITY_MULTIPLIER)) ||
2255 (!QEMU_IS_ALIGNED(ct3d->dc.total_capacity, CXL_CAPACITY_MULTIPLIER))) {
2256 return CXL_MBOX_INTERNAL_ERROR;
2257 }
2258
2259 stq_le_p(&part_info->active_vmem,
2260 cxl_dstate->vmem_size / CXL_CAPACITY_MULTIPLIER);
2261 /*
2262 * When both next_vmem and next_pmem are 0, there is no pending change to
2263 * partitioning.
2264 */
2265 stq_le_p(&part_info->next_vmem, 0);
2266 stq_le_p(&part_info->active_pmem,
2267 cxl_dstate->pmem_size / CXL_CAPACITY_MULTIPLIER);
2268 stq_le_p(&part_info->next_pmem, 0);
2269
2270 *len_out = sizeof(*part_info);
2271 return CXL_MBOX_SUCCESS;
2272 }
2273
2274 /* CXL r3.1 Section 8.2.9.9.2.3: Get LSA (Opcode 4102h) */
2275 static CXLRetCode cmd_ccls_get_lsa(const struct cxl_cmd *cmd,
2276 uint8_t *payload_in,
2277 size_t len_in,
2278 uint8_t *payload_out,
2279 size_t *len_out,
2280 CXLCCI *cci)
2281 {
2282 struct {
2283 uint32_t offset;
2284 uint32_t length;
2285 } QEMU_PACKED *get_lsa;
2286 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
2287 CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d);
2288 uint64_t offset, length;
2289
2290 get_lsa = (void *)payload_in;
2291 offset = get_lsa->offset;
2292 length = get_lsa->length;
2293
2294 if (offset + length > cvc->get_lsa_size(ct3d)) {
2295 *len_out = 0;
2296 return CXL_MBOX_INVALID_INPUT;
2297 }
2298
2299 *len_out = cvc->get_lsa(ct3d, payload_out, length, offset);
2300 return CXL_MBOX_SUCCESS;
2301 }
2302
2303 /* CXL r3.1 Section 8.2.9.9.2.4: Set LSA (Opcode 4103h) */
2304 static CXLRetCode cmd_ccls_set_lsa(const struct cxl_cmd *cmd,
2305 uint8_t *payload_in,
2306 size_t len_in,
2307 uint8_t *payload_out,
2308 size_t *len_out,
2309 CXLCCI *cci)
2310 {
2311 struct set_lsa_pl {
2312 uint32_t offset;
2313 uint32_t rsvd;
2314 uint8_t data[];
2315 } QEMU_PACKED;
2316 struct set_lsa_pl *set_lsa_payload = (void *)payload_in;
2317 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
2318 CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d);
2319 const size_t hdr_len = offsetof(struct set_lsa_pl, data);
2320
2321 *len_out = 0;
2322 if (len_in < hdr_len) {
2323 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
2324 }
2325
2326 if (set_lsa_payload->offset + len_in > cvc->get_lsa_size(ct3d) + hdr_len) {
2327 return CXL_MBOX_INVALID_INPUT;
2328 }
2329 len_in -= hdr_len;
2330
2331 cvc->set_lsa(ct3d, set_lsa_payload->data, len_in, set_lsa_payload->offset);
2332 return CXL_MBOX_SUCCESS;
2333 }
2334
2335 /* CXL r3.2 Section 8.2.10.9.3.2 Get Alert Configuration (Opcode 4201h) */
2336 static CXLRetCode cmd_get_alert_config(const struct cxl_cmd *cmd,
2337 uint8_t *payload_in,
2338 size_t len_in,
2339 uint8_t *payload_out,
2340 size_t *len_out,
2341 CXLCCI *cci)
2342 {
2343 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
2344 CXLAlertConfig *out = (CXLAlertConfig *)payload_out;
2345
2346 memcpy(out, &ct3d->alert_config, sizeof(ct3d->alert_config));
2347 *len_out = sizeof(ct3d->alert_config);
2348
2349 return CXL_MBOX_SUCCESS;
2350 }
2351
2352 /* CXL r3.2 Section 8.2.10.9.3.3 Set Alert Configuration (Opcode 4202h) */
2353 static CXLRetCode cmd_set_alert_config(const struct cxl_cmd *cmd,
2354 uint8_t *payload_in,
2355 size_t len_in,
2356 uint8_t *payload_out,
2357 size_t *len_out,
2358 CXLCCI *cci)
2359 {
2360 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
2361 CXLAlertConfig *alert_config = &ct3d->alert_config;
2362 struct {
2363 uint8_t valid_alert_actions;
2364 uint8_t enable_alert_actions;
2365 uint8_t life_used_warn_thresh;
2366 uint8_t rsvd;
2367 uint16_t over_temp_warn_thresh;
2368 uint16_t under_temp_warn_thresh;
2369 uint16_t cor_vmem_err_warn_thresh;
2370 uint16_t cor_pmem_err_warn_thresh;
2371 } QEMU_PACKED *in = (void *)payload_in;
2372
2373 if (in->valid_alert_actions & CXL_ALERTS_LIFE_USED_WARN_THRESH) {
2374 /*
2375 * CXL r3.2 Table 8-149 The life used warning threshold shall be
2376 * less than the life used critical alert value.
2377 */
2378 if (in->life_used_warn_thresh >=
2379 alert_config->life_used_crit_alert_thresh) {
2380 return CXL_MBOX_INVALID_INPUT;
2381 }
2382 alert_config->life_used_warn_thresh = in->life_used_warn_thresh;
2383 alert_config->enable_alerts |= CXL_ALERTS_LIFE_USED_WARN_THRESH;
2384 }
2385
2386 if (in->valid_alert_actions & CXL_ALERTS_OVER_TEMP_WARN_THRESH) {
2387 /*
2388 * CXL r3.2 Table 8-149 The Device Over-Temperature Warning Threshold
2389 * shall be less than the the Device Over-Temperature Critical
2390 * Alert Threshold.
2391 */
2392 if (in->over_temp_warn_thresh >=
2393 alert_config->over_temp_crit_alert_thresh) {
2394 return CXL_MBOX_INVALID_INPUT;
2395 }
2396 alert_config->over_temp_warn_thresh = in->over_temp_warn_thresh;
2397 alert_config->enable_alerts |= CXL_ALERTS_OVER_TEMP_WARN_THRESH;
2398 }
2399
2400 if (in->valid_alert_actions & CXL_ALERTS_UNDER_TEMP_WARN_THRESH) {
2401 /*
2402 * CXL r3.2 Table 8-149 The Device Under-Temperature Warning Threshold
2403 * shall be higher than the the Device Under-Temperature Critical
2404 * Alert Threshold.
2405 */
2406 if (in->under_temp_warn_thresh <=
2407 alert_config->under_temp_crit_alert_thresh) {
2408 return CXL_MBOX_INVALID_INPUT;
2409 }
2410 alert_config->under_temp_warn_thresh = in->under_temp_warn_thresh;
2411 alert_config->enable_alerts |= CXL_ALERTS_UNDER_TEMP_WARN_THRESH;
2412 }
2413
2414 if (in->valid_alert_actions & CXL_ALERTS_COR_VMEM_ERR_WARN_THRESH) {
2415 alert_config->cor_vmem_err_warn_thresh = in->cor_vmem_err_warn_thresh;
2416 alert_config->enable_alerts |= CXL_ALERTS_COR_VMEM_ERR_WARN_THRESH;
2417 }
2418
2419 if (in->valid_alert_actions & CXL_ALERTS_COR_PMEM_ERR_WARN_THRESH) {
2420 alert_config->cor_pmem_err_warn_thresh = in->cor_pmem_err_warn_thresh;
2421 alert_config->enable_alerts |= CXL_ALERTS_COR_PMEM_ERR_WARN_THRESH;
2422 }
2423 return CXL_MBOX_SUCCESS;
2424 }
2425
2426 /* Perform the actual device zeroing */
2427 static void __do_sanitization(CXLType3Dev *ct3d)
2428 {
2429 MemoryRegion *mr;
2430
2431 if (ct3d->hostvmem) {
2432 mr = host_memory_backend_get_memory(ct3d->hostvmem);
2433 if (mr) {
2434 void *hostmem = memory_region_get_ram_ptr(mr);
2435 memset(hostmem, 0, memory_region_size(mr));
2436 }
2437 }
2438
2439 if (ct3d->hostpmem) {
2440 mr = host_memory_backend_get_memory(ct3d->hostpmem);
2441 if (mr) {
2442 void *hostmem = memory_region_get_ram_ptr(mr);
2443 memset(hostmem, 0, memory_region_size(mr));
2444 }
2445 }
2446 if (ct3d->lsa) {
2447 mr = host_memory_backend_get_memory(ct3d->lsa);
2448 if (mr) {
2449 void *lsa = memory_region_get_ram_ptr(mr);
2450 memset(lsa, 0, memory_region_size(mr));
2451 }
2452 }
2453 cxl_discard_all_event_records(&ct3d->cxl_dstate);
2454 }
2455
2456 static int get_sanitize_duration(uint64_t total_mem)
2457 {
2458 int secs = 0;
2459
2460 if (total_mem <= 512) {
2461 secs = 4;
2462 } else if (total_mem <= 1024) {
2463 secs = 8;
2464 } else if (total_mem <= 2 * 1024) {
2465 secs = 15;
2466 } else if (total_mem <= 4 * 1024) {
2467 secs = 30;
2468 } else if (total_mem <= 8 * 1024) {
2469 secs = 60;
2470 } else if (total_mem <= 16 * 1024) {
2471 secs = 2 * 60;
2472 } else if (total_mem <= 32 * 1024) {
2473 secs = 4 * 60;
2474 } else if (total_mem <= 64 * 1024) {
2475 secs = 8 * 60;
2476 } else if (total_mem <= 128 * 1024) {
2477 secs = 15 * 60;
2478 } else if (total_mem <= 256 * 1024) {
2479 secs = 30 * 60;
2480 } else if (total_mem <= 512 * 1024) {
2481 secs = 60 * 60;
2482 } else if (total_mem <= 1024 * 1024) {
2483 secs = 120 * 60;
2484 } else {
2485 secs = 240 * 60; /* max 4 hrs */
2486 }
2487
2488 return secs;
2489 }
2490
2491 /*
2492 * CXL r3.1 Section 8.2.9.9.5.1: Sanitize (Opcode 4400h)
2493 *
2494 * Once the Sanitize command has started successfully, the device shall be
2495 * placed in the media disabled state. If the command fails or is interrupted
2496 * by a reset or power failure, it shall remain in the media disabled state
2497 * until a successful Sanitize command has been completed. During this state:
2498 *
2499 * 1. Memory writes to the device will have no effect, and all memory reads
2500 * will return random values (no user data returned, even for locations that
2501 * the failed Sanitize operation didn’t sanitize yet).
2502 *
2503 * 2. Mailbox commands shall still be processed in the disabled state, except
2504 * that commands that access Sanitized areas shall fail with the Media Disabled
2505 * error code.
2506 */
2507 static CXLRetCode cmd_sanitize_overwrite(const struct cxl_cmd *cmd,
2508 uint8_t *payload_in,
2509 size_t len_in,
2510 uint8_t *payload_out,
2511 size_t *len_out,
2512 CXLCCI *cci)
2513 {
2514 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
2515 uint64_t total_mem; /* in Mb */
2516 int secs;
2517
2518 total_mem = (ct3d->cxl_dstate.vmem_size + ct3d->cxl_dstate.pmem_size) >> 20;
2519 secs = get_sanitize_duration(total_mem);
2520
2521 /* EBUSY other bg cmds as of now */
2522 cci->bg.runtime = secs * 1000UL;
2523 *len_out = 0;
2524
2525 cxl_dev_disable_media(&ct3d->cxl_dstate);
2526
2527 /* sanitize when done */
2528 return CXL_MBOX_BG_STARTED;
2529 }
2530
2531 struct dpa_range_list_entry {
2532 uint64_t starting_dpa;
2533 uint64_t length;
2534 } QEMU_PACKED;
2535
2536 struct CXLSanitizeInfo {
2537 uint32_t dpa_range_count;
2538 uint8_t fill_value;
2539 struct dpa_range_list_entry dpa_range_list[];
2540 } QEMU_PACKED;
2541
2542 static uint64_t get_vmr_size(CXLType3Dev *ct3d, MemoryRegion **vmr)
2543 {
2544 MemoryRegion *mr;
2545 if (ct3d->hostvmem) {
2546 mr = host_memory_backend_get_memory(ct3d->hostvmem);
2547 if (vmr) {
2548 *vmr = mr;
2549 }
2550 return memory_region_size(mr);
2551 }
2552 return 0;
2553 }
2554
2555 static uint64_t get_pmr_size(CXLType3Dev *ct3d, MemoryRegion **pmr)
2556 {
2557 MemoryRegion *mr;
2558 if (ct3d->hostpmem) {
2559 mr = host_memory_backend_get_memory(ct3d->hostpmem);
2560 if (pmr) {
2561 *pmr = mr;
2562 }
2563 return memory_region_size(mr);
2564 }
2565 return 0;
2566 }
2567
2568 static uint64_t get_dc_size(CXLType3Dev *ct3d, MemoryRegion **dc_mr)
2569 {
2570 MemoryRegion *mr;
2571 if (ct3d->dc.host_dc) {
2572 mr = host_memory_backend_get_memory(ct3d->dc.host_dc);
2573 if (dc_mr) {
2574 *dc_mr = mr;
2575 }
2576 return memory_region_size(mr);
2577 }
2578 return 0;
2579 }
2580
2581 static int validate_dpa_addr(CXLType3Dev *ct3d, uint64_t dpa_addr,
2582 size_t length)
2583 {
2584 uint64_t vmr_size, pmr_size, dc_size, dpa_end;
2585
2586 if ((dpa_addr % CXL_CACHE_LINE_SIZE) ||
2587 (length % CXL_CACHE_LINE_SIZE) ||
2588 (length <= 0)) {
2589 return -EINVAL;
2590 }
2591
2592 vmr_size = get_vmr_size(ct3d, NULL);
2593 pmr_size = get_pmr_size(ct3d, NULL);
2594 dc_size = get_dc_size(ct3d, NULL);
2595
2596 /* sanitize 64 bit values coming from guest */
2597 if (uadd64_overflow(dpa_addr, length, &dpa_end)) {
2598 return -EINVAL;
2599 }
2600
2601 if (dpa_end > vmr_size + pmr_size + dc_size) {
2602 return -EINVAL;
2603 }
2604
2605 if (dpa_addr > vmr_size + pmr_size) {
2606 if (!ct3_test_region_block_backed(ct3d, dpa_addr, length)) {
2607 return -ENODEV;
2608 }
2609 }
2610
2611 return 0;
2612 }
2613
2614 static int sanitize_range(CXLType3Dev *ct3d, uint64_t dpa_addr, size_t length,
2615 uint8_t fill_value)
2616 {
2617
2618 uint64_t vmr_size, pmr_size;
2619 AddressSpace *as = NULL;
2620 MemTxAttrs mem_attrs = {};
2621
2622 vmr_size = get_vmr_size(ct3d, NULL);
2623 pmr_size = get_pmr_size(ct3d, NULL);
2624
2625 if (dpa_addr < vmr_size) {
2626 as = &ct3d->hostvmem_as;
2627 } else if (dpa_addr < vmr_size + pmr_size) {
2628 as = &ct3d->hostpmem_as;
2629 } else {
2630 if (!ct3_test_region_block_backed(ct3d, dpa_addr, length)) {
2631 return -ENODEV;
2632 }
2633 as = &ct3d->dc.host_dc_as;
2634 }
2635
2636 return address_space_set(as, dpa_addr, fill_value, length, mem_attrs);
2637 }
2638
2639 /* Perform the actual device zeroing */
2640 static void __do_sanitize(CXLType3Dev *ct3d)
2641 {
2642 struct CXLSanitizeInfo *san_info = ct3d->media_op_sanitize;
2643 int dpa_range_count = san_info->dpa_range_count;
2644 int rc = 0;
2645 int i;
2646
2647 for (i = 0; i < dpa_range_count; i++) {
2648 rc = sanitize_range(ct3d, san_info->dpa_range_list[i].starting_dpa,
2649 san_info->dpa_range_list[i].length,
2650 san_info->fill_value);
2651 if (rc) {
2652 goto exit;
2653 }
2654 }
2655 exit:
2656 g_free(ct3d->media_op_sanitize);
2657 ct3d->media_op_sanitize = NULL;
2658 return;
2659 }
2660
2661 enum {
2662 MEDIA_OP_CLASS_GENERAL = 0x0,
2663 #define MEDIA_OP_GEN_SUBC_DISCOVERY 0x0
2664 MEDIA_OP_CLASS_SANITIZE = 0x1,
2665 #define MEDIA_OP_SAN_SUBC_SANITIZE 0x0
2666 #define MEDIA_OP_SAN_SUBC_ZERO 0x1
2667 };
2668
2669 struct media_op_supported_list_entry {
2670 uint8_t media_op_class;
2671 uint8_t media_op_subclass;
2672 };
2673
2674 struct media_op_discovery_out_pl {
2675 uint64_t dpa_range_granularity;
2676 uint16_t total_supported_operations;
2677 uint16_t num_of_supported_operations;
2678 struct media_op_supported_list_entry entry[];
2679 } QEMU_PACKED;
2680
2681 static const struct media_op_supported_list_entry media_op_matrix[] = {
2682 { MEDIA_OP_CLASS_GENERAL, MEDIA_OP_GEN_SUBC_DISCOVERY },
2683 { MEDIA_OP_CLASS_SANITIZE, MEDIA_OP_SAN_SUBC_SANITIZE },
2684 { MEDIA_OP_CLASS_SANITIZE, MEDIA_OP_SAN_SUBC_ZERO },
2685 };
2686
2687 static CXLRetCode media_operations_discovery(uint8_t *payload_in,
2688 size_t len_in,
2689 uint8_t *payload_out,
2690 size_t *len_out)
2691 {
2692 struct {
2693 uint8_t media_operation_class;
2694 uint8_t media_operation_subclass;
2695 uint8_t rsvd[2];
2696 uint32_t dpa_range_count;
2697 struct {
2698 uint16_t start_index;
2699 uint16_t num_ops;
2700 } discovery_osa;
2701 } QEMU_PACKED *media_op_in_disc_pl = (void *)payload_in;
2702 struct media_op_discovery_out_pl *media_out_pl =
2703 (struct media_op_discovery_out_pl *)payload_out;
2704 int total = ARRAY_SIZE(media_op_matrix) - 1; /* exclude Discovery */
2705 int num_ops, start_index, i;
2706 int count = 0;
2707
2708 if (len_in < sizeof(*media_op_in_disc_pl)) {
2709 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
2710 }
2711
2712 num_ops = media_op_in_disc_pl->discovery_osa.num_ops;
2713 start_index = media_op_in_disc_pl->discovery_osa.start_index;
2714
2715 /*
2716 * As per spec CXL r3.2 8.2.10.9.5.3 dpa_range_count should be zero and
2717 * start index should not exceed the total number of entries for discovery
2718 * sub class command.
2719 */
2720 if (media_op_in_disc_pl->dpa_range_count ||
2721 start_index >= total) {
2722 return CXL_MBOX_INVALID_INPUT;
2723 }
2724
2725 media_out_pl->dpa_range_granularity = CXL_CACHE_LINE_SIZE;
2726 media_out_pl->total_supported_operations = total;
2727
2728 num_ops = MIN(num_ops, total - start_index);
2729 for (i = 0; i < num_ops; i++) {
2730 int idx = start_index + i + 1; /* skip Discovery (first entry) */
2731
2732 media_out_pl->entry[count].media_op_class =
2733 media_op_matrix[idx].media_op_class;
2734 media_out_pl->entry[count].media_op_subclass =
2735 media_op_matrix[idx].media_op_subclass;
2736 count++;
2737 }
2738
2739 media_out_pl->num_of_supported_operations = count;
2740 *len_out = sizeof(*media_out_pl) + count * sizeof(*media_out_pl->entry);
2741 return CXL_MBOX_SUCCESS;
2742 }
2743
2744 static CXLRetCode media_operations_sanitize(CXLType3Dev *ct3d,
2745 uint8_t *payload_in,
2746 size_t len_in,
2747 uint8_t *payload_out,
2748 size_t *len_out,
2749 uint8_t fill_value,
2750 CXLCCI *cci)
2751 {
2752 struct media_operations_sanitize {
2753 uint8_t media_operation_class;
2754 uint8_t media_operation_subclass;
2755 uint8_t rsvd[2];
2756 uint32_t dpa_range_count;
2757 struct dpa_range_list_entry dpa_range_list[];
2758 } QEMU_PACKED *media_op_in_sanitize_pl = (void *)payload_in;
2759 uint32_t dpa_range_count = media_op_in_sanitize_pl->dpa_range_count;
2760 uint64_t total_mem = 0;
2761 size_t dpa_range_list_size;
2762 int secs = 0, i;
2763
2764 if (dpa_range_count == 0) {
2765 return CXL_MBOX_SUCCESS;
2766 }
2767
2768 dpa_range_list_size = dpa_range_count * sizeof(struct dpa_range_list_entry);
2769 if (len_in < (sizeof(*media_op_in_sanitize_pl) + dpa_range_list_size)) {
2770 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
2771 }
2772
2773 for (i = 0; i < dpa_range_count; i++) {
2774 uint64_t start_dpa =
2775 media_op_in_sanitize_pl->dpa_range_list[i].starting_dpa;
2776 uint64_t length = media_op_in_sanitize_pl->dpa_range_list[i].length;
2777
2778 if (validate_dpa_addr(ct3d, start_dpa, length)) {
2779 return CXL_MBOX_INVALID_INPUT;
2780 }
2781 total_mem += length;
2782 }
2783 ct3d->media_op_sanitize = g_malloc0(sizeof(struct CXLSanitizeInfo) +
2784 dpa_range_list_size);
2785
2786 ct3d->media_op_sanitize->dpa_range_count = dpa_range_count;
2787 ct3d->media_op_sanitize->fill_value = fill_value;
2788 memcpy(ct3d->media_op_sanitize->dpa_range_list,
2789 media_op_in_sanitize_pl->dpa_range_list,
2790 dpa_range_list_size);
2791 secs = get_sanitize_duration(total_mem >> 20);
2792
2793 /* EBUSY other bg cmds as of now */
2794 cci->bg.runtime = secs * 1000UL;
2795 *len_out = 0;
2796 /*
2797 * media op sanitize is targeted so no need to disable media or
2798 * clear event logs
2799 */
2800 return CXL_MBOX_BG_STARTED;
2801 }
2802
2803 static CXLRetCode cmd_media_operations(const struct cxl_cmd *cmd,
2804 uint8_t *payload_in,
2805 size_t len_in,
2806 uint8_t *payload_out,
2807 size_t *len_out,
2808 CXLCCI *cci)
2809 {
2810 struct {
2811 uint8_t media_operation_class;
2812 uint8_t media_operation_subclass;
2813 uint8_t rsvd[2];
2814 uint32_t dpa_range_count;
2815 } QEMU_PACKED *media_op_in_common_pl = (void *)payload_in;
2816 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
2817 uint8_t media_op_cl = 0;
2818 uint8_t media_op_subclass = 0;
2819
2820 if (len_in < sizeof(*media_op_in_common_pl)) {
2821 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
2822 }
2823
2824 media_op_cl = media_op_in_common_pl->media_operation_class;
2825 media_op_subclass = media_op_in_common_pl->media_operation_subclass;
2826
2827 switch (media_op_cl) {
2828 case MEDIA_OP_CLASS_GENERAL:
2829 if (media_op_subclass != MEDIA_OP_GEN_SUBC_DISCOVERY) {
2830 return CXL_MBOX_UNSUPPORTED;
2831 }
2832
2833 return media_operations_discovery(payload_in, len_in, payload_out,
2834 len_out);
2835 case MEDIA_OP_CLASS_SANITIZE:
2836 switch (media_op_subclass) {
2837 case MEDIA_OP_SAN_SUBC_SANITIZE:
2838 return media_operations_sanitize(ct3d, payload_in, len_in,
2839 payload_out, len_out, 0xF,
2840 cci);
2841 case MEDIA_OP_SAN_SUBC_ZERO:
2842 return media_operations_sanitize(ct3d, payload_in, len_in,
2843 payload_out, len_out, 0,
2844 cci);
2845 default:
2846 return CXL_MBOX_UNSUPPORTED;
2847 }
2848 default:
2849 return CXL_MBOX_UNSUPPORTED;
2850 }
2851 }
2852
2853 static CXLRetCode cmd_get_security_state(const struct cxl_cmd *cmd,
2854 uint8_t *payload_in,
2855 size_t len_in,
2856 uint8_t *payload_out,
2857 size_t *len_out,
2858 CXLCCI *cci)
2859 {
2860 uint32_t *state = (uint32_t *)payload_out;
2861
2862 *state = 0;
2863 *len_out = 4;
2864 return CXL_MBOX_SUCCESS;
2865 }
2866
2867 /*
2868 * CXL r3.1 Section 8.2.9.9.4.1: Get Poison List (Opcode 4300h)
2869 *
2870 * This is very inefficient, but good enough for now!
2871 * Also the payload will always fit, so no need to handle the MORE flag and
2872 * make this stateful. We may want to allow longer poison lists to aid
2873 * testing that kernel functionality.
2874 */
2875 static CXLRetCode cmd_media_get_poison_list(const struct cxl_cmd *cmd,
2876 uint8_t *payload_in,
2877 size_t len_in,
2878 uint8_t *payload_out,
2879 size_t *len_out,
2880 CXLCCI *cci)
2881 {
2882 struct get_poison_list_pl {
2883 uint64_t pa;
2884 uint64_t length;
2885 } QEMU_PACKED;
2886
2887 struct get_poison_list_out_pl {
2888 uint8_t flags;
2889 uint8_t rsvd1;
2890 uint64_t overflow_timestamp;
2891 uint16_t count;
2892 uint8_t rsvd2[0x14];
2893 struct {
2894 uint64_t addr;
2895 uint32_t length;
2896 uint32_t resv;
2897 } QEMU_PACKED records[];
2898 } QEMU_PACKED;
2899
2900 struct get_poison_list_pl *in = (void *)payload_in;
2901 struct get_poison_list_out_pl *out = (void *)payload_out;
2902 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
2903 uint16_t record_count = 0, i = 0;
2904 uint64_t query_start, query_length;
2905 CXLPoisonList *poison_list = &ct3d->poison_list;
2906 CXLPoison *ent;
2907 uint16_t out_pl_len;
2908
2909 query_start = ldq_le_p(&in->pa);
2910 /* 64 byte alignment required */
2911 if (query_start & 0x3f) {
2912 return CXL_MBOX_INVALID_INPUT;
2913 }
2914 query_length = ldq_le_p(&in->length) * CXL_CACHE_LINE_SIZE;
2915
2916 QLIST_FOREACH(ent, poison_list, node) {
2917 /* Check for no overlap */
2918 if (!ranges_overlap(ent->start, ent->length,
2919 query_start, query_length)) {
2920 continue;
2921 }
2922 record_count++;
2923 }
2924 out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]);
2925 assert(out_pl_len <= CXL_MAILBOX_MAX_PAYLOAD_SIZE);
2926
2927 QLIST_FOREACH(ent, poison_list, node) {
2928 uint64_t start, stop;
2929
2930 /* Check for no overlap */
2931 if (!ranges_overlap(ent->start, ent->length,
2932 query_start, query_length)) {
2933 continue;
2934 }
2935
2936 /* Deal with overlap */
2937 start = MAX(ROUND_DOWN(ent->start, 64ull), query_start);
2938 stop = MIN(ROUND_DOWN(ent->start, 64ull) + ent->length,
2939 query_start + query_length);
2940 stq_le_p(&out->records[i].addr, start | (ent->type & 0x7));
2941 stl_le_p(&out->records[i].length, (stop - start) / CXL_CACHE_LINE_SIZE);
2942 i++;
2943 }
2944 if (ct3d->poison_list_overflowed) {
2945 out->flags = (1 << 1);
2946 stq_le_p(&out->overflow_timestamp, ct3d->poison_list_overflow_ts);
2947 }
2948 if (scan_media_running(cci)) {
2949 out->flags |= (1 << 2);
2950 }
2951
2952 stw_le_p(&out->count, record_count);
2953 *len_out = out_pl_len;
2954 return CXL_MBOX_SUCCESS;
2955 }
2956
2957 /* CXL r3.1 Section 8.2.9.9.4.2: Inject Poison (Opcode 4301h) */
2958 static CXLRetCode cmd_media_inject_poison(const struct cxl_cmd *cmd,
2959 uint8_t *payload_in,
2960 size_t len_in,
2961 uint8_t *payload_out,
2962 size_t *len_out,
2963 CXLCCI *cci)
2964 {
2965 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
2966 CXLPoisonList *poison_list = &ct3d->poison_list;
2967 CXLPoison *ent;
2968 struct inject_poison_pl {
2969 uint64_t dpa;
2970 };
2971 struct inject_poison_pl *in = (void *)payload_in;
2972 uint64_t dpa = ldq_le_p(&in->dpa);
2973 CXLPoison *p;
2974
2975 QLIST_FOREACH(ent, poison_list, node) {
2976 if (dpa >= ent->start &&
2977 dpa + CXL_CACHE_LINE_SIZE <= ent->start + ent->length) {
2978 return CXL_MBOX_SUCCESS;
2979 }
2980 }
2981 /*
2982 * Freeze the list if there is an on-going scan media operation.
2983 */
2984 if (scan_media_running(cci)) {
2985 /*
2986 * XXX: Spec is ambiguous - is this case considered
2987 * a successful return despite not adding to the list?
2988 */
2989 goto success;
2990 }
2991
2992 if (ct3d->poison_list_cnt == CXL_POISON_LIST_LIMIT) {
2993 return CXL_MBOX_INJECT_POISON_LIMIT;
2994 }
2995 p = g_new0(CXLPoison, 1);
2996
2997 p->length = CXL_CACHE_LINE_SIZE;
2998 p->start = dpa;
2999 p->type = CXL_POISON_TYPE_INJECTED;
3000
3001 /*
3002 * Possible todo: Merge with existing entry if next to it and if same type
3003 */
3004 QLIST_INSERT_HEAD(poison_list, p, node);
3005 ct3d->poison_list_cnt++;
3006 success:
3007 *len_out = 0;
3008
3009 return CXL_MBOX_SUCCESS;
3010 }
3011
3012 /* CXL r3.1 Section 8.2.9.9.4.3: Clear Poison (Opcode 4302h */
3013 static CXLRetCode cmd_media_clear_poison(const struct cxl_cmd *cmd,
3014 uint8_t *payload_in,
3015 size_t len_in,
3016 uint8_t *payload_out,
3017 size_t *len_out,
3018 CXLCCI *cci)
3019 {
3020 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
3021 CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate;
3022 CXLPoisonList *poison_list = &ct3d->poison_list;
3023 CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d);
3024 struct clear_poison_pl {
3025 uint64_t dpa;
3026 uint8_t data[64];
3027 };
3028 CXLPoison *ent;
3029 uint64_t dpa;
3030
3031 struct clear_poison_pl *in = (void *)payload_in;
3032
3033 dpa = ldq_le_p(&in->dpa);
3034 if (dpa + CXL_CACHE_LINE_SIZE > cxl_dstate->static_mem_size +
3035 ct3d->dc.total_capacity) {
3036 return CXL_MBOX_INVALID_PA;
3037 }
3038
3039 /* Clearing a region with no poison is not an error so always do so */
3040 if (cvc->set_cacheline) {
3041 if (!cvc->set_cacheline(ct3d, dpa, in->data)) {
3042 return CXL_MBOX_INTERNAL_ERROR;
3043 }
3044 }
3045
3046 /*
3047 * Freeze the list if there is an on-going scan media operation.
3048 */
3049 if (scan_media_running(cci)) {
3050 /*
3051 * XXX: Spec is ambiguous - is this case considered
3052 * a successful return despite not removing from the list?
3053 */
3054 goto success;
3055 }
3056
3057 QLIST_FOREACH(ent, poison_list, node) {
3058 /*
3059 * Test for contained in entry. Simpler than general case
3060 * as clearing 64 bytes and entries 64 byte aligned
3061 */
3062 if ((dpa >= ent->start) && (dpa < ent->start + ent->length)) {
3063 break;
3064 }
3065 }
3066 if (!ent) {
3067 goto success;
3068 }
3069
3070 QLIST_REMOVE(ent, node);
3071 ct3d->poison_list_cnt--;
3072
3073 if (dpa > ent->start) {
3074 CXLPoison *frag;
3075 /* Cannot overflow as replacing existing entry */
3076
3077 frag = g_new0(CXLPoison, 1);
3078
3079 frag->start = ent->start;
3080 frag->length = dpa - ent->start;
3081 frag->type = ent->type;
3082
3083 QLIST_INSERT_HEAD(poison_list, frag, node);
3084 ct3d->poison_list_cnt++;
3085 }
3086
3087 if (dpa + CXL_CACHE_LINE_SIZE < ent->start + ent->length) {
3088 CXLPoison *frag;
3089
3090 if (ct3d->poison_list_cnt == CXL_POISON_LIST_LIMIT) {
3091 cxl_set_poison_list_overflowed(ct3d);
3092 } else {
3093 frag = g_new0(CXLPoison, 1);
3094
3095 frag->start = dpa + CXL_CACHE_LINE_SIZE;
3096 frag->length = ent->start + ent->length - frag->start;
3097 frag->type = ent->type;
3098 QLIST_INSERT_HEAD(poison_list, frag, node);
3099 ct3d->poison_list_cnt++;
3100 }
3101 }
3102 /* Any fragments have been added, free original entry */
3103 g_free(ent);
3104 success:
3105 *len_out = 0;
3106
3107 return CXL_MBOX_SUCCESS;
3108 }
3109
3110 /*
3111 * CXL r3.1 section 8.2.9.9.4.4: Get Scan Media Capabilities
3112 */
3113 static CXLRetCode
3114 cmd_media_get_scan_media_capabilities(const struct cxl_cmd *cmd,
3115 uint8_t *payload_in,
3116 size_t len_in,
3117 uint8_t *payload_out,
3118 size_t *len_out,
3119 CXLCCI *cci)
3120 {
3121 struct get_scan_media_capabilities_pl {
3122 uint64_t pa;
3123 uint64_t length;
3124 } QEMU_PACKED;
3125
3126 struct get_scan_media_capabilities_out_pl {
3127 uint32_t estimated_runtime_ms;
3128 };
3129
3130 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
3131 CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate;
3132 struct get_scan_media_capabilities_pl *in = (void *)payload_in;
3133 struct get_scan_media_capabilities_out_pl *out = (void *)payload_out;
3134 uint64_t query_start;
3135 uint64_t query_length;
3136
3137 query_start = ldq_le_p(&in->pa);
3138 /* 64 byte alignment required */
3139 if (query_start & 0x3f) {
3140 return CXL_MBOX_INVALID_INPUT;
3141 }
3142 query_length = ldq_le_p(&in->length) * CXL_CACHE_LINE_SIZE;
3143
3144 if (query_start + query_length > cxl_dstate->static_mem_size) {
3145 return CXL_MBOX_INVALID_PA;
3146 }
3147
3148 /*
3149 * Just use 400 nanosecond access/read latency + 100 ns for
3150 * the cost of updating the poison list. For small enough
3151 * chunks return at least 1 ms.
3152 */
3153 stl_le_p(&out->estimated_runtime_ms,
3154 MAX(1, query_length * (0.0005L / 64)));
3155
3156 *len_out = sizeof(*out);
3157 return CXL_MBOX_SUCCESS;
3158 }
3159
3160 static void __do_scan_media(CXLType3Dev *ct3d)
3161 {
3162 CXLPoison *ent;
3163 unsigned int results_cnt = 0;
3164
3165 QLIST_FOREACH(ent, &ct3d->scan_media_results, node) {
3166 results_cnt++;
3167 }
3168
3169 /* only scan media may clear the overflow */
3170 if (ct3d->poison_list_overflowed &&
3171 ct3d->poison_list_cnt == results_cnt) {
3172 cxl_clear_poison_list_overflowed(ct3d);
3173 }
3174 /* scan media has run since last conventional reset */
3175 ct3d->scan_media_hasrun = true;
3176 }
3177
3178 /*
3179 * CXL r3.1 section 8.2.9.9.4.5: Scan Media
3180 */
3181 static CXLRetCode cmd_media_scan_media(const struct cxl_cmd *cmd,
3182 uint8_t *payload_in,
3183 size_t len_in,
3184 uint8_t *payload_out,
3185 size_t *len_out,
3186 CXLCCI *cci)
3187 {
3188 struct scan_media_pl {
3189 uint64_t pa;
3190 uint64_t length;
3191 uint8_t flags;
3192 } QEMU_PACKED;
3193
3194 struct scan_media_pl *in = (void *)payload_in;
3195 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
3196 CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate;
3197 uint64_t query_start;
3198 uint64_t query_length;
3199 CXLPoison *ent, *next;
3200
3201 query_start = ldq_le_p(&in->pa);
3202 /* 64 byte alignment required */
3203 if (query_start & 0x3f) {
3204 return CXL_MBOX_INVALID_INPUT;
3205 }
3206 query_length = ldq_le_p(&in->length) * CXL_CACHE_LINE_SIZE;
3207
3208 if (query_start + query_length > cxl_dstate->static_mem_size) {
3209 return CXL_MBOX_INVALID_PA;
3210 }
3211 if (ct3d->dc.num_regions && query_start + query_length >=
3212 cxl_dstate->static_mem_size + ct3d->dc.total_capacity) {
3213 return CXL_MBOX_INVALID_PA;
3214 }
3215
3216 if (in->flags == 0) { /* TODO */
3217 qemu_log_mask(LOG_UNIMP,
3218 "Scan Media Event Log is unsupported\n");
3219 }
3220
3221 /* any previous results are discarded upon a new Scan Media */
3222 QLIST_FOREACH_SAFE(ent, &ct3d->scan_media_results, node, next) {
3223 QLIST_REMOVE(ent, node);
3224 g_free(ent);
3225 }
3226
3227 /* kill the poison list - it will be recreated */
3228 if (ct3d->poison_list_overflowed) {
3229 QLIST_FOREACH_SAFE(ent, &ct3d->poison_list, node, next) {
3230 QLIST_REMOVE(ent, node);
3231 g_free(ent);
3232 ct3d->poison_list_cnt--;
3233 }
3234 }
3235
3236 /*
3237 * Scan the backup list and move corresponding entries
3238 * into the results list, updating the poison list
3239 * when possible.
3240 */
3241 QLIST_FOREACH_SAFE(ent, &ct3d->poison_list_bkp, node, next) {
3242 CXLPoison *res;
3243
3244 if (ent->start >= query_start + query_length ||
3245 ent->start + ent->length <= query_start) {
3246 continue;
3247 }
3248
3249 /*
3250 * If a Get Poison List cmd comes in while this
3251 * scan is being done, it will see the new complete
3252 * list, while setting the respective flag.
3253 */
3254 if (ct3d->poison_list_cnt < CXL_POISON_LIST_LIMIT) {
3255 CXLPoison *p = g_new0(CXLPoison, 1);
3256
3257 p->start = ent->start;
3258 p->length = ent->length;
3259 p->type = ent->type;
3260 QLIST_INSERT_HEAD(&ct3d->poison_list, p, node);
3261 ct3d->poison_list_cnt++;
3262 }
3263
3264 res = g_new0(CXLPoison, 1);
3265 res->start = ent->start;
3266 res->length = ent->length;
3267 res->type = ent->type;
3268 QLIST_INSERT_HEAD(&ct3d->scan_media_results, res, node);
3269
3270 QLIST_REMOVE(ent, node);
3271 g_free(ent);
3272 }
3273
3274 cci->bg.runtime = MAX(1, query_length * (0.0005L / 64));
3275 *len_out = 0;
3276
3277 return CXL_MBOX_BG_STARTED;
3278 }
3279
3280 /*
3281 * CXL r3.1 section 8.2.9.9.4.6: Get Scan Media Results
3282 */
3283 static CXLRetCode cmd_media_get_scan_media_results(const struct cxl_cmd *cmd,
3284 uint8_t *payload_in,
3285 size_t len_in,
3286 uint8_t *payload_out,
3287 size_t *len_out,
3288 CXLCCI *cci)
3289 {
3290 struct get_scan_media_results_out_pl {
3291 uint64_t dpa_restart;
3292 uint64_t length;
3293 uint8_t flags;
3294 uint8_t rsvd1;
3295 uint16_t count;
3296 uint8_t rsvd2[0xc];
3297 struct {
3298 uint64_t addr;
3299 uint32_t length;
3300 uint32_t resv;
3301 } QEMU_PACKED records[];
3302 } QEMU_PACKED;
3303
3304 struct get_scan_media_results_out_pl *out = (void *)payload_out;
3305 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
3306 CXLPoisonList *scan_media_results = &ct3d->scan_media_results;
3307 CXLPoison *ent, *next;
3308 uint16_t total_count = 0, record_count = 0, i = 0;
3309 uint16_t out_pl_len;
3310
3311 if (!ct3d->scan_media_hasrun) {
3312 return CXL_MBOX_UNSUPPORTED;
3313 }
3314
3315 /*
3316 * Calculate limits, all entries are within the same address range of the
3317 * last scan media call.
3318 */
3319 QLIST_FOREACH(ent, scan_media_results, node) {
3320 size_t rec_size = record_count * sizeof(out->records[0]);
3321
3322 if (sizeof(*out) + rec_size < CXL_MAILBOX_MAX_PAYLOAD_SIZE) {
3323 record_count++;
3324 }
3325 total_count++;
3326 }
3327
3328 out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]);
3329 assert(out_pl_len <= CXL_MAILBOX_MAX_PAYLOAD_SIZE);
3330
3331 memset(out, 0, out_pl_len);
3332 QLIST_FOREACH_SAFE(ent, scan_media_results, node, next) {
3333 uint64_t start, stop;
3334
3335 if (i == record_count) {
3336 break;
3337 }
3338
3339 start = ROUND_DOWN(ent->start, 64ull);
3340 stop = ROUND_DOWN(ent->start, 64ull) + ent->length;
3341 stq_le_p(&out->records[i].addr, start);
3342 stl_le_p(&out->records[i].length, (stop - start) / CXL_CACHE_LINE_SIZE);
3343 i++;
3344
3345 /* consume the returning entry */
3346 QLIST_REMOVE(ent, node);
3347 g_free(ent);
3348 }
3349
3350 stw_le_p(&out->count, record_count);
3351 if (total_count > record_count) {
3352 out->flags = (1 << 0); /* More Media Error Records */
3353 }
3354
3355 *len_out = out_pl_len;
3356 return CXL_MBOX_SUCCESS;
3357 }
3358
3359 /*
3360 * CXL r3.1 section 8.2.9.9.9.1: Get Dynamic Capacity Configuration
3361 * (Opcode: 4800h)
3362 */
3363 static CXLRetCode cmd_dcd_get_dyn_cap_config(const struct cxl_cmd *cmd,
3364 uint8_t *payload_in,
3365 size_t len_in,
3366 uint8_t *payload_out,
3367 size_t *len_out,
3368 CXLCCI *cci)
3369 {
3370 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
3371 struct {
3372 uint8_t region_cnt;
3373 uint8_t start_rid;
3374 } QEMU_PACKED *in = (void *)payload_in;
3375 struct {
3376 uint8_t num_regions;
3377 uint8_t regions_returned;
3378 uint8_t rsvd1[6];
3379 struct {
3380 uint64_t base;
3381 uint64_t decode_len;
3382 uint64_t region_len;
3383 uint64_t block_size;
3384 uint32_t dsmadhandle;
3385 uint8_t flags;
3386 uint8_t rsvd2[3];
3387 } QEMU_PACKED records[];
3388 } QEMU_PACKED *out = (void *)payload_out;
3389 struct {
3390 uint32_t num_extents_supported;
3391 uint32_t num_extents_available;
3392 uint32_t num_tags_supported;
3393 uint32_t num_tags_available;
3394 } QEMU_PACKED *extra_out;
3395 uint16_t record_count;
3396 uint16_t i;
3397 uint16_t out_pl_len;
3398 uint8_t start_rid;
3399
3400 start_rid = in->start_rid;
3401 if (start_rid >= ct3d->dc.num_regions) {
3402 return CXL_MBOX_INVALID_INPUT;
3403 }
3404
3405 record_count = MIN(ct3d->dc.num_regions - in->start_rid, in->region_cnt);
3406
3407 out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]);
3408 extra_out = (void *)(payload_out + out_pl_len);
3409 out_pl_len += sizeof(*extra_out);
3410 assert(out_pl_len <= CXL_MAILBOX_MAX_PAYLOAD_SIZE);
3411
3412 out->num_regions = ct3d->dc.num_regions;
3413 out->regions_returned = record_count;
3414 for (i = 0; i < record_count; i++) {
3415 stq_le_p(&out->records[i].base,
3416 ct3d->dc.regions[start_rid + i].base);
3417 stq_le_p(&out->records[i].decode_len,
3418 ct3d->dc.regions[start_rid + i].decode_len /
3419 CXL_CAPACITY_MULTIPLIER);
3420 stq_le_p(&out->records[i].region_len,
3421 ct3d->dc.regions[start_rid + i].len);
3422 stq_le_p(&out->records[i].block_size,
3423 ct3d->dc.regions[start_rid + i].block_size);
3424 stl_le_p(&out->records[i].dsmadhandle,
3425 ct3d->dc.regions[start_rid + i].dsmadhandle);
3426 out->records[i].flags = ct3d->dc.regions[start_rid + i].flags;
3427 }
3428 /*
3429 * TODO: Assign values once extents and tags are introduced
3430 * to use.
3431 */
3432 stl_le_p(&extra_out->num_extents_supported, CXL_NUM_EXTENTS_SUPPORTED);
3433 stl_le_p(&extra_out->num_extents_available, CXL_NUM_EXTENTS_SUPPORTED -
3434 ct3d->dc.total_extent_count);
3435 stl_le_p(&extra_out->num_tags_supported, CXL_NUM_TAGS_SUPPORTED);
3436 stl_le_p(&extra_out->num_tags_available, CXL_NUM_TAGS_SUPPORTED);
3437
3438 *len_out = out_pl_len;
3439 return CXL_MBOX_SUCCESS;
3440 }
3441
3442 /*
3443 * CXL r3.1 section 8.2.9.9.9.2:
3444 * Get Dynamic Capacity Extent List (Opcode 4801h)
3445 */
3446 static CXLRetCode cmd_dcd_get_dyn_cap_ext_list(const struct cxl_cmd *cmd,
3447 uint8_t *payload_in,
3448 size_t len_in,
3449 uint8_t *payload_out,
3450 size_t *len_out,
3451 CXLCCI *cci)
3452 {
3453 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
3454 struct {
3455 uint32_t extent_cnt;
3456 uint32_t start_extent_id;
3457 } QEMU_PACKED *in = (void *)payload_in;
3458 struct {
3459 uint32_t count;
3460 uint32_t total_extents;
3461 uint32_t generation_num;
3462 uint8_t rsvd[4];
3463 CXLDCExtentRaw records[];
3464 } QEMU_PACKED *out = (void *)payload_out;
3465 uint32_t start_extent_id = in->start_extent_id;
3466 CXLDCExtentList *extent_list = &ct3d->dc.extents;
3467 uint16_t record_count = 0, i = 0, record_done = 0;
3468 uint16_t out_pl_len, size;
3469 CXLDCExtent *ent;
3470
3471 if (start_extent_id > ct3d->dc.nr_extents_accepted) {
3472 return CXL_MBOX_INVALID_INPUT;
3473 }
3474
3475 record_count = MIN(in->extent_cnt,
3476 ct3d->dc.total_extent_count - start_extent_id);
3477 size = CXL_MAILBOX_MAX_PAYLOAD_SIZE - sizeof(*out);
3478 record_count = MIN(record_count, size / sizeof(out->records[0]));
3479 out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]);
3480
3481 stl_le_p(&out->count, record_count);
3482 stl_le_p(&out->total_extents, ct3d->dc.nr_extents_accepted);
3483 stl_le_p(&out->generation_num, ct3d->dc.ext_list_gen_seq);
3484
3485 if (record_count > 0) {
3486 CXLDCExtentRaw *out_rec = &out->records[record_done];
3487
3488 QTAILQ_FOREACH(ent, extent_list, node) {
3489 if (i++ < start_extent_id) {
3490 continue;
3491 }
3492 stq_le_p(&out_rec->start_dpa, ent->start_dpa);
3493 stq_le_p(&out_rec->len, ent->len);
3494 memcpy(&out_rec->tag, ent->tag, 0x10);
3495 stw_le_p(&out_rec->shared_seq, ent->shared_seq);
3496
3497 record_done++;
3498 out_rec++;
3499 if (record_done == record_count) {
3500 break;
3501 }
3502 }
3503 }
3504
3505 *len_out = out_pl_len;
3506 return CXL_MBOX_SUCCESS;
3507 }
3508
3509 /*
3510 * Check whether any bit between addr[nr, nr+size) is set,
3511 * return true if any bit is set, otherwise return false
3512 */
3513 bool test_any_bits_set(const unsigned long *addr, unsigned long nr,
3514 unsigned long size)
3515 {
3516 unsigned long res = find_next_bit(addr, size + nr, nr);
3517
3518 return res < nr + size;
3519 }
3520
3521 CXLDCRegion *cxl_find_dc_region(CXLType3Dev *ct3d, uint64_t dpa, uint64_t len)
3522 {
3523 int i;
3524 CXLDCRegion *region = &ct3d->dc.regions[0];
3525
3526 if (dpa < region->base ||
3527 dpa >= region->base + ct3d->dc.total_capacity) {
3528 return NULL;
3529 }
3530
3531 /*
3532 * CXL r3.1 section 9.13.3: Dynamic Capacity Device (DCD)
3533 *
3534 * Regions are used in increasing-DPA order, with Region 0 being used for
3535 * the lowest DPA of Dynamic Capacity and Region 7 for the highest DPA.
3536 * So check from the last region to find where the dpa belongs. Extents that
3537 * cross multiple regions are not allowed.
3538 */
3539 for (i = ct3d->dc.num_regions - 1; i >= 0; i--) {
3540 region = &ct3d->dc.regions[i];
3541 if (dpa >= region->base) {
3542 if (dpa + len > region->base + region->len) {
3543 return NULL;
3544 }
3545 return region;
3546 }
3547 }
3548
3549 return NULL;
3550 }
3551
3552 void cxl_insert_extent_to_extent_list(CXLDCExtentList *list,
3553 uint64_t dpa,
3554 uint64_t len,
3555 uint8_t *tag,
3556 uint16_t shared_seq)
3557 {
3558 CXLDCExtent *extent;
3559
3560 extent = g_new0(CXLDCExtent, 1);
3561 extent->start_dpa = dpa;
3562 extent->len = len;
3563 if (tag) {
3564 memcpy(extent->tag, tag, 0x10);
3565 }
3566 extent->shared_seq = shared_seq;
3567
3568 QTAILQ_INSERT_TAIL(list, extent, node);
3569 }
3570
3571 void cxl_remove_extent_from_extent_list(CXLDCExtentList *list,
3572 CXLDCExtent *extent)
3573 {
3574 QTAILQ_REMOVE(list, extent, node);
3575 g_free(extent);
3576 }
3577
3578 /*
3579 * Add a new extent to the extent "group" if group exists;
3580 * otherwise, create a new group
3581 * Return value: the extent group where the extent is inserted.
3582 */
3583 CXLDCExtentGroup *cxl_insert_extent_to_extent_group(CXLDCExtentGroup *group,
3584 uint64_t dpa,
3585 uint64_t len,
3586 uint8_t *tag,
3587 uint16_t shared_seq)
3588 {
3589 if (!group) {
3590 group = g_new0(CXLDCExtentGroup, 1);
3591 QTAILQ_INIT(&group->list);
3592 }
3593 cxl_insert_extent_to_extent_list(&group->list, dpa, len,
3594 tag, shared_seq);
3595 return group;
3596 }
3597
3598 void cxl_extent_group_list_insert_tail(CXLDCExtentGroupList *list,
3599 CXLDCExtentGroup *group)
3600 {
3601 QTAILQ_INSERT_TAIL(list, group, node);
3602 }
3603
3604 uint32_t cxl_extent_group_list_delete_front(CXLDCExtentGroupList *list)
3605 {
3606 CXLDCExtent *ent, *ent_next;
3607 CXLDCExtentGroup *group = QTAILQ_FIRST(list);
3608 uint32_t extents_deleted = 0;
3609
3610 QTAILQ_REMOVE(list, group, node);
3611 QTAILQ_FOREACH_SAFE(ent, &group->list, node, ent_next) {
3612 cxl_remove_extent_from_extent_list(&group->list, ent);
3613 extents_deleted++;
3614 }
3615 g_free(group);
3616
3617 return extents_deleted;
3618 }
3619
3620 /*
3621 * CXL r3.1 Table 8-168: Add Dynamic Capacity Response Input Payload
3622 * CXL r3.1 Table 8-170: Release Dynamic Capacity Input Payload
3623 */
3624 typedef struct CXLUpdateDCExtentListInPl {
3625 uint32_t num_entries_updated;
3626 uint8_t flags;
3627 uint8_t rsvd[3];
3628 /* CXL r3.1 Table 8-169: Updated Extent */
3629 struct {
3630 uint64_t start_dpa;
3631 uint64_t len;
3632 uint8_t rsvd[8];
3633 } QEMU_PACKED updated_entries[];
3634 } QEMU_PACKED CXLUpdateDCExtentListInPl;
3635
3636 /*
3637 * For the extents in the extent list to operate, check whether they are valid
3638 * 1. The extent should be in the range of a valid DC region;
3639 * 2. The extent should not cross multiple regions;
3640 * 3. The start DPA and the length of the extent should align with the block
3641 * size of the region;
3642 * 4. The address range of multiple extents in the list should not overlap.
3643 */
3644 static CXLRetCode cxl_detect_malformed_extent_list(CXLType3Dev *ct3d,
3645 const CXLUpdateDCExtentListInPl *in)
3646 {
3647 uint64_t min_block_size = UINT64_MAX;
3648 CXLDCRegion *region;
3649 CXLDCRegion *lastregion = &ct3d->dc.regions[ct3d->dc.num_regions - 1];
3650 g_autofree unsigned long *blk_bitmap = NULL;
3651 uint64_t dpa, len;
3652 uint32_t i;
3653
3654 for (i = 0; i < ct3d->dc.num_regions; i++) {
3655 region = &ct3d->dc.regions[i];
3656 min_block_size = MIN(min_block_size, region->block_size);
3657 }
3658
3659 blk_bitmap = bitmap_new((lastregion->base + lastregion->len -
3660 ct3d->dc.regions[0].base) / min_block_size);
3661
3662 for (i = 0; i < in->num_entries_updated; i++) {
3663 dpa = in->updated_entries[i].start_dpa;
3664 len = in->updated_entries[i].len;
3665
3666 region = cxl_find_dc_region(ct3d, dpa, len);
3667 if (!region) {
3668 return CXL_MBOX_INVALID_PA;
3669 }
3670
3671 dpa -= ct3d->dc.regions[0].base;
3672 if (dpa % region->block_size || len % region->block_size) {
3673 return CXL_MBOX_INVALID_EXTENT_LIST;
3674 }
3675 /* the dpa range already covered by some other extents in the list */
3676 if (test_any_bits_set(blk_bitmap, dpa / min_block_size,
3677 len / min_block_size)) {
3678 return CXL_MBOX_INVALID_EXTENT_LIST;
3679 }
3680 bitmap_set(blk_bitmap, dpa / min_block_size, len / min_block_size);
3681 }
3682
3683 return CXL_MBOX_SUCCESS;
3684 }
3685
3686 static CXLRetCode cxl_dcd_add_dyn_cap_rsp_dry_run(CXLType3Dev *ct3d,
3687 const CXLUpdateDCExtentListInPl *in)
3688 {
3689 uint32_t i;
3690 CXLDCExtent *ent;
3691 CXLDCExtentGroup *ext_group;
3692 uint64_t dpa, len;
3693 Range range1, range2;
3694
3695 for (i = 0; i < in->num_entries_updated; i++) {
3696 dpa = in->updated_entries[i].start_dpa;
3697 len = in->updated_entries[i].len;
3698
3699 range_init_nofail(&range1, dpa, len);
3700
3701 /*
3702 * The host-accepted DPA range must be contained by the first extent
3703 * group in the pending list
3704 */
3705 ext_group = QTAILQ_FIRST(&ct3d->dc.extents_pending);
3706 if (!cxl_extents_contains_dpa_range(&ext_group->list, dpa, len)) {
3707 return CXL_MBOX_INVALID_PA;
3708 }
3709
3710 /* to-be-added range should not overlap with range already accepted */
3711 QTAILQ_FOREACH(ent, &ct3d->dc.extents, node) {
3712 range_init_nofail(&range2, ent->start_dpa, ent->len);
3713 if (range_overlaps_range(&range1, &range2)) {
3714 return CXL_MBOX_INVALID_PA;
3715 }
3716 }
3717 }
3718 return CXL_MBOX_SUCCESS;
3719 }
3720
3721 /*
3722 * CXL r3.1 section 8.2.9.9.9.3: Add Dynamic Capacity Response (Opcode 4802h)
3723 * An extent is added to the extent list and becomes usable only after the
3724 * response is processed successfully.
3725 */
3726 static CXLRetCode cmd_dcd_add_dyn_cap_rsp(const struct cxl_cmd *cmd,
3727 uint8_t *payload_in,
3728 size_t len_in,
3729 uint8_t *payload_out,
3730 size_t *len_out,
3731 CXLCCI *cci)
3732 {
3733 CXLUpdateDCExtentListInPl *in = (void *)payload_in;
3734 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
3735 CXLDCExtentList *extent_list = &ct3d->dc.extents;
3736 uint32_t i, num;
3737 uint64_t dpa, len;
3738 CXLRetCode ret;
3739
3740 if (len_in < sizeof(*in)) {
3741 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
3742 }
3743
3744 if (in->num_entries_updated == 0) {
3745 num = cxl_extent_group_list_delete_front(&ct3d->dc.extents_pending);
3746 ct3d->dc.total_extent_count -= num;
3747 return CXL_MBOX_SUCCESS;
3748 }
3749
3750 if (len_in <
3751 sizeof(*in) + sizeof(*in->updated_entries) * in->num_entries_updated) {
3752 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
3753 }
3754
3755 /* Adding extents causes exceeding device's extent tracking ability. */
3756 if (in->num_entries_updated + ct3d->dc.total_extent_count >
3757 CXL_NUM_EXTENTS_SUPPORTED) {
3758 return CXL_MBOX_RESOURCES_EXHAUSTED;
3759 }
3760
3761 ret = cxl_detect_malformed_extent_list(ct3d, in);
3762 if (ret != CXL_MBOX_SUCCESS) {
3763 return ret;
3764 }
3765
3766 ret = cxl_dcd_add_dyn_cap_rsp_dry_run(ct3d, in);
3767 if (ret != CXL_MBOX_SUCCESS) {
3768 return ret;
3769 }
3770
3771 for (i = 0; i < in->num_entries_updated; i++) {
3772 dpa = in->updated_entries[i].start_dpa;
3773 len = in->updated_entries[i].len;
3774
3775 cxl_insert_extent_to_extent_list(extent_list, dpa, len, NULL, 0);
3776 ct3d->dc.total_extent_count += 1;
3777 ct3d->dc.nr_extents_accepted += 1;
3778 ct3_set_region_block_backed(ct3d, dpa, len);
3779 }
3780 /* Remove the first extent group in the pending list */
3781 num = cxl_extent_group_list_delete_front(&ct3d->dc.extents_pending);
3782 ct3d->dc.total_extent_count -= num;
3783
3784 return CXL_MBOX_SUCCESS;
3785 }
3786
3787 /*
3788 * Copy extent list from src to dst
3789 * Return value: number of extents copied
3790 */
3791 static uint32_t copy_extent_list(CXLDCExtentList *dst,
3792 const CXLDCExtentList *src)
3793 {
3794 uint32_t cnt = 0;
3795 CXLDCExtent *ent;
3796
3797 if (!dst || !src) {
3798 return 0;
3799 }
3800
3801 QTAILQ_FOREACH(ent, src, node) {
3802 cxl_insert_extent_to_extent_list(dst, ent->start_dpa, ent->len,
3803 ent->tag, ent->shared_seq);
3804 cnt++;
3805 }
3806 return cnt;
3807 }
3808
3809 static CXLRetCode cxl_dc_extent_release_dry_run(CXLType3Dev *ct3d,
3810 const CXLUpdateDCExtentListInPl *in, CXLDCExtentList *updated_list,
3811 uint32_t *updated_list_size)
3812 {
3813 CXLDCExtent *ent, *ent_next;
3814 uint64_t dpa, len;
3815 uint32_t i;
3816 int cnt_delta = 0;
3817 CXLRetCode ret = CXL_MBOX_SUCCESS;
3818
3819 QTAILQ_INIT(updated_list);
3820 copy_extent_list(updated_list, &ct3d->dc.extents);
3821
3822 for (i = 0; i < in->num_entries_updated; i++) {
3823 Range range;
3824
3825 dpa = in->updated_entries[i].start_dpa;
3826 len = in->updated_entries[i].len;
3827
3828 /* Check if the DPA range is not fully backed with valid extents */
3829 if (!ct3_test_region_block_backed(ct3d, dpa, len)) {
3830 ret = CXL_MBOX_INVALID_PA;
3831 goto free_and_exit;
3832 }
3833
3834 /* After this point, extent overflow is the only error can happen */
3835 while (len > 0) {
3836 QTAILQ_FOREACH(ent, updated_list, node) {
3837 range_init_nofail(&range, ent->start_dpa, ent->len);
3838
3839 if (range_contains(&range, dpa)) {
3840 uint64_t len1, len2 = 0, len_done = 0;
3841 uint64_t ent_start_dpa = ent->start_dpa;
3842 uint64_t ent_len = ent->len;
3843
3844 len1 = dpa - ent->start_dpa;
3845 /* Found the extent or the subset of an existing extent */
3846 if (range_contains(&range, dpa + len - 1)) {
3847 len2 = ent_start_dpa + ent_len - dpa - len;
3848 } else {
3849 dpa = ent_start_dpa + ent_len;
3850 }
3851 len_done = ent_len - len1 - len2;
3852
3853 cxl_remove_extent_from_extent_list(updated_list, ent);
3854 cnt_delta--;
3855
3856 if (len1) {
3857 cxl_insert_extent_to_extent_list(updated_list,
3858 ent_start_dpa,
3859 len1, NULL, 0);
3860 cnt_delta++;
3861 }
3862 if (len2) {
3863 cxl_insert_extent_to_extent_list(updated_list,
3864 dpa + len,
3865 len2, NULL, 0);
3866 cnt_delta++;
3867 }
3868
3869 if (cnt_delta + ct3d->dc.total_extent_count >
3870 CXL_NUM_EXTENTS_SUPPORTED) {
3871 ret = CXL_MBOX_RESOURCES_EXHAUSTED;
3872 goto free_and_exit;
3873 }
3874
3875 len -= len_done;
3876 break;
3877 }
3878 }
3879 }
3880 }
3881 free_and_exit:
3882 if (ret != CXL_MBOX_SUCCESS) {
3883 QTAILQ_FOREACH_SAFE(ent, updated_list, node, ent_next) {
3884 cxl_remove_extent_from_extent_list(updated_list, ent);
3885 }
3886 *updated_list_size = 0;
3887 } else {
3888 *updated_list_size = ct3d->dc.nr_extents_accepted + cnt_delta;
3889 }
3890
3891 return ret;
3892 }
3893
3894 /*
3895 * CXL r3.1 section 8.2.9.9.9.4: Release Dynamic Capacity (Opcode 4803h)
3896 */
3897 static CXLRetCode cmd_dcd_release_dyn_cap(const struct cxl_cmd *cmd,
3898 uint8_t *payload_in,
3899 size_t len_in,
3900 uint8_t *payload_out,
3901 size_t *len_out,
3902 CXLCCI *cci)
3903 {
3904 CXLUpdateDCExtentListInPl *in = (void *)payload_in;
3905 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
3906 CXLDCExtentList updated_list;
3907 CXLDCExtent *ent, *ent_next;
3908 uint32_t updated_list_size;
3909 CXLRetCode ret;
3910
3911 if (len_in < sizeof(*in)) {
3912 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
3913 }
3914
3915 if (in->num_entries_updated == 0) {
3916 return CXL_MBOX_INVALID_INPUT;
3917 }
3918
3919 if (len_in <
3920 sizeof(*in) + sizeof(*in->updated_entries) * in->num_entries_updated) {
3921 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
3922 }
3923
3924 ret = cxl_detect_malformed_extent_list(ct3d, in);
3925 if (ret != CXL_MBOX_SUCCESS) {
3926 return ret;
3927 }
3928
3929 ret = cxl_dc_extent_release_dry_run(ct3d, in, &updated_list,
3930 &updated_list_size);
3931 if (ret != CXL_MBOX_SUCCESS) {
3932 return ret;
3933 }
3934
3935 /*
3936 * If the dry run release passes, the returned updated_list will
3937 * be the updated extent list and we just need to clear the extents
3938 * in the accepted list and copy extents in the updated_list to accepted
3939 * list and update the extent count;
3940 */
3941 QTAILQ_FOREACH_SAFE(ent, &ct3d->dc.extents, node, ent_next) {
3942 ct3_clear_region_block_backed(ct3d, ent->start_dpa, ent->len);
3943 cxl_remove_extent_from_extent_list(&ct3d->dc.extents, ent);
3944 }
3945 copy_extent_list(&ct3d->dc.extents, &updated_list);
3946 QTAILQ_FOREACH_SAFE(ent, &updated_list, node, ent_next) {
3947 ct3_set_region_block_backed(ct3d, ent->start_dpa, ent->len);
3948 cxl_remove_extent_from_extent_list(&updated_list, ent);
3949 }
3950 ct3d->dc.total_extent_count += (updated_list_size -
3951 ct3d->dc.nr_extents_accepted);
3952
3953 ct3d->dc.nr_extents_accepted = updated_list_size;
3954
3955 return CXL_MBOX_SUCCESS;
3956 }
3957
3958 /* CXL r3.2 section 7.6.7.6.1: Get DCD Info (Opcode 5600h) */
3959 static CXLRetCode cmd_fm_get_dcd_info(const struct cxl_cmd *cmd,
3960 uint8_t *payload_in,
3961 size_t len_in,
3962 uint8_t *payload_out,
3963 size_t *len_out,
3964 CXLCCI *cci)
3965 {
3966 struct {
3967 uint8_t num_hosts;
3968 uint8_t num_regions_supported;
3969 uint8_t rsvd1[2];
3970 uint16_t supported_add_sel_policy_bitmask;
3971 uint8_t rsvd2[2];
3972 uint16_t supported_removal_policy_bitmask;
3973 uint8_t sanitize_on_release_bitmask;
3974 uint8_t rsvd3;
3975 uint64_t total_dynamic_capacity;
3976 uint64_t region_blk_size_bitmasks[8];
3977 } QEMU_PACKED *out = (void *)payload_out;
3978 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
3979 CXLDCRegion *region;
3980 int i;
3981
3982 out->num_hosts = 1;
3983 out->num_regions_supported = ct3d->dc.num_regions;
3984 stw_le_p(&out->supported_add_sel_policy_bitmask,
3985 BIT(CXL_EXTENT_SELECTION_POLICY_PRESCRIPTIVE));
3986 stw_le_p(&out->supported_removal_policy_bitmask,
3987 BIT(CXL_EXTENT_REMOVAL_POLICY_PRESCRIPTIVE));
3988 out->sanitize_on_release_bitmask = 0;
3989
3990 stq_le_p(&out->total_dynamic_capacity,
3991 ct3d->dc.total_capacity / CXL_CAPACITY_MULTIPLIER);
3992
3993 for (i = 0; i < ct3d->dc.num_regions; i++) {
3994 region = &ct3d->dc.regions[i];
3995 memcpy(&out->region_blk_size_bitmasks[i],
3996 &region->supported_blk_size_bitmask,
3997 sizeof(out->region_blk_size_bitmasks[i]));
3998 }
3999
4000 *len_out = sizeof(*out);
4001 return CXL_MBOX_SUCCESS;
4002 }
4003
4004 static void build_dsmas_flags(uint8_t *flags, CXLDCRegion *region)
4005 {
4006 *flags = 0;
4007
4008 if (region->nonvolatile) {
4009 *flags |= BIT(CXL_DSMAS_FLAGS_NONVOLATILE);
4010 }
4011 if (region->sharable) {
4012 *flags |= BIT(CXL_DSMAS_FLAGS_SHARABLE);
4013 }
4014 if (region->hw_managed_coherency) {
4015 *flags |= BIT(CXL_DSMAS_FLAGS_HW_MANAGED_COHERENCY);
4016 }
4017 if (region->ic_specific_dc_management) {
4018 *flags |= BIT(CXL_DSMAS_FLAGS_IC_SPECIFIC_DC_MANAGEMENT);
4019 }
4020 if (region->rdonly) {
4021 *flags |= BIT(CXL_DSMAS_FLAGS_RDONLY);
4022 }
4023 }
4024
4025 /*
4026 * CXL r3.2 section 7.6.7.6.2:
4027 * Get Host DC Region Configuration (Opcode 5601h)
4028 */
4029 static CXLRetCode cmd_fm_get_host_dc_region_config(const struct cxl_cmd *cmd,
4030 uint8_t *payload_in,
4031 size_t len_in,
4032 uint8_t *payload_out,
4033 size_t *len_out,
4034 CXLCCI *cci)
4035 {
4036 struct {
4037 uint16_t host_id;
4038 uint8_t region_cnt;
4039 uint8_t start_rid;
4040 } QEMU_PACKED *in = (void *)payload_in;
4041 struct {
4042 uint16_t host_id;
4043 uint8_t num_regions;
4044 uint8_t regions_returned;
4045 struct {
4046 uint64_t base;
4047 uint64_t decode_len;
4048 uint64_t region_len;
4049 uint64_t block_size;
4050 uint8_t flags;
4051 uint8_t rsvd1[3];
4052 uint8_t sanitize;
4053 uint8_t rsvd2[3];
4054 } QEMU_PACKED records[];
4055 } QEMU_PACKED *out = (void *)payload_out;
4056 struct {
4057 uint32_t num_extents_supported;
4058 uint32_t num_extents_available;
4059 uint32_t num_tags_supported;
4060 uint32_t num_tags_available;
4061 } QEMU_PACKED *extra_out;
4062 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
4063 uint16_t record_count, out_pl_len, i;
4064
4065 if (in->start_rid >= ct3d->dc.num_regions) {
4066 return CXL_MBOX_INVALID_INPUT;
4067 }
4068 record_count = MIN(ct3d->dc.num_regions - in->start_rid, in->region_cnt);
4069
4070 out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]);
4071 extra_out = (void *)out + out_pl_len;
4072 out_pl_len += sizeof(*extra_out);
4073
4074 assert(out_pl_len <= CXL_MAILBOX_MAX_PAYLOAD_SIZE);
4075
4076 stw_le_p(&out->host_id, 0);
4077 out->num_regions = ct3d->dc.num_regions;
4078 out->regions_returned = record_count;
4079
4080 for (i = 0; i < record_count; i++) {
4081 stq_le_p(&out->records[i].base,
4082 ct3d->dc.regions[in->start_rid + i].base);
4083 stq_le_p(&out->records[i].decode_len,
4084 ct3d->dc.regions[in->start_rid + i].decode_len /
4085 CXL_CAPACITY_MULTIPLIER);
4086 stq_le_p(&out->records[i].region_len,
4087 ct3d->dc.regions[in->start_rid + i].len);
4088 stq_le_p(&out->records[i].block_size,
4089 ct3d->dc.regions[in->start_rid + i].block_size);
4090 build_dsmas_flags(&out->records[i].flags,
4091 &ct3d->dc.regions[in->start_rid + i]);
4092 /* Sanitize is bit 0 of flags. */
4093 out->records[i].sanitize =
4094 ct3d->dc.regions[in->start_rid + i].flags & BIT(0);
4095 }
4096
4097 stl_le_p(&extra_out->num_extents_supported, CXL_NUM_EXTENTS_SUPPORTED);
4098 stl_le_p(&extra_out->num_extents_available, CXL_NUM_EXTENTS_SUPPORTED -
4099 ct3d->dc.total_extent_count);
4100 stl_le_p(&extra_out->num_tags_supported, CXL_NUM_TAGS_SUPPORTED);
4101 stl_le_p(&extra_out->num_tags_available, CXL_NUM_TAGS_SUPPORTED);
4102
4103 *len_out = out_pl_len;
4104 return CXL_MBOX_SUCCESS;
4105 }
4106
4107 /* CXL r3.2 section 7.6.7.6.3: Set Host DC Region Configuration (Opcode 5602) */
4108 static CXLRetCode cmd_fm_set_dc_region_config(const struct cxl_cmd *cmd,
4109 uint8_t *payload_in,
4110 size_t len_in,
4111 uint8_t *payload_out,
4112 size_t *len_out,
4113 CXLCCI *cci)
4114 {
4115 struct {
4116 uint8_t reg_id;
4117 uint8_t rsvd[3];
4118 uint64_t block_sz;
4119 uint8_t flags;
4120 uint8_t rsvd2[3];
4121 } QEMU_PACKED *in = (void *)payload_in;
4122 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
4123 CXLEventDynamicCapacity dcEvent = {};
4124 CXLDCRegion *region = &ct3d->dc.regions[in->reg_id];
4125
4126 /*
4127 * CXL r3.2 7.6.7.6.3: Set DC Region Configuration
4128 * This command shall fail with Unsupported when the Sanitize on Release
4129 * field does not match the region’s configuration... and the device
4130 * does not support reconfiguration of the Sanitize on Release setting.
4131 *
4132 * Currently not reconfigurable, so always fail if sanitize bit (bit 0)
4133 * doesn't match.
4134 */
4135 if ((in->flags & 0x1) != (region->flags & 0x1)) {
4136 return CXL_MBOX_UNSUPPORTED;
4137 }
4138
4139 if (in->reg_id >= DCD_MAX_NUM_REGION) {
4140 return CXL_MBOX_UNSUPPORTED;
4141 }
4142
4143 /* Check that no extents are in the region being reconfigured */
4144 if (!bitmap_empty(region->blk_bitmap, region->len / region->block_size)) {
4145 return CXL_MBOX_UNSUPPORTED;
4146 }
4147
4148 /* Check that new block size is supported */
4149 if (!is_power_of_2(in->block_sz) ||
4150 !(in->block_sz & region->supported_blk_size_bitmask)) {
4151 return CXL_MBOX_INVALID_INPUT;
4152 }
4153
4154 /* Return success if new block size == current block size */
4155 if (in->block_sz == region->block_size) {
4156 return CXL_MBOX_SUCCESS;
4157 }
4158
4159 /* Free bitmap and create new one for new block size. */
4160 qemu_mutex_lock(&region->bitmap_lock);
4161 g_free(region->blk_bitmap);
4162 region->blk_bitmap = bitmap_new(region->len / in->block_sz);
4163 qemu_mutex_unlock(&region->bitmap_lock);
4164 region->block_size = in->block_sz;
4165
4166 /* Create event record and insert into event log */
4167 cxl_assign_event_header(&dcEvent.hdr,
4168 &dynamic_capacity_uuid,
4169 (1 << CXL_EVENT_TYPE_INFO),
4170 sizeof(dcEvent),
4171 cxl_device_get_timestamp(&ct3d->cxl_dstate),
4172 0, 0, 0, 0, 0, 0, 0, 0);
4173 dcEvent.type = DC_EVENT_REGION_CONFIG_UPDATED;
4174 dcEvent.validity_flags = 1;
4175 dcEvent.host_id = 0;
4176 dcEvent.updated_region_id = in->reg_id;
4177
4178 if (cxl_event_insert(&ct3d->cxl_dstate,
4179 CXL_EVENT_TYPE_DYNAMIC_CAP,
4180 (CXLEventRecordRaw *)&dcEvent)) {
4181 cxl_event_irq_assert(ct3d);
4182 }
4183 return CXL_MBOX_SUCCESS;
4184 }
4185
4186 /* CXL r3.2 section 7.6.7.6.4: Get DC Region Extent Lists (Opcode 5603h) */
4187 static CXLRetCode cmd_fm_get_dc_region_extent_list(const struct cxl_cmd *cmd,
4188 uint8_t *payload_in,
4189 size_t len_in,
4190 uint8_t *payload_out,
4191 size_t *len_out,
4192 CXLCCI *cci)
4193 {
4194 struct {
4195 uint16_t host_id;
4196 uint8_t rsvd[2];
4197 uint32_t extent_cnt;
4198 uint32_t start_extent_id;
4199 } QEMU_PACKED *in = (void *)payload_in;
4200 struct {
4201 uint16_t host_id;
4202 uint8_t rsvd[2];
4203 uint32_t start_extent_id;
4204 uint32_t extents_returned;
4205 uint32_t total_extents;
4206 uint32_t list_generation_num;
4207 uint8_t rsvd2[4];
4208 CXLDCExtentRaw records[];
4209 } QEMU_PACKED *out = (void *)payload_out;
4210 QEMU_BUILD_BUG_ON(sizeof(*in) != 0xc);
4211 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
4212 CXLDCExtent *ent;
4213 CXLDCExtentRaw *out_rec;
4214 uint16_t record_count = 0, record_done = 0, i = 0;
4215 uint16_t out_pl_len, max_size;
4216
4217 if (in->host_id != 0) {
4218 return CXL_MBOX_INVALID_INPUT;
4219 }
4220
4221 if (in->start_extent_id > ct3d->dc.nr_extents_accepted) {
4222 return CXL_MBOX_INVALID_INPUT;
4223 }
4224
4225 record_count = MIN(in->extent_cnt,
4226 ct3d->dc.nr_extents_accepted - in->start_extent_id);
4227 max_size = CXL_MAILBOX_MAX_PAYLOAD_SIZE - sizeof(*out);
4228 record_count = MIN(record_count, max_size / sizeof(out->records[0]));
4229 out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]);
4230
4231 stw_le_p(&out->host_id, in->host_id);
4232 stl_le_p(&out->start_extent_id, in->start_extent_id);
4233 stl_le_p(&out->extents_returned, record_count);
4234 stl_le_p(&out->total_extents, ct3d->dc.nr_extents_accepted);
4235 stl_le_p(&out->list_generation_num, ct3d->dc.ext_list_gen_seq);
4236
4237 if (record_count > 0) {
4238 QTAILQ_FOREACH(ent, &ct3d->dc.extents, node) {
4239 if (i++ < in->start_extent_id) {
4240 continue;
4241 }
4242 out_rec = &out->records[record_done];
4243 stq_le_p(&out_rec->start_dpa, ent->start_dpa);
4244 stq_le_p(&out_rec->len, ent->len);
4245 memcpy(&out_rec->tag, ent->tag, 0x10);
4246 stw_le_p(&out_rec->shared_seq, ent->shared_seq);
4247
4248 record_done++;
4249 if (record_done == record_count) {
4250 break;
4251 }
4252 }
4253 }
4254
4255 *len_out = out_pl_len;
4256 return CXL_MBOX_SUCCESS;
4257 }
4258
4259 /*
4260 * Helper function to convert CXLDCExtentRaw to CXLUpdateDCExtentListInPl
4261 * in order to reuse cxl_detect_malformed_extent_list() function which accepts
4262 * CXLUpdateDCExtentListInPl as a parameter.
4263 */
4264 static void convert_raw_extents(CXLDCExtentRaw raw_extents[],
4265 CXLUpdateDCExtentListInPl *extent_list,
4266 int count)
4267 {
4268 int i;
4269
4270 extent_list->num_entries_updated = count;
4271
4272 for (i = 0; i < count; i++) {
4273 extent_list->updated_entries[i].start_dpa = raw_extents[i].start_dpa;
4274 extent_list->updated_entries[i].len = raw_extents[i].len;
4275 }
4276 }
4277
4278 /* CXL r3.2 Section 7.6.7.6.5: Initiate Dynamic Capacity Add (Opcode 5604h) */
4279 static CXLRetCode cmd_fm_initiate_dc_add(const struct cxl_cmd *cmd,
4280 uint8_t *payload_in,
4281 size_t len_in,
4282 uint8_t *payload_out,
4283 size_t *len_out,
4284 CXLCCI *cci)
4285 {
4286 struct {
4287 uint16_t host_id;
4288 uint8_t selection_policy;
4289 uint8_t reg_num;
4290 uint64_t length;
4291 uint8_t tag[0x10];
4292 uint32_t ext_count;
4293 CXLDCExtentRaw extents[];
4294 } QEMU_PACKED *in = (void *)payload_in;
4295 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
4296 int i, rc;
4297
4298 switch (in->selection_policy) {
4299 case CXL_EXTENT_SELECTION_POLICY_PRESCRIPTIVE: {
4300 /* Adding extents exceeds device's extent tracking ability. */
4301 if (in->ext_count + ct3d->dc.total_extent_count >
4302 CXL_NUM_EXTENTS_SUPPORTED) {
4303 return CXL_MBOX_RESOURCES_EXHAUSTED;
4304 }
4305
4306 g_autofree CXLUpdateDCExtentListInPl *list =
4307 g_malloc0(sizeof(*list) +
4308 in->ext_count * sizeof(*list->updated_entries));
4309
4310 convert_raw_extents(in->extents, list, in->ext_count);
4311 rc = cxl_detect_malformed_extent_list(ct3d, list);
4312
4313 for (i = 0; i < in->ext_count; i++) {
4314 CXLDCExtentRaw *ext = &in->extents[i];
4315
4316 /* Check requested extents do not overlap with pending ones. */
4317 if (cxl_extent_groups_overlaps_dpa_range(&ct3d->dc.extents_pending,
4318 ext->start_dpa,
4319 ext->len)) {
4320 return CXL_MBOX_INVALID_EXTENT_LIST;
4321 }
4322 /* Check requested extents do not overlap with existing ones. */
4323 if (cxl_extents_overlaps_dpa_range(&ct3d->dc.extents,
4324 ext->start_dpa,
4325 ext->len)) {
4326 return CXL_MBOX_INVALID_EXTENT_LIST;
4327 }
4328 }
4329
4330 if (rc) {
4331 return rc;
4332 }
4333
4334 CXLDCExtentGroup *group = NULL;
4335 for (i = 0; i < in->ext_count; i++) {
4336 CXLDCExtentRaw *ext = &in->extents[i];
4337
4338 group = cxl_insert_extent_to_extent_group(group, ext->start_dpa,
4339 ext->len, ext->tag,
4340 ext->shared_seq);
4341 }
4342
4343 cxl_extent_group_list_insert_tail(&ct3d->dc.extents_pending, group);
4344 ct3d->dc.total_extent_count += in->ext_count;
4345 cxl_create_dc_event_records_for_extents(ct3d,
4346 DC_EVENT_ADD_CAPACITY,
4347 in->extents,
4348 in->ext_count);
4349
4350 return CXL_MBOX_SUCCESS;
4351 }
4352 default: {
4353 qemu_log_mask(LOG_UNIMP,
4354 "CXL extent selection policy not supported.\n");
4355 return CXL_MBOX_INVALID_INPUT;
4356 }
4357 }
4358 }
4359
4360 #define CXL_EXTENT_REMOVAL_POLICY_MASK 0x0F
4361 #define CXL_FORCED_REMOVAL_MASK (1 << 4)
4362 /*
4363 * CXL r3.2 Section 7.6.7.6.6:
4364 * Initiate Dynamic Capacity Release (Opcode 5605h)
4365 */
4366 static CXLRetCode cmd_fm_initiate_dc_release(const struct cxl_cmd *cmd,
4367 uint8_t *payload_in,
4368 size_t len_in,
4369 uint8_t *payload_out,
4370 size_t *len_out,
4371 CXLCCI *cci)
4372 {
4373 struct {
4374 uint16_t host_id;
4375 uint8_t flags;
4376 uint8_t reg_num;
4377 uint64_t length;
4378 uint8_t tag[0x10];
4379 uint32_t ext_count;
4380 CXLDCExtentRaw extents[];
4381 } QEMU_PACKED *in = (void *)payload_in;
4382 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
4383 int i, rc;
4384
4385 switch (in->flags & CXL_EXTENT_REMOVAL_POLICY_MASK) {
4386 case CXL_EXTENT_REMOVAL_POLICY_PRESCRIPTIVE: {
4387 CXLDCExtentList updated_list;
4388 uint32_t updated_list_size;
4389 g_autofree CXLUpdateDCExtentListInPl *list =
4390 g_malloc0(sizeof(*list) +
4391 in->ext_count * sizeof(*list->updated_entries));
4392
4393 convert_raw_extents(in->extents, list, in->ext_count);
4394 rc = cxl_detect_malformed_extent_list(ct3d, list);
4395 if (rc) {
4396 return rc;
4397 }
4398
4399 /*
4400 * Fail with Invalid PA if an extent is pending and Forced Removal
4401 * flag not set.
4402 */
4403 if (!(in->flags & CXL_FORCED_REMOVAL_MASK)) {
4404 for (i = 0; i < in->ext_count; i++) {
4405 CXLDCExtentRaw ext = in->extents[i];
4406 /*
4407 * Check requested extents don't overlap with pending
4408 * extents.
4409 */
4410 if (cxl_extent_groups_overlaps_dpa_range(
4411 &ct3d->dc.extents_pending,
4412 ext.start_dpa,
4413 ext.len)) {
4414 return CXL_MBOX_INVALID_PA;
4415 }
4416 }
4417 }
4418
4419 rc = cxl_dc_extent_release_dry_run(ct3d,
4420 list,
4421 &updated_list,
4422 &updated_list_size);
4423 if (rc) {
4424 return rc;
4425 }
4426 cxl_create_dc_event_records_for_extents(ct3d,
4427 DC_EVENT_RELEASE_CAPACITY,
4428 in->extents,
4429 in->ext_count);
4430 return CXL_MBOX_SUCCESS;
4431 }
4432 default: {
4433 qemu_log_mask(LOG_UNIMP,
4434 "CXL extent removal policy not supported.\n");
4435 return CXL_MBOX_INVALID_INPUT;
4436 }
4437 }
4438 }
4439
4440 static const struct cxl_cmd cxl_cmd_set[256][256] = {
4441 [INFOSTAT][BACKGROUND_OPERATION_ABORT] = { "BACKGROUND_OPERATION_ABORT",
4442 cmd_infostat_bg_op_abort, 0, 0 },
4443 [EVENTS][GET_RECORDS] = { "EVENTS_GET_RECORDS",
4444 cmd_events_get_records, 1, 0 },
4445 [EVENTS][CLEAR_RECORDS] = { "EVENTS_CLEAR_RECORDS",
4446 cmd_events_clear_records, ~0, CXL_MBOX_IMMEDIATE_LOG_CHANGE },
4447 [EVENTS][GET_INTERRUPT_POLICY] = { "EVENTS_GET_INTERRUPT_POLICY",
4448 cmd_events_get_interrupt_policy, 0, 0 },
4449 [EVENTS][SET_INTERRUPT_POLICY] = { "EVENTS_SET_INTERRUPT_POLICY",
4450 cmd_events_set_interrupt_policy,
4451 ~0, CXL_MBOX_IMMEDIATE_CONFIG_CHANGE },
4452 [FIRMWARE_UPDATE][GET_INFO] = { "FIRMWARE_UPDATE_GET_INFO",
4453 cmd_firmware_update_get_info, 0, 0 },
4454 [FIRMWARE_UPDATE][TRANSFER] = { "FIRMWARE_UPDATE_TRANSFER",
4455 cmd_firmware_update_transfer, ~0,
4456 CXL_MBOX_BACKGROUND_OPERATION | CXL_MBOX_BACKGROUND_OPERATION_ABORT },
4457 [FIRMWARE_UPDATE][ACTIVATE] = { "FIRMWARE_UPDATE_ACTIVATE",
4458 cmd_firmware_update_activate, 2,
4459 CXL_MBOX_BACKGROUND_OPERATION | CXL_MBOX_BACKGROUND_OPERATION_ABORT },
4460 [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 },
4461 [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set,
4462 8, CXL_MBOX_IMMEDIATE_POLICY_CHANGE },
4463 [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported,
4464 0, 0 },
4465 [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 },
4466 [FEATURES][GET_SUPPORTED] = { "FEATURES_GET_SUPPORTED",
4467 cmd_features_get_supported, 0x8, 0 },
4468 [FEATURES][GET_FEATURE] = { "FEATURES_GET_FEATURE",
4469 cmd_features_get_feature, 0x15, 0 },
4470 [FEATURES][SET_FEATURE] = { "FEATURES_SET_FEATURE",
4471 cmd_features_set_feature,
4472 ~0,
4473 (CXL_MBOX_IMMEDIATE_CONFIG_CHANGE |
4474 CXL_MBOX_IMMEDIATE_DATA_CHANGE |
4475 CXL_MBOX_IMMEDIATE_POLICY_CHANGE |
4476 CXL_MBOX_IMMEDIATE_LOG_CHANGE |
4477 CXL_MBOX_SECURITY_STATE_CHANGE)},
4478 [MAINTENANCE][PERFORM] = { "MAINTENANCE_PERFORM",
4479 cmd_media_perform_maintenance, ~0,
4480 CXL_MBOX_IMMEDIATE_CONFIG_CHANGE |
4481 CXL_MBOX_IMMEDIATE_DATA_CHANGE |
4482 CXL_MBOX_IMMEDIATE_LOG_CHANGE |
4483 CXL_MBOX_BACKGROUND_OPERATION },
4484 [IDENTIFY][MEMORY_DEVICE] = { "IDENTIFY_MEMORY_DEVICE",
4485 cmd_identify_memory_device, 0, 0 },
4486 [CCLS][GET_PARTITION_INFO] = { "CCLS_GET_PARTITION_INFO",
4487 cmd_ccls_get_partition_info, 0, 0 },
4488 [CCLS][GET_LSA] = { "CCLS_GET_LSA", cmd_ccls_get_lsa, 8, 0 },
4489 [CCLS][SET_LSA] = { "CCLS_SET_LSA", cmd_ccls_set_lsa,
4490 ~0, CXL_MBOX_IMMEDIATE_CONFIG_CHANGE | CXL_MBOX_IMMEDIATE_DATA_CHANGE },
4491 [HEALTH_INFO_ALERTS][GET_ALERT_CONFIG] = {
4492 "HEALTH_INFO_ALERTS_GET_ALERT_CONFIG",
4493 cmd_get_alert_config, 0, 0 },
4494 [HEALTH_INFO_ALERTS][SET_ALERT_CONFIG] = {
4495 "HEALTH_INFO_ALERTS_SET_ALERT_CONFIG",
4496 cmd_set_alert_config, 12, CXL_MBOX_IMMEDIATE_POLICY_CHANGE },
4497 [SANITIZE][OVERWRITE] = { "SANITIZE_OVERWRITE", cmd_sanitize_overwrite, 0,
4498 (CXL_MBOX_IMMEDIATE_DATA_CHANGE |
4499 CXL_MBOX_SECURITY_STATE_CHANGE |
4500 CXL_MBOX_BACKGROUND_OPERATION |
4501 CXL_MBOX_BACKGROUND_OPERATION_ABORT)},
4502 [SANITIZE][MEDIA_OPERATIONS] = { "MEDIA_OPERATIONS", cmd_media_operations,
4503 ~0,
4504 (CXL_MBOX_IMMEDIATE_DATA_CHANGE |
4505 CXL_MBOX_BACKGROUND_OPERATION)},
4506 [PERSISTENT_MEM][GET_SECURITY_STATE] = { "GET_SECURITY_STATE",
4507 cmd_get_security_state, 0, 0 },
4508 [MEDIA_AND_POISON][GET_POISON_LIST] = { "MEDIA_AND_POISON_GET_POISON_LIST",
4509 cmd_media_get_poison_list, 16, 0 },
4510 [MEDIA_AND_POISON][INJECT_POISON] = { "MEDIA_AND_POISON_INJECT_POISON",
4511 cmd_media_inject_poison, 8, 0 },
4512 [MEDIA_AND_POISON][CLEAR_POISON] = { "MEDIA_AND_POISON_CLEAR_POISON",
4513 cmd_media_clear_poison, 72, 0 },
4514 [MEDIA_AND_POISON][GET_SCAN_MEDIA_CAPABILITIES] = {
4515 "MEDIA_AND_POISON_GET_SCAN_MEDIA_CAPABILITIES",
4516 cmd_media_get_scan_media_capabilities, 16, 0 },
4517 [MEDIA_AND_POISON][SCAN_MEDIA] = { "MEDIA_AND_POISON_SCAN_MEDIA",
4518 cmd_media_scan_media, 17,
4519 (CXL_MBOX_BACKGROUND_OPERATION | CXL_MBOX_BACKGROUND_OPERATION_ABORT)},
4520 [MEDIA_AND_POISON][GET_SCAN_MEDIA_RESULTS] = {
4521 "MEDIA_AND_POISON_GET_SCAN_MEDIA_RESULTS",
4522 cmd_media_get_scan_media_results, 0, 0 },
4523 };
4524
4525 static const struct cxl_cmd cxl_cmd_set_dcd[256][256] = {
4526 [DCD_CONFIG][GET_DC_CONFIG] = { "DCD_GET_DC_CONFIG",
4527 cmd_dcd_get_dyn_cap_config, 2, 0 },
4528 [DCD_CONFIG][GET_DYN_CAP_EXT_LIST] = {
4529 "DCD_GET_DYNAMIC_CAPACITY_EXTENT_LIST", cmd_dcd_get_dyn_cap_ext_list,
4530 8, 0 },
4531 [DCD_CONFIG][ADD_DYN_CAP_RSP] = {
4532 "DCD_ADD_DYNAMIC_CAPACITY_RESPONSE", cmd_dcd_add_dyn_cap_rsp,
4533 ~0, CXL_MBOX_IMMEDIATE_DATA_CHANGE },
4534 [DCD_CONFIG][RELEASE_DYN_CAP] = {
4535 "DCD_RELEASE_DYNAMIC_CAPACITY", cmd_dcd_release_dyn_cap,
4536 ~0, CXL_MBOX_IMMEDIATE_DATA_CHANGE },
4537 };
4538
4539 static const struct cxl_cmd cxl_cmd_set_sw[256][256] = {
4540 [INFOSTAT][IS_IDENTIFY] = { "IDENTIFY", cmd_infostat_identify, 0, 0 },
4541 [INFOSTAT][BACKGROUND_OPERATION_STATUS] = { "BACKGROUND_OPERATION_STATUS",
4542 cmd_infostat_bg_op_sts, 0, 0 },
4543 [INFOSTAT][BACKGROUND_OPERATION_ABORT] = { "BACKGROUND_OPERATION_ABORT",
4544 cmd_infostat_bg_op_abort, 0, 0 },
4545 [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 },
4546 [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set, 8,
4547 CXL_MBOX_IMMEDIATE_POLICY_CHANGE },
4548 [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0,
4549 0 },
4550 [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 },
4551 [PHYSICAL_SWITCH][IDENTIFY_SWITCH_DEVICE] = { "IDENTIFY_SWITCH_DEVICE",
4552 cmd_identify_switch_device, 0, 0 },
4553 [PHYSICAL_SWITCH][GET_PHYSICAL_PORT_STATE] = { "SWITCH_PHYSICAL_PORT_STATS",
4554 cmd_get_physical_port_state, ~0, 0 },
4555 [PHYSICAL_SWITCH][PHYSICAL_PORT_CONTROL] = { "SWITCH_PHYSICAL_PORT_CONTROL",
4556 cmd_physical_port_control, 2, 0 },
4557 [TUNNEL][MANAGEMENT_COMMAND] = { "TUNNEL_MANAGEMENT_COMMAND",
4558 cmd_tunnel_management_cmd, ~0, 0 },
4559 };
4560
4561 static const struct cxl_cmd cxl_cmd_set_fm_dcd[256][256] = {
4562 [FMAPI_DCD_MGMT][GET_DCD_INFO] = { "GET_DCD_INFO",
4563 cmd_fm_get_dcd_info, 0, 0 },
4564 [FMAPI_DCD_MGMT][GET_HOST_DC_REGION_CONFIG] = { "GET_HOST_DC_REGION_CONFIG",
4565 cmd_fm_get_host_dc_region_config, 4, 0 },
4566 [FMAPI_DCD_MGMT][SET_DC_REGION_CONFIG] = { "SET_DC_REGION_CONFIG",
4567 cmd_fm_set_dc_region_config, 16,
4568 (CXL_MBOX_CONFIG_CHANGE_COLD_RESET |
4569 CXL_MBOX_CONFIG_CHANGE_CONV_RESET |
4570 CXL_MBOX_CONFIG_CHANGE_CXL_RESET |
4571 CXL_MBOX_IMMEDIATE_CONFIG_CHANGE |
4572 CXL_MBOX_IMMEDIATE_DATA_CHANGE) },
4573 [FMAPI_DCD_MGMT][GET_DC_REGION_EXTENT_LIST] = { "GET_DC_REGION_EXTENT_LIST",
4574 cmd_fm_get_dc_region_extent_list, 12, 0 },
4575 [FMAPI_DCD_MGMT][INITIATE_DC_ADD] = { "INIT_DC_ADD",
4576 cmd_fm_initiate_dc_add, ~0,
4577 (CXL_MBOX_CONFIG_CHANGE_COLD_RESET |
4578 CXL_MBOX_CONFIG_CHANGE_CONV_RESET |
4579 CXL_MBOX_CONFIG_CHANGE_CXL_RESET |
4580 CXL_MBOX_IMMEDIATE_CONFIG_CHANGE |
4581 CXL_MBOX_IMMEDIATE_DATA_CHANGE) },
4582 [FMAPI_DCD_MGMT][INITIATE_DC_RELEASE] = { "INIT_DC_RELEASE",
4583 cmd_fm_initiate_dc_release, ~0,
4584 (CXL_MBOX_CONFIG_CHANGE_COLD_RESET |
4585 CXL_MBOX_CONFIG_CHANGE_CONV_RESET |
4586 CXL_MBOX_CONFIG_CHANGE_CXL_RESET |
4587 CXL_MBOX_IMMEDIATE_CONFIG_CHANGE |
4588 CXL_MBOX_IMMEDIATE_DATA_CHANGE) },
4589 };
4590
4591 /*
4592 * While the command is executing in the background, the device should
4593 * update the percentage complete in the Background Command Status Register
4594 * at least once per second.
4595 */
4596
4597 #define CXL_MBOX_BG_UPDATE_FREQ 1000UL
4598
4599 int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd,
4600 size_t len_in, uint8_t *pl_in, size_t *len_out,
4601 uint8_t *pl_out, bool *bg_started)
4602 {
4603 int ret;
4604 const struct cxl_cmd *cxl_cmd;
4605 opcode_handler h;
4606 CXLDeviceState *cxl_dstate;
4607
4608 *len_out = 0;
4609 cxl_cmd = &cci->cxl_cmd_set[set][cmd];
4610 h = cxl_cmd->handler;
4611 if (!h) {
4612 qemu_log_mask(LOG_UNIMP, "Command %04xh not implemented\n",
4613 set << 8 | cmd);
4614 return CXL_MBOX_UNSUPPORTED;
4615 }
4616
4617 if (len_in != cxl_cmd->in && cxl_cmd->in != ~0) {
4618 return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
4619 }
4620
4621 /* Only one bg command at a time */
4622 if ((cxl_cmd->effect & CXL_MBOX_BACKGROUND_OPERATION) &&
4623 cci->bg.runtime > 0) {
4624 return CXL_MBOX_BUSY;
4625 }
4626
4627 /* forbid any selected commands while the media is disabled */
4628 if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) {
4629 cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate;
4630
4631 if (cxl_dev_media_disabled(cxl_dstate)) {
4632 if (h == cmd_events_get_records ||
4633 h == cmd_ccls_get_partition_info ||
4634 h == cmd_ccls_set_lsa ||
4635 h == cmd_ccls_get_lsa ||
4636 h == cmd_logs_get_log ||
4637 h == cmd_media_get_poison_list ||
4638 h == cmd_media_inject_poison ||
4639 h == cmd_media_clear_poison ||
4640 h == cmd_sanitize_overwrite ||
4641 h == cmd_firmware_update_transfer ||
4642 h == cmd_firmware_update_activate) {
4643 return CXL_MBOX_MEDIA_DISABLED;
4644 }
4645 }
4646 }
4647
4648 ret = (*h)(cxl_cmd, pl_in, len_in, pl_out, len_out, cci);
4649 if ((cxl_cmd->effect & CXL_MBOX_BACKGROUND_OPERATION) &&
4650 ret == CXL_MBOX_BG_STARTED) {
4651 *bg_started = true;
4652 } else {
4653 *bg_started = false;
4654 }
4655
4656 /* Set bg and the return code */
4657 if (*bg_started) {
4658 uint64_t now;
4659
4660 cci->bg.opcode = (set << 8) | cmd;
4661
4662 cci->bg.complete_pct = 0;
4663 cci->bg.aborted = false;
4664 cci->bg.ret_code = 0;
4665
4666 now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
4667 cci->bg.starttime = now;
4668 timer_mod(cci->bg.timer, now + CXL_MBOX_BG_UPDATE_FREQ);
4669 }
4670
4671 return ret;
4672 }
4673
4674 static void bg_timercb(void *opaque)
4675 {
4676 CXLCCI *cci = opaque;
4677 uint64_t now, total_time;
4678
4679 qemu_mutex_lock(&cci->bg.lock);
4680
4681 now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
4682 total_time = cci->bg.starttime + cci->bg.runtime;
4683
4684 if (now >= total_time) { /* we are done */
4685 uint16_t ret = CXL_MBOX_SUCCESS;
4686
4687 cci->bg.complete_pct = 100;
4688 cci->bg.ret_code = ret;
4689 switch (cci->bg.opcode) {
4690 case 0x0201: /* fw transfer */
4691 __do_firmware_xfer(cci);
4692 break;
4693 case 0x4400: /* sanitize */
4694 {
4695 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
4696
4697 __do_sanitization(ct3d);
4698 cxl_dev_enable_media(&ct3d->cxl_dstate);
4699 }
4700 break;
4701 case 0x4402: /* Media Operations sanitize */
4702 {
4703 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
4704 __do_sanitize(ct3d);
4705 }
4706 break;
4707 case 0x4304: /* scan media */
4708 {
4709 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
4710
4711 __do_scan_media(ct3d);
4712 break;
4713 }
4714 default:
4715 __builtin_unreachable();
4716 break;
4717 }
4718 } else {
4719 /* estimate only */
4720 cci->bg.complete_pct =
4721 100 * (now - cci->bg.starttime) / cci->bg.runtime;
4722 timer_mod(cci->bg.timer, now + CXL_MBOX_BG_UPDATE_FREQ);
4723 }
4724
4725 if (cci->bg.complete_pct == 100) {
4726 /* TODO: generalize to switch CCI */
4727 CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
4728 CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate;
4729 PCIDevice *pdev = PCI_DEVICE(cci->d);
4730
4731 cci->bg.starttime = 0;
4732 /* registers are updated, allow new bg-capable cmds */
4733 cci->bg.runtime = 0;
4734
4735 if (msix_enabled(pdev)) {
4736 msix_notify(pdev, cxl_dstate->mbox_msi_n);
4737 } else if (msi_enabled(pdev)) {
4738 msi_notify(pdev, cxl_dstate->mbox_msi_n);
4739 }
4740 }
4741
4742 qemu_mutex_unlock(&cci->bg.lock);
4743 }
4744
4745 static void cxl_rebuild_cel(CXLCCI *cci)
4746 {
4747 cci->cel_size = 0; /* Reset for a fresh build */
4748 for (int set = 0; set < 256; set++) {
4749 for (int cmd = 0; cmd < 256; cmd++) {
4750 if (cci->cxl_cmd_set[set][cmd].handler) {
4751 const struct cxl_cmd *c = &cci->cxl_cmd_set[set][cmd];
4752 struct cel_log *log =
4753 &cci->cel_log[cci->cel_size];
4754
4755 log->opcode = (set << 8) | cmd;
4756 log->effect = c->effect;
4757 cci->cel_size++;
4758 }
4759 }
4760 }
4761 }
4762
4763 void cxl_init_physical_port_control(CXLPhyPortPerst *perst)
4764 {
4765 qemu_mutex_init(&perst->lock);
4766 perst->issued_assert_perst = false;
4767 /*
4768 * Assert PERST involves physical port to be in
4769 * hold reset phase for minimum 100ms. No other
4770 * physical port control requests are entertained
4771 * until Deassert PERST command.
4772 */
4773 perst->asrt_time = ASSERT_WAIT_TIME_MS;
4774 }
4775
4776 void cxl_init_cci(CXLCCI *cci, size_t payload_max)
4777 {
4778 cci->payload_max = payload_max;
4779 cxl_rebuild_cel(cci);
4780
4781 cci->bg.complete_pct = 0;
4782 cci->bg.starttime = 0;
4783 cci->bg.runtime = 0;
4784 cci->bg.aborted = false;
4785 cci->bg.timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
4786 bg_timercb, cci);
4787 qemu_mutex_init(&cci->bg.lock);
4788
4789 memset(&cci->fw, 0, sizeof(cci->fw));
4790 cci->fw.active_slot = 1;
4791 cci->fw.slot[cci->fw.active_slot - 1] = true;
4792 cci->initialized = true;
4793 }
4794
4795 void cxl_destroy_cci(CXLCCI *cci)
4796 {
4797 qemu_mutex_destroy(&cci->bg.lock);
4798 cci->initialized = false;
4799 }
4800
4801 static void cxl_copy_cci_commands(CXLCCI *cci, const struct cxl_cmd (*cxl_cmds)[256])
4802 {
4803 for (int set = 0; set < 256; set++) {
4804 for (int cmd = 0; cmd < 256; cmd++) {
4805 if (cxl_cmds[set][cmd].handler) {
4806 cci->cxl_cmd_set[set][cmd] = cxl_cmds[set][cmd];
4807 }
4808 }
4809 }
4810 }
4811
4812 void cxl_add_cci_commands(CXLCCI *cci, const struct cxl_cmd (*cxl_cmd_set)[256],
4813 size_t payload_max)
4814 {
4815 cci->payload_max = MAX(payload_max, cci->payload_max);
4816 cxl_copy_cci_commands(cci, cxl_cmd_set);
4817 cxl_rebuild_cel(cci);
4818 }
4819
4820 void cxl_initialize_mailbox_swcci(CXLCCI *cci, DeviceState *intf,
4821 DeviceState *d, size_t payload_max)
4822 {
4823 cxl_copy_cci_commands(cci, cxl_cmd_set_sw);
4824 cci->d = d;
4825 cci->intf = intf;
4826 cxl_init_cci(cci, payload_max);
4827 }
4828
4829 void cxl_initialize_mailbox_t3(CXLCCI *cci, DeviceState *d, size_t payload_max)
4830 {
4831 CXLType3Dev *ct3d = CXL_TYPE3(d);
4832
4833 cxl_copy_cci_commands(cci, cxl_cmd_set);
4834 if (ct3d->dc.num_regions) {
4835 cxl_copy_cci_commands(cci, cxl_cmd_set_dcd);
4836 }
4837 cci->d = d;
4838
4839 /* No separation for PCI MB as protocol handled in PCI device */
4840 cci->intf = d;
4841 cxl_init_cci(cci, payload_max);
4842 }
4843
4844 static const struct cxl_cmd cxl_cmd_set_t3_ld[256][256] = {
4845 [INFOSTAT][IS_IDENTIFY] = { "IDENTIFY", cmd_infostat_identify, 0, 0 },
4846 [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0,
4847 0 },
4848 [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 },
4849 };
4850
4851 void cxl_initialize_t3_ld_cci(CXLCCI *cci, DeviceState *d, DeviceState *intf,
4852 size_t payload_max)
4853 {
4854 cxl_copy_cci_commands(cci, cxl_cmd_set_t3_ld);
4855 cci->d = d;
4856 cci->intf = intf;
4857 cxl_init_cci(cci, payload_max);
4858 }
4859
4860 static const struct cxl_cmd cxl_cmd_set_t3_fm_owned_ld_mctp[256][256] = {
4861 [INFOSTAT][IS_IDENTIFY] = { "IDENTIFY", cmd_infostat_identify, 0, 0},
4862 [INFOSTAT][GET_RESPONSE_MSG_LIMIT] = { "GET_RESPONSE_MSG_LIMIT",
4863 cmd_get_response_msg_limit, 0, 0 },
4864 [INFOSTAT][SET_RESPONSE_MSG_LIMIT] = { "SET_RESPONSE_MSG_LIMIT",
4865 cmd_set_response_msg_limit, 1, 0 },
4866 [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0,
4867 0 },
4868 [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 },
4869 [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 },
4870 [TUNNEL][MANAGEMENT_COMMAND] = { "TUNNEL_MANAGEMENT_COMMAND",
4871 cmd_tunnel_management_cmd, ~0, 0 },
4872 };
4873
4874 void cxl_initialize_t3_fm_owned_ld_mctpcci(CXLCCI *cci, DeviceState *d,
4875 DeviceState *intf,
4876 size_t payload_max)
4877 {
4878 CXLType3Dev *ct3d = CXL_TYPE3(d);
4879
4880 cxl_copy_cci_commands(cci, cxl_cmd_set_t3_fm_owned_ld_mctp);
4881 if (ct3d->dc.num_regions) {
4882 cxl_copy_cci_commands(cci, cxl_cmd_set_fm_dcd);
4883 }
4884 cci->d = d;
4885 cci->intf = intf;
4886 cxl_init_cci(cci, payload_max);
4887 }