xref: /openbmc/qemu/hw/cxl/cxl-events.c (revision 5885bcef3d760e84d17eb4113e85f2aea0bd0582)
122d7e3beSIra Weiny /*
222d7e3beSIra Weiny  * CXL Event processing
322d7e3beSIra Weiny  *
422d7e3beSIra Weiny  * Copyright(C) 2023 Intel Corporation.
522d7e3beSIra Weiny  *
622d7e3beSIra Weiny  * This work is licensed under the terms of the GNU GPL, version 2. See the
722d7e3beSIra Weiny  * COPYING file in the top-level directory.
822d7e3beSIra Weiny  */
922d7e3beSIra Weiny 
1022d7e3beSIra Weiny #include "qemu/osdep.h"
11ad5a8c4dSPeter Maydell 
1222d7e3beSIra Weiny #include "qemu/bswap.h"
1322d7e3beSIra Weiny #include "qemu/error-report.h"
146676bb97SIra Weiny #include "hw/pci/msi.h"
156676bb97SIra Weiny #include "hw/pci/msix.h"
1622d7e3beSIra Weiny #include "hw/cxl/cxl.h"
1722d7e3beSIra Weiny #include "hw/cxl/cxl_events.h"
1822d7e3beSIra Weiny 
1922d7e3beSIra Weiny /* Artificial limit on the number of events a log can hold */
2022d7e3beSIra Weiny #define CXL_TEST_EVENT_OVERFLOW 8
2122d7e3beSIra Weiny 
reset_overflow(CXLEventLog * log)2222d7e3beSIra Weiny static void reset_overflow(CXLEventLog *log)
2322d7e3beSIra Weiny {
2422d7e3beSIra Weiny     log->overflow_err_count = 0;
2522d7e3beSIra Weiny     log->first_overflow_timestamp = 0;
2622d7e3beSIra Weiny     log->last_overflow_timestamp = 0;
2722d7e3beSIra Weiny }
2822d7e3beSIra Weiny 
cxl_event_init(CXLDeviceState * cxlds,int start_msg_num)296676bb97SIra Weiny void cxl_event_init(CXLDeviceState *cxlds, int start_msg_num)
3022d7e3beSIra Weiny {
3122d7e3beSIra Weiny     CXLEventLog *log;
3222d7e3beSIra Weiny     int i;
3322d7e3beSIra Weiny 
3422d7e3beSIra Weiny     for (i = 0; i < CXL_EVENT_TYPE_MAX; i++) {
3522d7e3beSIra Weiny         log = &cxlds->event_logs[i];
3622d7e3beSIra Weiny         log->next_handle = 1;
3722d7e3beSIra Weiny         log->overflow_err_count = 0;
3822d7e3beSIra Weiny         log->first_overflow_timestamp = 0;
3922d7e3beSIra Weiny         log->last_overflow_timestamp = 0;
406676bb97SIra Weiny         log->irq_enabled = false;
416676bb97SIra Weiny         log->irq_vec = start_msg_num++;
4222d7e3beSIra Weiny         qemu_mutex_init(&log->lock);
4322d7e3beSIra Weiny         QSIMPLEQ_INIT(&log->events);
4422d7e3beSIra Weiny     }
456676bb97SIra Weiny 
466676bb97SIra Weiny     /* Override -- Dynamic Capacity uses the same vector as info */
476676bb97SIra Weiny     cxlds->event_logs[CXL_EVENT_TYPE_DYNAMIC_CAP].irq_vec =
486676bb97SIra Weiny                       cxlds->event_logs[CXL_EVENT_TYPE_INFO].irq_vec;
496676bb97SIra Weiny 
5022d7e3beSIra Weiny }
5122d7e3beSIra Weiny 
cxl_event_get_head(CXLEventLog * log)5222d7e3beSIra Weiny static CXLEvent *cxl_event_get_head(CXLEventLog *log)
5322d7e3beSIra Weiny {
5422d7e3beSIra Weiny     return QSIMPLEQ_FIRST(&log->events);
5522d7e3beSIra Weiny }
5622d7e3beSIra Weiny 
cxl_event_get_next(CXLEvent * entry)5722d7e3beSIra Weiny static CXLEvent *cxl_event_get_next(CXLEvent *entry)
5822d7e3beSIra Weiny {
5922d7e3beSIra Weiny     return QSIMPLEQ_NEXT(entry, node);
6022d7e3beSIra Weiny }
6122d7e3beSIra Weiny 
cxl_event_count(CXLEventLog * log)6222d7e3beSIra Weiny static int cxl_event_count(CXLEventLog *log)
6322d7e3beSIra Weiny {
6422d7e3beSIra Weiny     CXLEvent *event;
6522d7e3beSIra Weiny     int rc = 0;
6622d7e3beSIra Weiny 
6722d7e3beSIra Weiny     QSIMPLEQ_FOREACH(event, &log->events, node) {
6822d7e3beSIra Weiny         rc++;
6922d7e3beSIra Weiny     }
7022d7e3beSIra Weiny 
7122d7e3beSIra Weiny     return rc;
7222d7e3beSIra Weiny }
7322d7e3beSIra Weiny 
cxl_event_empty(CXLEventLog * log)7422d7e3beSIra Weiny static bool cxl_event_empty(CXLEventLog *log)
7522d7e3beSIra Weiny {
7622d7e3beSIra Weiny     return QSIMPLEQ_EMPTY(&log->events);
7722d7e3beSIra Weiny }
7822d7e3beSIra Weiny 
cxl_event_delete_head(CXLDeviceState * cxlds,CXLEventLogType log_type,CXLEventLog * log)7922d7e3beSIra Weiny static void cxl_event_delete_head(CXLDeviceState *cxlds,
8022d7e3beSIra Weiny                                   CXLEventLogType log_type,
8122d7e3beSIra Weiny                                   CXLEventLog *log)
8222d7e3beSIra Weiny {
8322d7e3beSIra Weiny     CXLEvent *entry = cxl_event_get_head(log);
8422d7e3beSIra Weiny 
8522d7e3beSIra Weiny     reset_overflow(log);
8622d7e3beSIra Weiny     QSIMPLEQ_REMOVE_HEAD(&log->events, node);
8722d7e3beSIra Weiny     if (cxl_event_empty(log)) {
8822d7e3beSIra Weiny         cxl_event_set_status(cxlds, log_type, false);
8922d7e3beSIra Weiny     }
9022d7e3beSIra Weiny     g_free(entry);
9122d7e3beSIra Weiny }
9222d7e3beSIra Weiny 
9322d7e3beSIra Weiny /*
9422d7e3beSIra Weiny  * return true if an interrupt should be generated as a result
9522d7e3beSIra Weiny  * of inserting this event.
9622d7e3beSIra Weiny  */
cxl_event_insert(CXLDeviceState * cxlds,CXLEventLogType log_type,CXLEventRecordRaw * event)9722d7e3beSIra Weiny bool cxl_event_insert(CXLDeviceState *cxlds, CXLEventLogType log_type,
9822d7e3beSIra Weiny                       CXLEventRecordRaw *event)
9922d7e3beSIra Weiny {
10022d7e3beSIra Weiny     uint64_t time;
10122d7e3beSIra Weiny     CXLEventLog *log;
10222d7e3beSIra Weiny     CXLEvent *entry;
10322d7e3beSIra Weiny 
10422d7e3beSIra Weiny     if (log_type >= CXL_EVENT_TYPE_MAX) {
10522d7e3beSIra Weiny         return false;
10622d7e3beSIra Weiny     }
10722d7e3beSIra Weiny 
10822d7e3beSIra Weiny     time = cxl_device_get_timestamp(cxlds);
10922d7e3beSIra Weiny 
11022d7e3beSIra Weiny     log = &cxlds->event_logs[log_type];
11122d7e3beSIra Weiny 
11222d7e3beSIra Weiny     QEMU_LOCK_GUARD(&log->lock);
11322d7e3beSIra Weiny 
11422d7e3beSIra Weiny     if (cxl_event_count(log) >= CXL_TEST_EVENT_OVERFLOW) {
11522d7e3beSIra Weiny         if (log->overflow_err_count == 0) {
11622d7e3beSIra Weiny             log->first_overflow_timestamp = time;
11722d7e3beSIra Weiny         }
11822d7e3beSIra Weiny         log->overflow_err_count++;
11922d7e3beSIra Weiny         log->last_overflow_timestamp = time;
12022d7e3beSIra Weiny         return false;
12122d7e3beSIra Weiny     }
12222d7e3beSIra Weiny 
12322d7e3beSIra Weiny     entry = g_new0(CXLEvent, 1);
12422d7e3beSIra Weiny 
12522d7e3beSIra Weiny     memcpy(&entry->data, event, sizeof(*event));
12622d7e3beSIra Weiny 
12722d7e3beSIra Weiny     entry->data.hdr.handle = cpu_to_le16(log->next_handle);
12822d7e3beSIra Weiny     log->next_handle++;
12922d7e3beSIra Weiny     /* 0 handle is never valid */
13022d7e3beSIra Weiny     if (log->next_handle == 0) {
13122d7e3beSIra Weiny         log->next_handle++;
13222d7e3beSIra Weiny     }
13322d7e3beSIra Weiny     entry->data.hdr.timestamp = cpu_to_le64(time);
13422d7e3beSIra Weiny 
13522d7e3beSIra Weiny     QSIMPLEQ_INSERT_TAIL(&log->events, entry, node);
13622d7e3beSIra Weiny     cxl_event_set_status(cxlds, log_type, true);
13722d7e3beSIra Weiny 
13822d7e3beSIra Weiny     /* Count went from 0 to 1 */
13922d7e3beSIra Weiny     return cxl_event_count(log) == 1;
14022d7e3beSIra Weiny }
14122d7e3beSIra Weiny 
cxl_discard_all_event_records(CXLDeviceState * cxlds)142*7d65874bSHyeonggon Yoo void cxl_discard_all_event_records(CXLDeviceState *cxlds)
143*7d65874bSHyeonggon Yoo {
144*7d65874bSHyeonggon Yoo     CXLEventLogType log_type;
145*7d65874bSHyeonggon Yoo     CXLEventLog *log;
146*7d65874bSHyeonggon Yoo 
147*7d65874bSHyeonggon Yoo     for (log_type = 0; log_type < CXL_EVENT_TYPE_MAX; log_type++) {
148*7d65874bSHyeonggon Yoo         log = &cxlds->event_logs[log_type];
149*7d65874bSHyeonggon Yoo         while (!cxl_event_empty(log)) {
150*7d65874bSHyeonggon Yoo             cxl_event_delete_head(cxlds, log_type, log);
151*7d65874bSHyeonggon Yoo         }
152*7d65874bSHyeonggon Yoo     }
153*7d65874bSHyeonggon Yoo }
154*7d65874bSHyeonggon Yoo 
cxl_event_get_records(CXLDeviceState * cxlds,CXLGetEventPayload * pl,uint8_t log_type,int max_recs,size_t * len)15522d7e3beSIra Weiny CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload *pl,
15622d7e3beSIra Weiny                                  uint8_t log_type, int max_recs,
1576f59274eSJonathan Cameron                                  size_t *len)
15822d7e3beSIra Weiny {
15922d7e3beSIra Weiny     CXLEventLog *log;
16022d7e3beSIra Weiny     CXLEvent *entry;
16122d7e3beSIra Weiny     uint16_t nr;
16222d7e3beSIra Weiny 
16322d7e3beSIra Weiny     if (log_type >= CXL_EVENT_TYPE_MAX) {
16422d7e3beSIra Weiny         return CXL_MBOX_INVALID_INPUT;
16522d7e3beSIra Weiny     }
16622d7e3beSIra Weiny 
16722d7e3beSIra Weiny     log = &cxlds->event_logs[log_type];
16822d7e3beSIra Weiny 
16922d7e3beSIra Weiny     QEMU_LOCK_GUARD(&log->lock);
17022d7e3beSIra Weiny 
17122d7e3beSIra Weiny     entry = cxl_event_get_head(log);
17222d7e3beSIra Weiny     for (nr = 0; entry && nr < max_recs; nr++) {
17322d7e3beSIra Weiny         memcpy(&pl->records[nr], &entry->data, CXL_EVENT_RECORD_SIZE);
17422d7e3beSIra Weiny         entry = cxl_event_get_next(entry);
17522d7e3beSIra Weiny     }
17622d7e3beSIra Weiny 
17722d7e3beSIra Weiny     if (!cxl_event_empty(log)) {
17822d7e3beSIra Weiny         pl->flags |= CXL_GET_EVENT_FLAG_MORE_RECORDS;
17922d7e3beSIra Weiny     }
18022d7e3beSIra Weiny 
18122d7e3beSIra Weiny     if (log->overflow_err_count) {
18222d7e3beSIra Weiny         pl->flags |= CXL_GET_EVENT_FLAG_OVERFLOW;
18322d7e3beSIra Weiny         pl->overflow_err_count = cpu_to_le16(log->overflow_err_count);
184b342489aSJonathan Cameron         pl->first_overflow_timestamp =
185b342489aSJonathan Cameron             cpu_to_le64(log->first_overflow_timestamp);
186b342489aSJonathan Cameron         pl->last_overflow_timestamp =
187b342489aSJonathan Cameron             cpu_to_le64(log->last_overflow_timestamp);
18822d7e3beSIra Weiny     }
18922d7e3beSIra Weiny 
19022d7e3beSIra Weiny     pl->record_count = cpu_to_le16(nr);
19122d7e3beSIra Weiny     *len = CXL_EVENT_PAYLOAD_HDR_SIZE + (CXL_EVENT_RECORD_SIZE * nr);
19222d7e3beSIra Weiny 
19322d7e3beSIra Weiny     return CXL_MBOX_SUCCESS;
19422d7e3beSIra Weiny }
19522d7e3beSIra Weiny 
cxl_event_clear_records(CXLDeviceState * cxlds,CXLClearEventPayload * pl)196b342489aSJonathan Cameron CXLRetCode cxl_event_clear_records(CXLDeviceState *cxlds,
197b342489aSJonathan Cameron                                    CXLClearEventPayload *pl)
19822d7e3beSIra Weiny {
19922d7e3beSIra Weiny     CXLEventLog *log;
20022d7e3beSIra Weiny     uint8_t log_type;
20122d7e3beSIra Weiny     CXLEvent *entry;
20222d7e3beSIra Weiny     int nr;
20322d7e3beSIra Weiny 
20422d7e3beSIra Weiny     log_type = pl->event_log;
20522d7e3beSIra Weiny 
20622d7e3beSIra Weiny     if (log_type >= CXL_EVENT_TYPE_MAX) {
20722d7e3beSIra Weiny         return CXL_MBOX_INVALID_INPUT;
20822d7e3beSIra Weiny     }
20922d7e3beSIra Weiny 
21022d7e3beSIra Weiny     log = &cxlds->event_logs[log_type];
21122d7e3beSIra Weiny 
21222d7e3beSIra Weiny     QEMU_LOCK_GUARD(&log->lock);
21322d7e3beSIra Weiny     /*
2149b4b4e51SMichael Tokarev      * Must iterate the queue twice.
21522d7e3beSIra Weiny      * "The device shall verify the event record handles specified in the input
21622d7e3beSIra Weiny      * payload are in temporal order. If the device detects an older event
21722d7e3beSIra Weiny      * record that will not be cleared when Clear Event Records is executed,
21822d7e3beSIra Weiny      * the device shall return the Invalid Handle return code and shall not
21922d7e3beSIra Weiny      * clear any of the specified event records."
2208700ee15SJonathan Cameron      *   -- CXL r3.1 Section 8.2.9.2.3: Clear Event Records (0101h)
22122d7e3beSIra Weiny      */
22222d7e3beSIra Weiny     entry = cxl_event_get_head(log);
22322d7e3beSIra Weiny     for (nr = 0; entry && nr < pl->nr_recs; nr++) {
22422d7e3beSIra Weiny         uint16_t handle = pl->handle[nr];
22522d7e3beSIra Weiny 
22622d7e3beSIra Weiny         /* NOTE: Both handles are little endian. */
22722d7e3beSIra Weiny         if (handle == 0 || entry->data.hdr.handle != handle) {
22822d7e3beSIra Weiny             return CXL_MBOX_INVALID_INPUT;
22922d7e3beSIra Weiny         }
23022d7e3beSIra Weiny         entry = cxl_event_get_next(entry);
23122d7e3beSIra Weiny     }
23222d7e3beSIra Weiny 
23322d7e3beSIra Weiny     entry = cxl_event_get_head(log);
23422d7e3beSIra Weiny     for (nr = 0; entry && nr < pl->nr_recs; nr++) {
23522d7e3beSIra Weiny         cxl_event_delete_head(cxlds, log_type, log);
23622d7e3beSIra Weiny         entry = cxl_event_get_head(log);
23722d7e3beSIra Weiny     }
23822d7e3beSIra Weiny 
23922d7e3beSIra Weiny     return CXL_MBOX_SUCCESS;
24022d7e3beSIra Weiny }
2416676bb97SIra Weiny 
cxl_event_irq_assert(CXLType3Dev * ct3d)2426676bb97SIra Weiny void cxl_event_irq_assert(CXLType3Dev *ct3d)
2436676bb97SIra Weiny {
2446676bb97SIra Weiny     CXLDeviceState *cxlds = &ct3d->cxl_dstate;
2456676bb97SIra Weiny     PCIDevice *pdev = &ct3d->parent_obj;
2466676bb97SIra Weiny     int i;
2476676bb97SIra Weiny 
2486676bb97SIra Weiny     for (i = 0; i < CXL_EVENT_TYPE_MAX; i++) {
2496676bb97SIra Weiny         CXLEventLog *log = &cxlds->event_logs[i];
2506676bb97SIra Weiny 
2516676bb97SIra Weiny         if (!log->irq_enabled || cxl_event_empty(log)) {
2526676bb97SIra Weiny             continue;
2536676bb97SIra Weiny         }
2546676bb97SIra Weiny 
2556676bb97SIra Weiny         /*  Notifies interrupt, legacy IRQ is not supported */
2566676bb97SIra Weiny         if (msix_enabled(pdev)) {
2576676bb97SIra Weiny             msix_notify(pdev, log->irq_vec);
2586676bb97SIra Weiny         } else if (msi_enabled(pdev)) {
2596676bb97SIra Weiny             msi_notify(pdev, log->irq_vec);
2606676bb97SIra Weiny         }
2616676bb97SIra Weiny     }
2626676bb97SIra Weiny }
263