| 1 | /* |
| 2 | * PCIe Data Object Exchange |
| 3 | * |
| 4 | * Copyright (C) 2021 Avery Design Systems, Inc. |
| 5 | * |
| 6 | * This work is licensed under the terms of the GNU GPL, version 2 or later. |
| 7 | * See the COPYING file in the top-level directory. |
| 8 | */ |
| 9 | |
| 10 | #include "qemu/osdep.h" |
| 11 | #include "qemu/log.h" |
| 12 | #include "qemu/error-report.h" |
| 13 | #include "qapi/error.h" |
| 14 | #include "qemu/range.h" |
| 15 | #include "hw/pci/pci.h" |
| 16 | #include "hw/pci/pcie.h" |
| 17 | #include "hw/pci/pcie_doe.h" |
| 18 | #include "hw/pci/msi.h" |
| 19 | #include "hw/pci/msix.h" |
| 20 | |
| 21 | #define DWORD_BYTE 4 |
| 22 | |
| 23 | typedef struct DoeDiscoveryReq { |
| 24 | DOEHeader header; |
| 25 | uint8_t index; |
| 26 | uint8_t reserved[3]; |
| 27 | } QEMU_PACKED DoeDiscoveryReq; |
| 28 | |
| 29 | typedef struct DoeDiscoveryRsp { |
| 30 | DOEHeader header; |
| 31 | uint16_t vendor_id; |
| 32 | uint8_t data_obj_type; |
| 33 | uint8_t next_index; |
| 34 | } QEMU_PACKED DoeDiscoveryRsp; |
| 35 | |
| 36 | static bool pcie_doe_discovery(DOECap *doe_cap) |
| 37 | { |
| 38 | DoeDiscoveryReq *req = pcie_doe_get_write_mbox_ptr(doe_cap); |
| 39 | DoeDiscoveryRsp rsp; |
| 40 | uint8_t index = req->index; |
| 41 | DOEProtocol *prot; |
| 42 | |
| 43 | /* Discard request if length does not match DoeDiscoveryReq */ |
| 44 | if (pcie_doe_get_obj_len(req) < |
| 45 | DIV_ROUND_UP(sizeof(DoeDiscoveryReq), DWORD_BYTE)) { |
| 46 | return false; |
| 47 | } |
| 48 | |
| 49 | rsp.header = (DOEHeader) { |
| 50 | .vendor_id = PCI_VENDOR_ID_PCI_SIG, |
| 51 | .data_obj_type = PCI_SIG_DOE_DISCOVERY, |
| 52 | .length = DIV_ROUND_UP(sizeof(DoeDiscoveryRsp), DWORD_BYTE), |
| 53 | }; |
| 54 | |
| 55 | /* Point to the requested protocol, index 0 must be Discovery */ |
| 56 | if (index == 0) { |
| 57 | rsp.vendor_id = PCI_VENDOR_ID_PCI_SIG; |
| 58 | rsp.data_obj_type = PCI_SIG_DOE_DISCOVERY; |
| 59 | } else { |
| 60 | if (index < doe_cap->protocol_num) { |
| 61 | prot = &doe_cap->protocols[index - 1]; |
| 62 | rsp.vendor_id = prot->vendor_id; |
| 63 | rsp.data_obj_type = prot->data_obj_type; |
| 64 | } else { |
| 65 | rsp.vendor_id = 0xFFFF; |
| 66 | rsp.data_obj_type = 0xFF; |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | if (index + 1 == doe_cap->protocol_num) { |
| 71 | rsp.next_index = 0; |
| 72 | } else { |
| 73 | rsp.next_index = index + 1; |
| 74 | } |
| 75 | |
| 76 | pcie_doe_set_rsp(doe_cap, &rsp); |
| 77 | |
| 78 | return true; |
| 79 | } |
| 80 | |
| 81 | static void pcie_doe_reset_write_mbox(DOECap *st) |
| 82 | { |
| 83 | st->write_mbox_len = 0; |
| 84 | |
| 85 | memset(st->write_mbox, 0, PCI_DOE_DW_SIZE_MAX * DWORD_BYTE); |
| 86 | } |
| 87 | |
| 88 | static void pcie_doe_reset_mbox(DOECap *st) |
| 89 | { |
| 90 | st->read_mbox_idx = 0; |
| 91 | st->read_mbox_len = 0; |
| 92 | |
| 93 | memset(st->read_mbox, 0, PCI_DOE_DW_SIZE_MAX * DWORD_BYTE); |
| 94 | |
| 95 | pcie_doe_reset_write_mbox(st); |
| 96 | } |
| 97 | |
| 98 | void pcie_doe_init(PCIDevice *dev, DOECap *doe_cap, uint16_t offset, |
| 99 | DOEProtocol *protocols, bool intr, uint16_t vec) |
| 100 | { |
| 101 | pcie_add_capability(dev, PCI_EXT_CAP_ID_DOE, 0x1, offset, |
| 102 | PCI_DOE_SIZEOF); |
| 103 | |
| 104 | doe_cap->pdev = dev; |
| 105 | doe_cap->offset = offset; |
| 106 | |
| 107 | if (intr && (msi_present(dev) || msix_present(dev))) { |
| 108 | doe_cap->cap.intr = intr; |
| 109 | doe_cap->cap.vec = vec; |
| 110 | } |
| 111 | |
| 112 | doe_cap->write_mbox = g_malloc0(PCI_DOE_DW_SIZE_MAX * DWORD_BYTE); |
| 113 | doe_cap->read_mbox = g_malloc0(PCI_DOE_DW_SIZE_MAX * DWORD_BYTE); |
| 114 | |
| 115 | pcie_doe_reset_mbox(doe_cap); |
| 116 | |
| 117 | doe_cap->protocols = protocols; |
| 118 | for (; protocols->vendor_id; protocols++) { |
| 119 | doe_cap->protocol_num++; |
| 120 | } |
| 121 | assert(doe_cap->protocol_num < PCI_DOE_PROTOCOL_NUM_MAX); |
| 122 | |
| 123 | /* Increment to allow for the discovery protocol */ |
| 124 | doe_cap->protocol_num++; |
| 125 | } |
| 126 | |
| 127 | void pcie_doe_fini(DOECap *doe_cap) |
| 128 | { |
| 129 | g_free(doe_cap->read_mbox); |
| 130 | g_free(doe_cap->write_mbox); |
| 131 | g_free(doe_cap); |
| 132 | } |
| 133 | |
| 134 | uint32_t pcie_doe_build_protocol(DOEProtocol *p) |
| 135 | { |
| 136 | return DATA_OBJ_BUILD_HEADER1(p->vendor_id, p->data_obj_type); |
| 137 | } |
| 138 | |
| 139 | void *pcie_doe_get_write_mbox_ptr(DOECap *doe_cap) |
| 140 | { |
| 141 | return doe_cap->write_mbox; |
| 142 | } |
| 143 | |
| 144 | /* |
| 145 | * Copy the response to read mailbox buffer |
| 146 | * This might be called in self-defined handle_request() if a DOE response is |
| 147 | * required in the corresponding protocol |
| 148 | */ |
| 149 | void pcie_doe_set_rsp(DOECap *doe_cap, void *rsp) |
| 150 | { |
| 151 | uint32_t len = pcie_doe_get_obj_len(rsp); |
| 152 | |
| 153 | memcpy(doe_cap->read_mbox + doe_cap->read_mbox_len, rsp, len * DWORD_BYTE); |
| 154 | doe_cap->read_mbox_len += len; |
| 155 | } |
| 156 | |
| 157 | uint32_t pcie_doe_get_obj_len(void *obj) |
| 158 | { |
| 159 | uint32_t len; |
| 160 | |
| 161 | if (!obj) { |
| 162 | return 0; |
| 163 | } |
| 164 | |
| 165 | /* Only lower 18 bits are valid */ |
| 166 | len = DATA_OBJ_LEN_MASK(((DOEHeader *)obj)->length); |
| 167 | |
| 168 | /* PCIe r6.0 Table 6.29: a value of 00000h indicates 2^18 DW */ |
| 169 | return (len) ? len : PCI_DOE_DW_SIZE_MAX; |
| 170 | } |
| 171 | |
| 172 | static void pcie_doe_irq_assert(DOECap *doe_cap) |
| 173 | { |
| 174 | PCIDevice *dev = doe_cap->pdev; |
| 175 | |
| 176 | if (doe_cap->cap.intr && doe_cap->ctrl.intr) { |
| 177 | if (doe_cap->status.intr) { |
| 178 | return; |
| 179 | } |
| 180 | doe_cap->status.intr = 1; |
| 181 | |
| 182 | if (msix_enabled(dev)) { |
| 183 | msix_notify(dev, doe_cap->cap.vec); |
| 184 | } else if (msi_enabled(dev)) { |
| 185 | msi_notify(dev, doe_cap->cap.vec); |
| 186 | } |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | static void pcie_doe_set_ready(DOECap *doe_cap, bool rdy) |
| 191 | { |
| 192 | doe_cap->status.ready = rdy; |
| 193 | |
| 194 | if (rdy) { |
| 195 | pcie_doe_irq_assert(doe_cap); |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | static void pcie_doe_set_error(DOECap *doe_cap, bool err) |
| 200 | { |
| 201 | doe_cap->status.error = err; |
| 202 | |
| 203 | if (err) { |
| 204 | pcie_doe_irq_assert(doe_cap); |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | /* |
| 209 | * Check incoming request in write_mbox for protocol format |
| 210 | */ |
| 211 | static void pcie_doe_prepare_rsp(DOECap *doe_cap) |
| 212 | { |
| 213 | bool success = false; |
| 214 | int p; |
| 215 | bool (*handle_request)(DOECap *) = NULL; |
| 216 | |
| 217 | if (doe_cap->status.error) { |
| 218 | return; |
| 219 | } |
| 220 | |
| 221 | if (doe_cap->write_mbox[0] == |
| 222 | DATA_OBJ_BUILD_HEADER1(PCI_VENDOR_ID_PCI_SIG, PCI_SIG_DOE_DISCOVERY)) { |
| 223 | handle_request = pcie_doe_discovery; |
| 224 | } else { |
| 225 | for (p = 0; p < doe_cap->protocol_num - 1; p++) { |
| 226 | if (doe_cap->write_mbox[0] == |
| 227 | pcie_doe_build_protocol(&doe_cap->protocols[p])) { |
| 228 | handle_request = doe_cap->protocols[p].handle_request; |
| 229 | break; |
| 230 | } |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | /* |
| 235 | * PCIe r6 DOE 6.30.1: |
| 236 | * If the number of DW transferred does not match the |
| 237 | * indicated Length for a data object, then the |
| 238 | * data object must be silently discarded. |
| 239 | */ |
| 240 | if (handle_request && (doe_cap->write_mbox_len == |
| 241 | pcie_doe_get_obj_len(pcie_doe_get_write_mbox_ptr(doe_cap)))) { |
| 242 | success = handle_request(doe_cap); |
| 243 | } |
| 244 | |
| 245 | if (success) { |
| 246 | pcie_doe_set_ready(doe_cap, 1); |
| 247 | } else { |
| 248 | pcie_doe_reset_mbox(doe_cap); |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | /* |
| 253 | * Read from DOE config space. |
| 254 | * Return false if the address not within DOE_CAP range. |
| 255 | */ |
| 256 | bool pcie_doe_read_config(DOECap *doe_cap, uint32_t addr, int size, |
| 257 | uint32_t *buf) |
| 258 | { |
| 259 | uint32_t shift; |
| 260 | uint16_t doe_offset = doe_cap->offset; |
| 261 | |
| 262 | if (!range_covers_byte(doe_offset + PCI_EXP_DOE_CAP, |
| 263 | PCI_DOE_SIZEOF - 4, addr)) { |
| 264 | return false; |
| 265 | } |
| 266 | |
| 267 | addr -= doe_offset; |
| 268 | *buf = 0; |
| 269 | |
| 270 | if (range_covers_byte(PCI_EXP_DOE_CAP, DWORD_BYTE, addr)) { |
| 271 | *buf = FIELD_DP32(*buf, PCI_DOE_CAP_REG, INTR_SUPP, |
| 272 | doe_cap->cap.intr); |
| 273 | *buf = FIELD_DP32(*buf, PCI_DOE_CAP_REG, DOE_INTR_MSG_NUM, |
| 274 | doe_cap->cap.vec); |
| 275 | } else if (range_covers_byte(PCI_EXP_DOE_CTRL, DWORD_BYTE, addr)) { |
| 276 | /* Must return ABORT=0 and GO=0 */ |
| 277 | *buf = FIELD_DP32(*buf, PCI_DOE_CAP_CONTROL, DOE_INTR_EN, |
| 278 | doe_cap->ctrl.intr); |
| 279 | } else if (range_covers_byte(PCI_EXP_DOE_STATUS, DWORD_BYTE, addr)) { |
| 280 | *buf = FIELD_DP32(*buf, PCI_DOE_CAP_STATUS, DOE_BUSY, |
| 281 | doe_cap->status.busy); |
| 282 | *buf = FIELD_DP32(*buf, PCI_DOE_CAP_STATUS, DOE_INTR_STATUS, |
| 283 | doe_cap->status.intr); |
| 284 | *buf = FIELD_DP32(*buf, PCI_DOE_CAP_STATUS, DOE_ERROR, |
| 285 | doe_cap->status.error); |
| 286 | *buf = FIELD_DP32(*buf, PCI_DOE_CAP_STATUS, DATA_OBJ_RDY, |
| 287 | doe_cap->status.ready); |
| 288 | /* Mailbox should be DW accessed */ |
| 289 | } else if (addr == PCI_EXP_DOE_RD_DATA_MBOX && size == DWORD_BYTE) { |
| 290 | if (doe_cap->status.ready && !doe_cap->status.error) { |
| 291 | *buf = doe_cap->read_mbox[doe_cap->read_mbox_idx]; |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | /* Process Alignment */ |
| 296 | shift = addr % DWORD_BYTE; |
| 297 | *buf = extract32(*buf, shift * 8, size * 8); |
| 298 | |
| 299 | return true; |
| 300 | } |
| 301 | |
| 302 | /* |
| 303 | * Write to DOE config space. |
| 304 | * Return if the address not within DOE_CAP range or receives an abort |
| 305 | */ |
| 306 | void pcie_doe_write_config(DOECap *doe_cap, |
| 307 | uint32_t addr, uint32_t val, int size) |
| 308 | { |
| 309 | uint16_t doe_offset = doe_cap->offset; |
| 310 | uint32_t shift; |
| 311 | |
| 312 | if (!range_covers_byte(doe_offset + PCI_EXP_DOE_CAP, |
| 313 | PCI_DOE_SIZEOF - 4, addr)) { |
| 314 | return; |
| 315 | } |
| 316 | |
| 317 | /* Process Alignment */ |
| 318 | shift = addr % DWORD_BYTE; |
| 319 | addr -= (doe_offset + shift); |
| 320 | val = deposit32(val, shift * 8, size * 8, val); |
| 321 | |
| 322 | switch (addr) { |
| 323 | case PCI_EXP_DOE_CTRL: |
| 324 | if (FIELD_EX32(val, PCI_DOE_CAP_CONTROL, DOE_ABORT)) { |
| 325 | pcie_doe_set_ready(doe_cap, 0); |
| 326 | pcie_doe_set_error(doe_cap, 0); |
| 327 | pcie_doe_reset_mbox(doe_cap); |
| 328 | return; |
| 329 | } |
| 330 | |
| 331 | if (FIELD_EX32(val, PCI_DOE_CAP_CONTROL, DOE_GO)) { |
| 332 | pcie_doe_prepare_rsp(doe_cap); |
| 333 | } |
| 334 | |
| 335 | if (FIELD_EX32(val, PCI_DOE_CAP_CONTROL, DOE_INTR_EN)) { |
| 336 | doe_cap->ctrl.intr = 1; |
| 337 | /* Clear interrupt bit located within the first byte */ |
| 338 | } else if (shift == 0) { |
| 339 | doe_cap->ctrl.intr = 0; |
| 340 | } |
| 341 | break; |
| 342 | case PCI_EXP_DOE_STATUS: |
| 343 | if (FIELD_EX32(val, PCI_DOE_CAP_STATUS, DOE_INTR_STATUS)) { |
| 344 | doe_cap->status.intr = 0; |
| 345 | } |
| 346 | break; |
| 347 | case PCI_EXP_DOE_RD_DATA_MBOX: |
| 348 | /* Mailbox should be DW accessed */ |
| 349 | if (size != DWORD_BYTE) { |
| 350 | return; |
| 351 | } |
| 352 | doe_cap->read_mbox_idx++; |
| 353 | if (doe_cap->read_mbox_idx == doe_cap->read_mbox_len) { |
| 354 | pcie_doe_reset_mbox(doe_cap); |
| 355 | pcie_doe_set_ready(doe_cap, 0); |
| 356 | } else if (doe_cap->read_mbox_idx > doe_cap->read_mbox_len) { |
| 357 | /* Underflow */ |
| 358 | pcie_doe_set_error(doe_cap, 1); |
| 359 | } |
| 360 | break; |
| 361 | case PCI_EXP_DOE_WR_DATA_MBOX: |
| 362 | /* Mailbox should be DW accessed */ |
| 363 | if (size != DWORD_BYTE) { |
| 364 | return; |
| 365 | } |
| 366 | if (doe_cap->write_mbox_len < PCI_DOE_DW_SIZE_MAX) { |
| 367 | doe_cap->write_mbox[doe_cap->write_mbox_len] = val; |
| 368 | doe_cap->write_mbox_len++; |
| 369 | } else { |
| 370 | qemu_log_mask(LOG_GUEST_ERROR, |
| 371 | "Mailbox write length (%d) overflow\n", |
| 372 | doe_cap->write_mbox_len); |
| 373 | /* |
| 374 | * Too much data has been written, it can't |
| 375 | * "match the Length indicated in DOE Data Object Header 2" |
| 376 | * so we drop the entire object. |
| 377 | */ |
| 378 | pcie_doe_reset_write_mbox(doe_cap); |
| 379 | } |
| 380 | break; |
| 381 | case PCI_EXP_DOE_CAP: |
| 382 | /* fallthrough */ |
| 383 | default: |
| 384 | break; |
| 385 | } |
| 386 | } |