]>
Commit | Line | Data |
---|---|---|
c4efe1ca AL |
1 | /* |
2 | * libqos PCI bindings | |
3 | * | |
4 | * Copyright IBM, Corp. 2012-2013 | |
5 | * | |
6 | * Authors: | |
7 | * Anthony Liguori <[email protected]> | |
8 | * | |
9 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
10 | * See the COPYING file in the top-level directory. | |
11 | */ | |
12 | ||
681c28a3 | 13 | #include "qemu/osdep.h" |
c4efe1ca AL |
14 | #include "libqos/pci.h" |
15 | ||
16 | #include "hw/pci/pci_regs.h" | |
b8cc4d02 | 17 | #include "qemu/host-utils.h" |
85af0057 | 18 | #include "libqos/qgraph.h" |
c4efe1ca | 19 | |
c4efe1ca AL |
20 | void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id, |
21 | void (*func)(QPCIDevice *dev, int devfn, void *data), | |
22 | void *data) | |
23 | { | |
24 | int slot; | |
25 | ||
26 | for (slot = 0; slot < 32; slot++) { | |
27 | int fn; | |
28 | ||
29 | for (fn = 0; fn < 8; fn++) { | |
30 | QPCIDevice *dev; | |
31 | ||
32 | dev = qpci_device_find(bus, QPCI_DEVFN(slot, fn)); | |
33 | if (!dev) { | |
34 | continue; | |
35 | } | |
36 | ||
37 | if (vendor_id != -1 && | |
38 | qpci_config_readw(dev, PCI_VENDOR_ID) != vendor_id) { | |
ea53854a | 39 | g_free(dev); |
c4efe1ca AL |
40 | continue; |
41 | } | |
42 | ||
43 | if (device_id != -1 && | |
44 | qpci_config_readw(dev, PCI_DEVICE_ID) != device_id) { | |
ea53854a | 45 | g_free(dev); |
c4efe1ca AL |
46 | continue; |
47 | } | |
48 | ||
49 | func(dev, QPCI_DEVFN(slot, fn), data); | |
50 | } | |
51 | } | |
52 | } | |
53 | ||
92bbafc7 EGE |
54 | bool qpci_has_buggy_msi(QPCIDevice *dev) |
55 | { | |
56 | return dev->bus->has_buggy_msi; | |
57 | } | |
58 | ||
59 | bool qpci_check_buggy_msi(QPCIDevice *dev) | |
60 | { | |
61 | if (qpci_has_buggy_msi(dev)) { | |
62 | g_test_skip("Skipping due to incomplete support for MSI"); | |
63 | return true; | |
64 | } | |
65 | return false; | |
66 | } | |
67 | ||
85af0057 EGE |
68 | static void qpci_device_set(QPCIDevice *dev, QPCIBus *bus, int devfn) |
69 | { | |
70 | g_assert(dev); | |
71 | ||
72 | dev->bus = bus; | |
73 | dev->devfn = devfn; | |
74 | } | |
75 | ||
c4efe1ca AL |
76 | QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn) |
77 | { | |
78 | QPCIDevice *dev; | |
79 | ||
80 | dev = g_malloc0(sizeof(*dev)); | |
85af0057 | 81 | qpci_device_set(dev, bus, devfn); |
c4efe1ca AL |
82 | |
83 | if (qpci_config_readw(dev, PCI_VENDOR_ID) == 0xFFFF) { | |
84 | g_free(dev); | |
85 | return NULL; | |
86 | } | |
87 | ||
88 | return dev; | |
89 | } | |
90 | ||
85af0057 EGE |
91 | void qpci_device_init(QPCIDevice *dev, QPCIBus *bus, QPCIAddress *addr) |
92 | { | |
93 | uint16_t vendor_id, device_id; | |
94 | ||
95 | qpci_device_set(dev, bus, addr->devfn); | |
96 | vendor_id = qpci_config_readw(dev, PCI_VENDOR_ID); | |
97 | device_id = qpci_config_readw(dev, PCI_DEVICE_ID); | |
98 | g_assert(!addr->vendor_id || vendor_id == addr->vendor_id); | |
99 | g_assert(!addr->device_id || device_id == addr->device_id); | |
100 | } | |
101 | ||
c4efe1ca AL |
102 | void qpci_device_enable(QPCIDevice *dev) |
103 | { | |
104 | uint16_t cmd; | |
105 | ||
106 | /* FIXME -- does this need to be a bus callout? */ | |
107 | cmd = qpci_config_readw(dev, PCI_COMMAND); | |
9f0332b8 | 108 | cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; |
c4efe1ca | 109 | qpci_config_writew(dev, PCI_COMMAND, cmd); |
96d6d3ba JS |
110 | |
111 | /* Verify the bits are now set. */ | |
112 | cmd = qpci_config_readw(dev, PCI_COMMAND); | |
113 | g_assert_cmphex(cmd & PCI_COMMAND_IO, ==, PCI_COMMAND_IO); | |
114 | g_assert_cmphex(cmd & PCI_COMMAND_MEMORY, ==, PCI_COMMAND_MEMORY); | |
115 | g_assert_cmphex(cmd & PCI_COMMAND_MASTER, ==, PCI_COMMAND_MASTER); | |
c4efe1ca AL |
116 | } |
117 | ||
58368113 MM |
118 | uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id) |
119 | { | |
120 | uint8_t cap; | |
121 | uint8_t addr = qpci_config_readb(dev, PCI_CAPABILITY_LIST); | |
122 | ||
123 | do { | |
124 | cap = qpci_config_readb(dev, addr); | |
125 | if (cap != id) { | |
126 | addr = qpci_config_readb(dev, addr + PCI_CAP_LIST_NEXT); | |
127 | } | |
128 | } while (cap != id && addr != 0); | |
129 | ||
130 | return addr; | |
131 | } | |
132 | ||
133 | void qpci_msix_enable(QPCIDevice *dev) | |
134 | { | |
135 | uint8_t addr; | |
136 | uint16_t val; | |
137 | uint32_t table; | |
138 | uint8_t bir_table; | |
139 | uint8_t bir_pba; | |
58368113 MM |
140 | |
141 | addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); | |
142 | g_assert_cmphex(addr, !=, 0); | |
143 | ||
144 | val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS); | |
145 | qpci_config_writew(dev, addr + PCI_MSIX_FLAGS, val | PCI_MSIX_FLAGS_ENABLE); | |
146 | ||
147 | table = qpci_config_readl(dev, addr + PCI_MSIX_TABLE); | |
148 | bir_table = table & PCI_MSIX_FLAGS_BIRMASK; | |
b4ba67d9 DG |
149 | dev->msix_table_bar = qpci_iomap(dev, bir_table, NULL); |
150 | dev->msix_table_off = table & ~PCI_MSIX_FLAGS_BIRMASK; | |
58368113 MM |
151 | |
152 | table = qpci_config_readl(dev, addr + PCI_MSIX_PBA); | |
153 | bir_pba = table & PCI_MSIX_FLAGS_BIRMASK; | |
154 | if (bir_pba != bir_table) { | |
b4ba67d9 | 155 | dev->msix_pba_bar = qpci_iomap(dev, bir_pba, NULL); |
4446158a TH |
156 | } else { |
157 | dev->msix_pba_bar = dev->msix_table_bar; | |
58368113 | 158 | } |
b4ba67d9 | 159 | dev->msix_pba_off = table & ~PCI_MSIX_FLAGS_BIRMASK; |
58368113 | 160 | |
58368113 MM |
161 | dev->msix_enabled = true; |
162 | } | |
163 | ||
164 | void qpci_msix_disable(QPCIDevice *dev) | |
165 | { | |
166 | uint8_t addr; | |
167 | uint16_t val; | |
168 | ||
169 | g_assert(dev->msix_enabled); | |
170 | addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); | |
171 | g_assert_cmphex(addr, !=, 0); | |
172 | val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS); | |
173 | qpci_config_writew(dev, addr + PCI_MSIX_FLAGS, | |
174 | val & ~PCI_MSIX_FLAGS_ENABLE); | |
175 | ||
4446158a TH |
176 | if (dev->msix_pba_bar.addr != dev->msix_table_bar.addr) { |
177 | qpci_iounmap(dev, dev->msix_pba_bar); | |
178 | } | |
b4ba67d9 | 179 | qpci_iounmap(dev, dev->msix_table_bar); |
4446158a | 180 | |
58368113 | 181 | dev->msix_enabled = 0; |
b4ba67d9 DG |
182 | dev->msix_table_off = 0; |
183 | dev->msix_pba_off = 0; | |
58368113 MM |
184 | } |
185 | ||
186 | bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry) | |
187 | { | |
188 | uint32_t pba_entry; | |
189 | uint8_t bit_n = entry % 32; | |
b4ba67d9 | 190 | uint64_t off = (entry / 32) * PCI_MSIX_ENTRY_SIZE / 4; |
58368113 MM |
191 | |
192 | g_assert(dev->msix_enabled); | |
b4ba67d9 DG |
193 | pba_entry = qpci_io_readl(dev, dev->msix_pba_bar, dev->msix_pba_off + off); |
194 | qpci_io_writel(dev, dev->msix_pba_bar, dev->msix_pba_off + off, | |
195 | pba_entry & ~(1 << bit_n)); | |
58368113 MM |
196 | return (pba_entry & (1 << bit_n)) != 0; |
197 | } | |
198 | ||
199 | bool qpci_msix_masked(QPCIDevice *dev, uint16_t entry) | |
200 | { | |
201 | uint8_t addr; | |
202 | uint16_t val; | |
b4ba67d9 | 203 | uint64_t vector_off = dev->msix_table_off + entry * PCI_MSIX_ENTRY_SIZE; |
58368113 MM |
204 | |
205 | g_assert(dev->msix_enabled); | |
206 | addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); | |
207 | g_assert_cmphex(addr, !=, 0); | |
208 | val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS); | |
209 | ||
210 | if (val & PCI_MSIX_FLAGS_MASKALL) { | |
211 | return true; | |
212 | } else { | |
b4ba67d9 DG |
213 | return (qpci_io_readl(dev, dev->msix_table_bar, |
214 | vector_off + PCI_MSIX_ENTRY_VECTOR_CTRL) | |
215 | & PCI_MSIX_ENTRY_CTRL_MASKBIT) != 0; | |
58368113 MM |
216 | } |
217 | } | |
218 | ||
219 | uint16_t qpci_msix_table_size(QPCIDevice *dev) | |
220 | { | |
221 | uint8_t addr; | |
222 | uint16_t control; | |
223 | ||
224 | addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); | |
225 | g_assert_cmphex(addr, !=, 0); | |
226 | ||
227 | control = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS); | |
228 | return (control & PCI_MSIX_FLAGS_QSIZE) + 1; | |
229 | } | |
230 | ||
c4efe1ca AL |
231 | uint8_t qpci_config_readb(QPCIDevice *dev, uint8_t offset) |
232 | { | |
233 | return dev->bus->config_readb(dev->bus, dev->devfn, offset); | |
234 | } | |
235 | ||
236 | uint16_t qpci_config_readw(QPCIDevice *dev, uint8_t offset) | |
237 | { | |
238 | return dev->bus->config_readw(dev->bus, dev->devfn, offset); | |
239 | } | |
240 | ||
241 | uint32_t qpci_config_readl(QPCIDevice *dev, uint8_t offset) | |
242 | { | |
243 | return dev->bus->config_readl(dev->bus, dev->devfn, offset); | |
244 | } | |
245 | ||
246 | ||
247 | void qpci_config_writeb(QPCIDevice *dev, uint8_t offset, uint8_t value) | |
248 | { | |
249 | dev->bus->config_writeb(dev->bus, dev->devfn, offset, value); | |
250 | } | |
251 | ||
252 | void qpci_config_writew(QPCIDevice *dev, uint8_t offset, uint16_t value) | |
253 | { | |
254 | dev->bus->config_writew(dev->bus, dev->devfn, offset, value); | |
255 | } | |
256 | ||
257 | void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value) | |
258 | { | |
ad489e93 | 259 | dev->bus->config_writel(dev->bus, dev->devfn, offset, value); |
c4efe1ca AL |
260 | } |
261 | ||
b4ba67d9 | 262 | uint8_t qpci_io_readb(QPCIDevice *dev, QPCIBar token, uint64_t off) |
c4efe1ca | 263 | { |
b4ba67d9 DG |
264 | if (token.addr < QPCI_PIO_LIMIT) { |
265 | return dev->bus->pio_readb(dev->bus, token.addr + off); | |
a795fc08 | 266 | } else { |
352d664c | 267 | uint8_t val; |
b4ba67d9 | 268 | dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); |
352d664c | 269 | return val; |
a795fc08 | 270 | } |
c4efe1ca AL |
271 | } |
272 | ||
b4ba67d9 | 273 | uint16_t qpci_io_readw(QPCIDevice *dev, QPCIBar token, uint64_t off) |
c4efe1ca | 274 | { |
b4ba67d9 DG |
275 | if (token.addr < QPCI_PIO_LIMIT) { |
276 | return dev->bus->pio_readw(dev->bus, token.addr + off); | |
a795fc08 | 277 | } else { |
352d664c | 278 | uint16_t val; |
b4ba67d9 | 279 | dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); |
352d664c | 280 | return le16_to_cpu(val); |
a795fc08 | 281 | } |
c4efe1ca AL |
282 | } |
283 | ||
b4ba67d9 | 284 | uint32_t qpci_io_readl(QPCIDevice *dev, QPCIBar token, uint64_t off) |
c4efe1ca | 285 | { |
b4ba67d9 DG |
286 | if (token.addr < QPCI_PIO_LIMIT) { |
287 | return dev->bus->pio_readl(dev->bus, token.addr + off); | |
a795fc08 | 288 | } else { |
352d664c | 289 | uint32_t val; |
b4ba67d9 | 290 | dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); |
352d664c | 291 | return le32_to_cpu(val); |
a795fc08 DG |
292 | } |
293 | } | |
c4efe1ca | 294 | |
b4ba67d9 | 295 | uint64_t qpci_io_readq(QPCIDevice *dev, QPCIBar token, uint64_t off) |
f775f45a | 296 | { |
b4ba67d9 DG |
297 | if (token.addr < QPCI_PIO_LIMIT) { |
298 | return dev->bus->pio_readq(dev->bus, token.addr + off); | |
f775f45a DG |
299 | } else { |
300 | uint64_t val; | |
b4ba67d9 | 301 | dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); |
f775f45a DG |
302 | return le64_to_cpu(val); |
303 | } | |
304 | } | |
305 | ||
b4ba67d9 DG |
306 | void qpci_io_writeb(QPCIDevice *dev, QPCIBar token, uint64_t off, |
307 | uint8_t value) | |
c4efe1ca | 308 | { |
b4ba67d9 DG |
309 | if (token.addr < QPCI_PIO_LIMIT) { |
310 | dev->bus->pio_writeb(dev->bus, token.addr + off, value); | |
a795fc08 | 311 | } else { |
b4ba67d9 | 312 | dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value)); |
a795fc08 | 313 | } |
c4efe1ca AL |
314 | } |
315 | ||
b4ba67d9 DG |
316 | void qpci_io_writew(QPCIDevice *dev, QPCIBar token, uint64_t off, |
317 | uint16_t value) | |
c4efe1ca | 318 | { |
b4ba67d9 DG |
319 | if (token.addr < QPCI_PIO_LIMIT) { |
320 | dev->bus->pio_writew(dev->bus, token.addr + off, value); | |
a795fc08 | 321 | } else { |
352d664c | 322 | value = cpu_to_le16(value); |
b4ba67d9 | 323 | dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value)); |
a795fc08 | 324 | } |
c4efe1ca AL |
325 | } |
326 | ||
b4ba67d9 DG |
327 | void qpci_io_writel(QPCIDevice *dev, QPCIBar token, uint64_t off, |
328 | uint32_t value) | |
c4efe1ca | 329 | { |
b4ba67d9 DG |
330 | if (token.addr < QPCI_PIO_LIMIT) { |
331 | dev->bus->pio_writel(dev->bus, token.addr + off, value); | |
a795fc08 | 332 | } else { |
352d664c | 333 | value = cpu_to_le32(value); |
b4ba67d9 | 334 | dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value)); |
a795fc08 | 335 | } |
c4efe1ca AL |
336 | } |
337 | ||
b4ba67d9 DG |
338 | void qpci_io_writeq(QPCIDevice *dev, QPCIBar token, uint64_t off, |
339 | uint64_t value) | |
f775f45a | 340 | { |
b4ba67d9 DG |
341 | if (token.addr < QPCI_PIO_LIMIT) { |
342 | dev->bus->pio_writeq(dev->bus, token.addr + off, value); | |
f775f45a DG |
343 | } else { |
344 | value = cpu_to_le64(value); | |
b4ba67d9 | 345 | dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value)); |
f775f45a DG |
346 | } |
347 | } | |
348 | ||
b4ba67d9 DG |
349 | void qpci_memread(QPCIDevice *dev, QPCIBar token, uint64_t off, |
350 | void *buf, size_t len) | |
9a84f889 | 351 | { |
b4ba67d9 DG |
352 | g_assert(token.addr >= QPCI_PIO_LIMIT); |
353 | dev->bus->memread(dev->bus, token.addr + off, buf, len); | |
9a84f889 DG |
354 | } |
355 | ||
b4ba67d9 DG |
356 | void qpci_memwrite(QPCIDevice *dev, QPCIBar token, uint64_t off, |
357 | const void *buf, size_t len) | |
9a84f889 | 358 | { |
b4ba67d9 DG |
359 | g_assert(token.addr >= QPCI_PIO_LIMIT); |
360 | dev->bus->memwrite(dev->bus, token.addr + off, buf, len); | |
9a84f889 DG |
361 | } |
362 | ||
b4ba67d9 | 363 | QPCIBar qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr) |
c4efe1ca | 364 | { |
b8cc4d02 DG |
365 | QPCIBus *bus = dev->bus; |
366 | static const int bar_reg_map[] = { | |
367 | PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2, | |
368 | PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5, | |
369 | }; | |
b4ba67d9 | 370 | QPCIBar bar; |
b8cc4d02 DG |
371 | int bar_reg; |
372 | uint32_t addr, size; | |
373 | uint32_t io_type; | |
374 | uint64_t loc; | |
375 | ||
376 | g_assert(barno >= 0 && barno <= 5); | |
377 | bar_reg = bar_reg_map[barno]; | |
378 | ||
379 | qpci_config_writel(dev, bar_reg, 0xFFFFFFFF); | |
380 | addr = qpci_config_readl(dev, bar_reg); | |
381 | ||
382 | io_type = addr & PCI_BASE_ADDRESS_SPACE; | |
383 | if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { | |
384 | addr &= PCI_BASE_ADDRESS_IO_MASK; | |
385 | } else { | |
386 | addr &= PCI_BASE_ADDRESS_MEM_MASK; | |
387 | } | |
388 | ||
389 | g_assert(addr); /* Must have *some* size bits */ | |
390 | ||
391 | size = 1U << ctz32(addr); | |
392 | if (sizeptr) { | |
393 | *sizeptr = size; | |
394 | } | |
395 | ||
396 | if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { | |
397 | loc = QEMU_ALIGN_UP(bus->pio_alloc_ptr, size); | |
398 | ||
399 | g_assert(loc >= bus->pio_alloc_ptr); | |
400 | g_assert(loc + size <= QPCI_PIO_LIMIT); /* Keep PIO below 64kiB */ | |
401 | ||
402 | bus->pio_alloc_ptr = loc + size; | |
403 | ||
404 | qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO); | |
405 | } else { | |
406 | loc = QEMU_ALIGN_UP(bus->mmio_alloc_ptr, size); | |
407 | ||
408 | /* Check for space */ | |
409 | g_assert(loc >= bus->mmio_alloc_ptr); | |
410 | g_assert(loc + size <= bus->mmio_limit); | |
411 | ||
412 | bus->mmio_alloc_ptr = loc + size; | |
413 | ||
414 | qpci_config_writel(dev, bar_reg, loc); | |
415 | } | |
416 | ||
b4ba67d9 DG |
417 | bar.addr = loc; |
418 | return bar; | |
c4efe1ca AL |
419 | } |
420 | ||
b4ba67d9 | 421 | void qpci_iounmap(QPCIDevice *dev, QPCIBar bar) |
c4efe1ca | 422 | { |
b8cc4d02 | 423 | /* FIXME */ |
c4efe1ca AL |
424 | } |
425 | ||
b4ba67d9 | 426 | QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr) |
a7b85b60 | 427 | { |
b4ba67d9 DG |
428 | QPCIBar bar = { .addr = addr }; |
429 | return bar; | |
a7b85b60 | 430 | } |
85af0057 EGE |
431 | |
432 | void add_qpci_address(QOSGraphEdgeOptions *opts, QPCIAddress *addr) | |
433 | { | |
434 | g_assert(addr); | |
435 | g_assert(opts); | |
436 | ||
437 | opts->arg = addr; | |
438 | opts->size_arg = sizeof(QPCIAddress); | |
439 | } |