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