1 /* 2 * CXL Event processing 3 * 4 * Copyright(C) 2023 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 12 #include "qemu/bswap.h" 13 #include "qemu/error-report.h" 14 #include "hw/pci/msi.h" 15 #include "hw/pci/msix.h" 16 #include "hw/cxl/cxl.h" 17 #include "hw/cxl/cxl_events.h" 18 19 /* Artificial limit on the number of events a log can hold */ 20 #define CXL_TEST_EVENT_OVERFLOW 8 21 22 static void reset_overflow(CXLEventLog *log) 23 { 24 log->overflow_err_count = 0; 25 log->first_overflow_timestamp = 0; 26 log->last_overflow_timestamp = 0; 27 } 28 29 void cxl_event_init(CXLDeviceState *cxlds, int start_msg_num) 30 { 31 CXLEventLog *log; 32 int i; 33 34 for (i = 0; i < CXL_EVENT_TYPE_MAX; i++) { 35 log = &cxlds->event_logs[i]; 36 log->next_handle = 1; 37 log->overflow_err_count = 0; 38 log->first_overflow_timestamp = 0; 39 log->last_overflow_timestamp = 0; 40 log->irq_enabled = false; 41 log->irq_vec = start_msg_num++; 42 qemu_mutex_init(&log->lock); 43 QSIMPLEQ_INIT(&log->events); 44 } 45 46 /* Override -- Dynamic Capacity uses the same vector as info */ 47 cxlds->event_logs[CXL_EVENT_TYPE_DYNAMIC_CAP].irq_vec = 48 cxlds->event_logs[CXL_EVENT_TYPE_INFO].irq_vec; 49 50 } 51 52 static CXLEvent *cxl_event_get_head(CXLEventLog *log) 53 { 54 return QSIMPLEQ_FIRST(&log->events); 55 } 56 57 static CXLEvent *cxl_event_get_next(CXLEvent *entry) 58 { 59 return QSIMPLEQ_NEXT(entry, node); 60 } 61 62 static int cxl_event_count(CXLEventLog *log) 63 { 64 CXLEvent *event; 65 int rc = 0; 66 67 QSIMPLEQ_FOREACH(event, &log->events, node) { 68 rc++; 69 } 70 71 return rc; 72 } 73 74 static bool cxl_event_empty(CXLEventLog *log) 75 { 76 return QSIMPLEQ_EMPTY(&log->events); 77 } 78 79 static void cxl_event_delete_head(CXLDeviceState *cxlds, 80 CXLEventLogType log_type, 81 CXLEventLog *log) 82 { 83 CXLEvent *entry = cxl_event_get_head(log); 84 85 reset_overflow(log); 86 QSIMPLEQ_REMOVE_HEAD(&log->events, node); 87 if (cxl_event_empty(log)) { 88 cxl_event_set_status(cxlds, log_type, false); 89 } 90 g_free(entry); 91 } 92 93 /* 94 * return true if an interrupt should be generated as a result 95 * of inserting this event. 96 */ 97 bool cxl_event_insert(CXLDeviceState *cxlds, CXLEventLogType log_type, 98 CXLEventRecordRaw *event) 99 { 100 uint64_t time; 101 CXLEventLog *log; 102 CXLEvent *entry; 103 104 if (log_type >= CXL_EVENT_TYPE_MAX) { 105 return false; 106 } 107 108 time = cxl_device_get_timestamp(cxlds); 109 110 log = &cxlds->event_logs[log_type]; 111 112 QEMU_LOCK_GUARD(&log->lock); 113 114 if (cxl_event_count(log) >= CXL_TEST_EVENT_OVERFLOW) { 115 if (log->overflow_err_count == 0) { 116 log->first_overflow_timestamp = time; 117 } 118 log->overflow_err_count++; 119 log->last_overflow_timestamp = time; 120 return false; 121 } 122 123 entry = g_new0(CXLEvent, 1); 124 125 memcpy(&entry->data, event, sizeof(*event)); 126 127 entry->data.hdr.handle = cpu_to_le16(log->next_handle); 128 log->next_handle++; 129 /* 0 handle is never valid */ 130 if (log->next_handle == 0) { 131 log->next_handle++; 132 } 133 entry->data.hdr.timestamp = cpu_to_le64(time); 134 135 QSIMPLEQ_INSERT_TAIL(&log->events, entry, node); 136 cxl_event_set_status(cxlds, log_type, true); 137 138 /* Count went from 0 to 1 */ 139 return cxl_event_count(log) == 1; 140 } 141 142 CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload *pl, 143 uint8_t log_type, int max_recs, 144 size_t *len) 145 { 146 CXLEventLog *log; 147 CXLEvent *entry; 148 uint16_t nr; 149 150 if (log_type >= CXL_EVENT_TYPE_MAX) { 151 return CXL_MBOX_INVALID_INPUT; 152 } 153 154 log = &cxlds->event_logs[log_type]; 155 156 QEMU_LOCK_GUARD(&log->lock); 157 158 entry = cxl_event_get_head(log); 159 for (nr = 0; entry && nr < max_recs; nr++) { 160 memcpy(&pl->records[nr], &entry->data, CXL_EVENT_RECORD_SIZE); 161 entry = cxl_event_get_next(entry); 162 } 163 164 if (!cxl_event_empty(log)) { 165 pl->flags |= CXL_GET_EVENT_FLAG_MORE_RECORDS; 166 } 167 168 if (log->overflow_err_count) { 169 pl->flags |= CXL_GET_EVENT_FLAG_OVERFLOW; 170 pl->overflow_err_count = cpu_to_le16(log->overflow_err_count); 171 pl->first_overflow_timestamp = 172 cpu_to_le64(log->first_overflow_timestamp); 173 pl->last_overflow_timestamp = 174 cpu_to_le64(log->last_overflow_timestamp); 175 } 176 177 pl->record_count = cpu_to_le16(nr); 178 *len = CXL_EVENT_PAYLOAD_HDR_SIZE + (CXL_EVENT_RECORD_SIZE * nr); 179 180 return CXL_MBOX_SUCCESS; 181 } 182 183 CXLRetCode cxl_event_clear_records(CXLDeviceState *cxlds, 184 CXLClearEventPayload *pl) 185 { 186 CXLEventLog *log; 187 uint8_t log_type; 188 CXLEvent *entry; 189 int nr; 190 191 log_type = pl->event_log; 192 193 if (log_type >= CXL_EVENT_TYPE_MAX) { 194 return CXL_MBOX_INVALID_INPUT; 195 } 196 197 log = &cxlds->event_logs[log_type]; 198 199 QEMU_LOCK_GUARD(&log->lock); 200 /* 201 * Must iterate the queue twice. 202 * "The device shall verify the event record handles specified in the input 203 * payload are in temporal order. If the device detects an older event 204 * record that will not be cleared when Clear Event Records is executed, 205 * the device shall return the Invalid Handle return code and shall not 206 * clear any of the specified event records." 207 * -- CXL r3.1 Section 8.2.9.2.3: Clear Event Records (0101h) 208 */ 209 entry = cxl_event_get_head(log); 210 for (nr = 0; entry && nr < pl->nr_recs; nr++) { 211 uint16_t handle = pl->handle[nr]; 212 213 /* NOTE: Both handles are little endian. */ 214 if (handle == 0 || entry->data.hdr.handle != handle) { 215 return CXL_MBOX_INVALID_INPUT; 216 } 217 entry = cxl_event_get_next(entry); 218 } 219 220 entry = cxl_event_get_head(log); 221 for (nr = 0; entry && nr < pl->nr_recs; nr++) { 222 cxl_event_delete_head(cxlds, log_type, log); 223 entry = cxl_event_get_head(log); 224 } 225 226 return CXL_MBOX_SUCCESS; 227 } 228 229 void cxl_event_irq_assert(CXLType3Dev *ct3d) 230 { 231 CXLDeviceState *cxlds = &ct3d->cxl_dstate; 232 PCIDevice *pdev = &ct3d->parent_obj; 233 int i; 234 235 for (i = 0; i < CXL_EVENT_TYPE_MAX; i++) { 236 CXLEventLog *log = &cxlds->event_logs[i]; 237 238 if (!log->irq_enabled || cxl_event_empty(log)) { 239 continue; 240 } 241 242 /* Notifies interrupt, legacy IRQ is not supported */ 243 if (msix_enabled(pdev)) { 244 msix_notify(pdev, log->irq_vec); 245 } else if (msi_enabled(pdev)) { 246 msi_notify(pdev, log->irq_vec); 247 } 248 } 249 } 250