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