4 * Copyright(C) 2023 Intel Corporation.
6 * This work is licensed under the terms of the GNU GPL, version 2. See the
7 * COPYING file in the top-level directory.
12 #include "qemu/osdep.h"
13 #include "qemu/bswap.h"
14 #include "qemu/typedefs.h"
15 #include "qemu/error-report.h"
16 #include "hw/pci/msi.h"
17 #include "hw/pci/msix.h"
18 #include "hw/cxl/cxl.h"
19 #include "hw/cxl/cxl_events.h"
21 /* Artificial limit on the number of events a log can hold */
22 #define CXL_TEST_EVENT_OVERFLOW 8
24 static void reset_overflow(CXLEventLog
*log
)
26 log
->overflow_err_count
= 0;
27 log
->first_overflow_timestamp
= 0;
28 log
->last_overflow_timestamp
= 0;
31 void cxl_event_init(CXLDeviceState
*cxlds
, int start_msg_num
)
36 for (i
= 0; i
< CXL_EVENT_TYPE_MAX
; i
++) {
37 log
= &cxlds
->event_logs
[i
];
39 log
->overflow_err_count
= 0;
40 log
->first_overflow_timestamp
= 0;
41 log
->last_overflow_timestamp
= 0;
42 log
->irq_enabled
= false;
43 log
->irq_vec
= start_msg_num
++;
44 qemu_mutex_init(&log
->lock
);
45 QSIMPLEQ_INIT(&log
->events
);
48 /* Override -- Dynamic Capacity uses the same vector as info */
49 cxlds
->event_logs
[CXL_EVENT_TYPE_DYNAMIC_CAP
].irq_vec
=
50 cxlds
->event_logs
[CXL_EVENT_TYPE_INFO
].irq_vec
;
54 static CXLEvent
*cxl_event_get_head(CXLEventLog
*log
)
56 return QSIMPLEQ_FIRST(&log
->events
);
59 static CXLEvent
*cxl_event_get_next(CXLEvent
*entry
)
61 return QSIMPLEQ_NEXT(entry
, node
);
64 static int cxl_event_count(CXLEventLog
*log
)
69 QSIMPLEQ_FOREACH(event
, &log
->events
, node
) {
76 static bool cxl_event_empty(CXLEventLog
*log
)
78 return QSIMPLEQ_EMPTY(&log
->events
);
81 static void cxl_event_delete_head(CXLDeviceState
*cxlds
,
82 CXLEventLogType log_type
,
85 CXLEvent
*entry
= cxl_event_get_head(log
);
88 QSIMPLEQ_REMOVE_HEAD(&log
->events
, node
);
89 if (cxl_event_empty(log
)) {
90 cxl_event_set_status(cxlds
, log_type
, false);
96 * return true if an interrupt should be generated as a result
97 * of inserting this event.
99 bool cxl_event_insert(CXLDeviceState
*cxlds
, CXLEventLogType log_type
,
100 CXLEventRecordRaw
*event
)
106 if (log_type
>= CXL_EVENT_TYPE_MAX
) {
110 time
= cxl_device_get_timestamp(cxlds
);
112 log
= &cxlds
->event_logs
[log_type
];
114 QEMU_LOCK_GUARD(&log
->lock
);
116 if (cxl_event_count(log
) >= CXL_TEST_EVENT_OVERFLOW
) {
117 if (log
->overflow_err_count
== 0) {
118 log
->first_overflow_timestamp
= time
;
120 log
->overflow_err_count
++;
121 log
->last_overflow_timestamp
= time
;
125 entry
= g_new0(CXLEvent
, 1);
127 memcpy(&entry
->data
, event
, sizeof(*event
));
129 entry
->data
.hdr
.handle
= cpu_to_le16(log
->next_handle
);
131 /* 0 handle is never valid */
132 if (log
->next_handle
== 0) {
135 entry
->data
.hdr
.timestamp
= cpu_to_le64(time
);
137 QSIMPLEQ_INSERT_TAIL(&log
->events
, entry
, node
);
138 cxl_event_set_status(cxlds
, log_type
, true);
140 /* Count went from 0 to 1 */
141 return cxl_event_count(log
) == 1;
144 CXLRetCode
cxl_event_get_records(CXLDeviceState
*cxlds
, CXLGetEventPayload
*pl
,
145 uint8_t log_type
, int max_recs
,
152 if (log_type
>= CXL_EVENT_TYPE_MAX
) {
153 return CXL_MBOX_INVALID_INPUT
;
156 log
= &cxlds
->event_logs
[log_type
];
158 QEMU_LOCK_GUARD(&log
->lock
);
160 entry
= cxl_event_get_head(log
);
161 for (nr
= 0; entry
&& nr
< max_recs
; nr
++) {
162 memcpy(&pl
->records
[nr
], &entry
->data
, CXL_EVENT_RECORD_SIZE
);
163 entry
= cxl_event_get_next(entry
);
166 if (!cxl_event_empty(log
)) {
167 pl
->flags
|= CXL_GET_EVENT_FLAG_MORE_RECORDS
;
170 if (log
->overflow_err_count
) {
171 pl
->flags
|= CXL_GET_EVENT_FLAG_OVERFLOW
;
172 pl
->overflow_err_count
= cpu_to_le16(log
->overflow_err_count
);
173 pl
->first_overflow_timestamp
=
174 cpu_to_le64(log
->first_overflow_timestamp
);
175 pl
->last_overflow_timestamp
=
176 cpu_to_le64(log
->last_overflow_timestamp
);
179 pl
->record_count
= cpu_to_le16(nr
);
180 *len
= CXL_EVENT_PAYLOAD_HDR_SIZE
+ (CXL_EVENT_RECORD_SIZE
* nr
);
182 return CXL_MBOX_SUCCESS
;
185 CXLRetCode
cxl_event_clear_records(CXLDeviceState
*cxlds
,
186 CXLClearEventPayload
*pl
)
193 log_type
= pl
->event_log
;
195 if (log_type
>= CXL_EVENT_TYPE_MAX
) {
196 return CXL_MBOX_INVALID_INPUT
;
199 log
= &cxlds
->event_logs
[log_type
];
201 QEMU_LOCK_GUARD(&log
->lock
);
203 * Must iterate the queue twice.
204 * "The device shall verify the event record handles specified in the input
205 * payload are in temporal order. If the device detects an older event
206 * record that will not be cleared when Clear Event Records is executed,
207 * the device shall return the Invalid Handle return code and shall not
208 * clear any of the specified event records."
209 * -- CXL 3.0 8.2.9.2.3
211 entry
= cxl_event_get_head(log
);
212 for (nr
= 0; entry
&& nr
< pl
->nr_recs
; nr
++) {
213 uint16_t handle
= pl
->handle
[nr
];
215 /* NOTE: Both handles are little endian. */
216 if (handle
== 0 || entry
->data
.hdr
.handle
!= handle
) {
217 return CXL_MBOX_INVALID_INPUT
;
219 entry
= cxl_event_get_next(entry
);
222 entry
= cxl_event_get_head(log
);
223 for (nr
= 0; entry
&& nr
< pl
->nr_recs
; nr
++) {
224 cxl_event_delete_head(cxlds
, log_type
, log
);
225 entry
= cxl_event_get_head(log
);
228 return CXL_MBOX_SUCCESS
;
231 void cxl_event_irq_assert(CXLType3Dev
*ct3d
)
233 CXLDeviceState
*cxlds
= &ct3d
->cxl_dstate
;
234 PCIDevice
*pdev
= &ct3d
->parent_obj
;
237 for (i
= 0; i
< CXL_EVENT_TYPE_MAX
; i
++) {
238 CXLEventLog
*log
= &cxlds
->event_logs
[i
];
240 if (!log
->irq_enabled
|| cxl_event_empty(log
)) {
244 /* Notifies interrupt, legacy IRQ is not supported */
245 if (msix_enabled(pdev
)) {
246 msix_notify(pdev
, log
->irq_vec
);
247 } else if (msi_enabled(pdev
)) {
248 msi_notify(pdev
, log
->irq_vec
);