]>
Commit | Line | Data |
---|---|---|
5fb52f6c HCK |
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 | } |