qemu

Форк
0
/
pcie_doe.c 
367 строк · 10.0 Кб
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
}
368

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.