qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

pcie_doe.c (10290B)


      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_mbox(DOECap *st)
     82 {
     83     st->read_mbox_idx = 0;
     84     st->read_mbox_len = 0;
     85     st->write_mbox_len = 0;
     86 
     87     memset(st->read_mbox, 0, PCI_DOE_DW_SIZE_MAX * DWORD_BYTE);
     88     memset(st->write_mbox, 0, PCI_DOE_DW_SIZE_MAX * DWORD_BYTE);
     89 }
     90 
     91 void pcie_doe_init(PCIDevice *dev, DOECap *doe_cap, uint16_t offset,
     92                    DOEProtocol *protocols, bool intr, uint16_t vec)
     93 {
     94     pcie_add_capability(dev, PCI_EXT_CAP_ID_DOE, 0x1, offset,
     95                         PCI_DOE_SIZEOF);
     96 
     97     doe_cap->pdev = dev;
     98     doe_cap->offset = offset;
     99 
    100     if (intr && (msi_present(dev) || msix_present(dev))) {
    101         doe_cap->cap.intr = intr;
    102         doe_cap->cap.vec = vec;
    103     }
    104 
    105     doe_cap->write_mbox = g_malloc0(PCI_DOE_DW_SIZE_MAX * DWORD_BYTE);
    106     doe_cap->read_mbox = g_malloc0(PCI_DOE_DW_SIZE_MAX * DWORD_BYTE);
    107 
    108     pcie_doe_reset_mbox(doe_cap);
    109 
    110     doe_cap->protocols = protocols;
    111     for (; protocols->vendor_id; protocols++) {
    112         doe_cap->protocol_num++;
    113     }
    114     assert(doe_cap->protocol_num < PCI_DOE_PROTOCOL_NUM_MAX);
    115 
    116     /* Increment to allow for the discovery protocol */
    117     doe_cap->protocol_num++;
    118 }
    119 
    120 void pcie_doe_fini(DOECap *doe_cap)
    121 {
    122     g_free(doe_cap->read_mbox);
    123     g_free(doe_cap->write_mbox);
    124     g_free(doe_cap);
    125 }
    126 
    127 uint32_t pcie_doe_build_protocol(DOEProtocol *p)
    128 {
    129     return DATA_OBJ_BUILD_HEADER1(p->vendor_id, p->data_obj_type);
    130 }
    131 
    132 void *pcie_doe_get_write_mbox_ptr(DOECap *doe_cap)
    133 {
    134     return doe_cap->write_mbox;
    135 }
    136 
    137 /*
    138  * Copy the response to read mailbox buffer
    139  * This might be called in self-defined handle_request() if a DOE response is
    140  * required in the corresponding protocol
    141  */
    142 void pcie_doe_set_rsp(DOECap *doe_cap, void *rsp)
    143 {
    144     uint32_t len = pcie_doe_get_obj_len(rsp);
    145 
    146     memcpy(doe_cap->read_mbox + doe_cap->read_mbox_len, rsp, len * DWORD_BYTE);
    147     doe_cap->read_mbox_len += len;
    148 }
    149 
    150 uint32_t pcie_doe_get_obj_len(void *obj)
    151 {
    152     uint32_t len;
    153 
    154     if (!obj) {
    155         return 0;
    156     }
    157 
    158     /* Only lower 18 bits are valid */
    159     len = DATA_OBJ_LEN_MASK(((DOEHeader *)obj)->length);
    160 
    161     /* PCIe r6.0 Table 6.29: a value of 00000h indicates 2^18 DW */
    162     return (len) ? len : PCI_DOE_DW_SIZE_MAX;
    163 }
    164 
    165 static void pcie_doe_irq_assert(DOECap *doe_cap)
    166 {
    167     PCIDevice *dev = doe_cap->pdev;
    168 
    169     if (doe_cap->cap.intr && doe_cap->ctrl.intr) {
    170         if (doe_cap->status.intr) {
    171             return;
    172         }
    173         doe_cap->status.intr = 1;
    174 
    175         if (msix_enabled(dev)) {
    176             msix_notify(dev, doe_cap->cap.vec);
    177         } else if (msi_enabled(dev)) {
    178             msi_notify(dev, doe_cap->cap.vec);
    179         }
    180     }
    181 }
    182 
    183 static void pcie_doe_set_ready(DOECap *doe_cap, bool rdy)
    184 {
    185     doe_cap->status.ready = rdy;
    186 
    187     if (rdy) {
    188         pcie_doe_irq_assert(doe_cap);
    189     }
    190 }
    191 
    192 static void pcie_doe_set_error(DOECap *doe_cap, bool err)
    193 {
    194     doe_cap->status.error = err;
    195 
    196     if (err) {
    197         pcie_doe_irq_assert(doe_cap);
    198     }
    199 }
    200 
    201 /*
    202  * Check incoming request in write_mbox for protocol format
    203  */
    204 static void pcie_doe_prepare_rsp(DOECap *doe_cap)
    205 {
    206     bool success = false;
    207     int p;
    208     bool (*handle_request)(DOECap *) = NULL;
    209 
    210     if (doe_cap->status.error) {
    211         return;
    212     }
    213 
    214     if (doe_cap->write_mbox[0] ==
    215         DATA_OBJ_BUILD_HEADER1(PCI_VENDOR_ID_PCI_SIG, PCI_SIG_DOE_DISCOVERY)) {
    216         handle_request = pcie_doe_discovery;
    217     } else {
    218         for (p = 0; p < doe_cap->protocol_num - 1; p++) {
    219             if (doe_cap->write_mbox[0] ==
    220                 pcie_doe_build_protocol(&doe_cap->protocols[p])) {
    221                 handle_request = doe_cap->protocols[p].handle_request;
    222                 break;
    223             }
    224         }
    225     }
    226 
    227     /*
    228      * PCIe r6 DOE 6.30.1:
    229      * If the number of DW transferred does not match the
    230      * indicated Length for a data object, then the
    231      * data object must be silently discarded.
    232      */
    233     if (handle_request && (doe_cap->write_mbox_len ==
    234         pcie_doe_get_obj_len(pcie_doe_get_write_mbox_ptr(doe_cap)))) {
    235         success = handle_request(doe_cap);
    236     }
    237 
    238     if (success) {
    239         pcie_doe_set_ready(doe_cap, 1);
    240     } else {
    241         pcie_doe_reset_mbox(doe_cap);
    242     }
    243 }
    244 
    245 /*
    246  * Read from DOE config space.
    247  * Return false if the address not within DOE_CAP range.
    248  */
    249 bool pcie_doe_read_config(DOECap *doe_cap, uint32_t addr, int size,
    250                           uint32_t *buf)
    251 {
    252     uint32_t shift;
    253     uint16_t doe_offset = doe_cap->offset;
    254 
    255     if (!range_covers_byte(doe_offset + PCI_EXP_DOE_CAP,
    256                            PCI_DOE_SIZEOF - 4, addr)) {
    257         return false;
    258     }
    259 
    260     addr -= doe_offset;
    261     *buf = 0;
    262 
    263     if (range_covers_byte(PCI_EXP_DOE_CAP, DWORD_BYTE, addr)) {
    264         *buf = FIELD_DP32(*buf, PCI_DOE_CAP_REG, INTR_SUPP,
    265                           doe_cap->cap.intr);
    266         *buf = FIELD_DP32(*buf, PCI_DOE_CAP_REG, DOE_INTR_MSG_NUM,
    267                           doe_cap->cap.vec);
    268     } else if (range_covers_byte(PCI_EXP_DOE_CTRL, DWORD_BYTE, addr)) {
    269         /* Must return ABORT=0 and GO=0 */
    270         *buf = FIELD_DP32(*buf, PCI_DOE_CAP_CONTROL, DOE_INTR_EN,
    271                           doe_cap->ctrl.intr);
    272     } else if (range_covers_byte(PCI_EXP_DOE_STATUS, DWORD_BYTE, addr)) {
    273         *buf = FIELD_DP32(*buf, PCI_DOE_CAP_STATUS, DOE_BUSY,
    274                           doe_cap->status.busy);
    275         *buf = FIELD_DP32(*buf, PCI_DOE_CAP_STATUS, DOE_INTR_STATUS,
    276                           doe_cap->status.intr);
    277         *buf = FIELD_DP32(*buf, PCI_DOE_CAP_STATUS, DOE_ERROR,
    278                           doe_cap->status.error);
    279         *buf = FIELD_DP32(*buf, PCI_DOE_CAP_STATUS, DATA_OBJ_RDY,
    280                           doe_cap->status.ready);
    281     /* Mailbox should be DW accessed */
    282     } else if (addr == PCI_EXP_DOE_RD_DATA_MBOX && size == DWORD_BYTE) {
    283         if (doe_cap->status.ready && !doe_cap->status.error) {
    284             *buf = doe_cap->read_mbox[doe_cap->read_mbox_idx];
    285         }
    286     }
    287 
    288     /* Process Alignment */
    289     shift = addr % DWORD_BYTE;
    290     *buf = extract32(*buf, shift * 8, size * 8);
    291 
    292     return true;
    293 }
    294 
    295 /*
    296  * Write to DOE config space.
    297  * Return if the address not within DOE_CAP range or receives an abort
    298  */
    299 void pcie_doe_write_config(DOECap *doe_cap,
    300                            uint32_t addr, uint32_t val, int size)
    301 {
    302     uint16_t doe_offset = doe_cap->offset;
    303     uint32_t shift;
    304 
    305     if (!range_covers_byte(doe_offset + PCI_EXP_DOE_CAP,
    306                            PCI_DOE_SIZEOF - 4, addr)) {
    307         return;
    308     }
    309 
    310     /* Process Alignment */
    311     shift = addr % DWORD_BYTE;
    312     addr -= (doe_offset + shift);
    313     val = deposit32(val, shift * 8, size * 8, val);
    314 
    315     switch (addr) {
    316     case PCI_EXP_DOE_CTRL:
    317         if (FIELD_EX32(val, PCI_DOE_CAP_CONTROL, DOE_ABORT)) {
    318             pcie_doe_set_ready(doe_cap, 0);
    319             pcie_doe_set_error(doe_cap, 0);
    320             pcie_doe_reset_mbox(doe_cap);
    321             return;
    322         }
    323 
    324         if (FIELD_EX32(val, PCI_DOE_CAP_CONTROL, DOE_GO)) {
    325             pcie_doe_prepare_rsp(doe_cap);
    326         }
    327 
    328         if (FIELD_EX32(val, PCI_DOE_CAP_CONTROL, DOE_INTR_EN)) {
    329             doe_cap->ctrl.intr = 1;
    330         /* Clear interrupt bit located within the first byte */
    331         } else if (shift == 0) {
    332             doe_cap->ctrl.intr = 0;
    333         }
    334         break;
    335     case PCI_EXP_DOE_STATUS:
    336         if (FIELD_EX32(val, PCI_DOE_CAP_STATUS, DOE_INTR_STATUS)) {
    337             doe_cap->status.intr = 0;
    338         }
    339         break;
    340     case PCI_EXP_DOE_RD_DATA_MBOX:
    341         /* Mailbox should be DW accessed */
    342         if (size != DWORD_BYTE) {
    343             return;
    344         }
    345         doe_cap->read_mbox_idx++;
    346         if (doe_cap->read_mbox_idx == doe_cap->read_mbox_len) {
    347             pcie_doe_reset_mbox(doe_cap);
    348             pcie_doe_set_ready(doe_cap, 0);
    349         } else if (doe_cap->read_mbox_idx > doe_cap->read_mbox_len) {
    350             /* Underflow */
    351             pcie_doe_set_error(doe_cap, 1);
    352         }
    353         break;
    354     case PCI_EXP_DOE_WR_DATA_MBOX:
    355         /* Mailbox should be DW accessed */
    356         if (size != DWORD_BYTE) {
    357             return;
    358         }
    359         doe_cap->write_mbox[doe_cap->write_mbox_len] = val;
    360         doe_cap->write_mbox_len++;
    361         break;
    362     case PCI_EXP_DOE_CAP:
    363         /* fallthrough */
    364     default:
    365         break;
    366     }
    367 }