]>
Commit | Line | Data |
---|---|---|
311e666a MM |
1 | /* |
2 | * libqos virtio PCI driver | |
3 | * | |
4 | * Copyright (c) 2014 Marc Marí | |
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 | ||
681c28a3 | 10 | #include "qemu/osdep.h" |
311e666a MM |
11 | #include "libqtest.h" |
12 | #include "libqos/virtio.h" | |
13 | #include "libqos/virtio-pci.h" | |
14 | #include "libqos/pci.h" | |
15 | #include "libqos/pci-pc.h" | |
bf3c63d2 MM |
16 | #include "libqos/malloc.h" |
17 | #include "libqos/malloc-pc.h" | |
ee3b850a | 18 | #include "standard-headers/linux/virtio_ring.h" |
c75f4c06 | 19 | #include "standard-headers/linux/virtio_pci.h" |
311e666a | 20 | |
7ad1e708 | 21 | #include "hw/pci/pci.h" |
311e666a MM |
22 | #include "hw/pci/pci_regs.h" |
23 | ||
24 | typedef struct QVirtioPCIForeachData { | |
25 | void (*func)(QVirtioDevice *d, void *data); | |
26 | uint16_t device_type; | |
c4523aae MAL |
27 | bool has_slot; |
28 | int slot; | |
311e666a MM |
29 | void *user_data; |
30 | } QVirtioPCIForeachData; | |
31 | ||
0c0eb302 MAL |
32 | void qvirtio_pci_device_free(QVirtioPCIDevice *dev) |
33 | { | |
34 | g_free(dev->pdev); | |
35 | g_free(dev); | |
36 | } | |
37 | ||
311e666a MM |
38 | static QVirtioPCIDevice *qpcidevice_to_qvirtiodevice(QPCIDevice *pdev) |
39 | { | |
40 | QVirtioPCIDevice *vpcidev; | |
41 | vpcidev = g_malloc0(sizeof(*vpcidev)); | |
42 | ||
43 | if (pdev) { | |
44 | vpcidev->pdev = pdev; | |
45 | vpcidev->vdev.device_type = | |
46 | qpci_config_readw(vpcidev->pdev, PCI_SUBSYSTEM_ID); | |
47 | } | |
48 | ||
58368113 MM |
49 | vpcidev->config_msix_entry = -1; |
50 | ||
311e666a MM |
51 | return vpcidev; |
52 | } | |
53 | ||
54 | static void qvirtio_pci_foreach_callback( | |
55 | QPCIDevice *dev, int devfn, void *data) | |
56 | { | |
57 | QVirtioPCIForeachData *d = data; | |
58 | QVirtioPCIDevice *vpcidev = qpcidevice_to_qvirtiodevice(dev); | |
59 | ||
c4523aae MAL |
60 | if (vpcidev->vdev.device_type == d->device_type && |
61 | (!d->has_slot || vpcidev->pdev->devfn == d->slot << 3)) { | |
311e666a MM |
62 | d->func(&vpcidev->vdev, d->user_data); |
63 | } else { | |
80e1eea3 | 64 | qvirtio_pci_device_free(vpcidev); |
311e666a MM |
65 | } |
66 | } | |
67 | ||
68 | static void qvirtio_pci_assign_device(QVirtioDevice *d, void *data) | |
69 | { | |
70 | QVirtioPCIDevice **vpcidev = data; | |
80e1eea3 | 71 | assert(!*vpcidev); |
311e666a MM |
72 | *vpcidev = (QVirtioPCIDevice *)d; |
73 | } | |
74 | ||
b4ba67d9 | 75 | #define CONFIG_BASE(dev) (VIRTIO_PCI_CONFIG_OFF((dev)->pdev->msix_enabled)) |
246fc0fb DG |
76 | |
77 | static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t off) | |
46e0cf76 MM |
78 | { |
79 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
b4ba67d9 | 80 | return qpci_io_readb(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); |
46e0cf76 MM |
81 | } |
82 | ||
30ca440e LV |
83 | /* PCI is always read in little-endian order |
84 | * but virtio ( < 1.0) is in guest order | |
85 | * so with a big-endian guest the order has been reversed, | |
86 | * reverse it again | |
87 | * virtio-1.0 is always little-endian, like PCI, but this | |
88 | * case will be managed inside qvirtio_is_big_endian() | |
89 | */ | |
90 | ||
246fc0fb | 91 | static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t off) |
46e0cf76 MM |
92 | { |
93 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
30ca440e LV |
94 | uint16_t value; |
95 | ||
b4ba67d9 | 96 | value = qpci_io_readw(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); |
30ca440e LV |
97 | if (qvirtio_is_big_endian(d)) { |
98 | value = bswap16(value); | |
99 | } | |
100 | return value; | |
46e0cf76 MM |
101 | } |
102 | ||
246fc0fb | 103 | static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t off) |
46e0cf76 MM |
104 | { |
105 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
30ca440e LV |
106 | uint32_t value; |
107 | ||
b4ba67d9 | 108 | value = qpci_io_readl(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); |
30ca440e LV |
109 | if (qvirtio_is_big_endian(d)) { |
110 | value = bswap32(value); | |
111 | } | |
112 | return value; | |
46e0cf76 MM |
113 | } |
114 | ||
246fc0fb | 115 | static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t off) |
46e0cf76 MM |
116 | { |
117 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
f775f45a | 118 | uint64_t val; |
46e0cf76 | 119 | |
b4ba67d9 | 120 | val = qpci_io_readq(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); |
8b4b80c3 | 121 | if (qvirtio_is_big_endian(d)) { |
f775f45a | 122 | val = bswap64(val); |
46e0cf76 MM |
123 | } |
124 | ||
f775f45a | 125 | return val; |
46e0cf76 MM |
126 | } |
127 | ||
bf3c63d2 MM |
128 | static uint32_t qvirtio_pci_get_features(QVirtioDevice *d) |
129 | { | |
130 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
b4ba67d9 | 131 | return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_HOST_FEATURES); |
bf3c63d2 MM |
132 | } |
133 | ||
134 | static void qvirtio_pci_set_features(QVirtioDevice *d, uint32_t features) | |
135 | { | |
136 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
b4ba67d9 | 137 | qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES, features); |
bf3c63d2 MM |
138 | } |
139 | ||
f294b029 MM |
140 | static uint32_t qvirtio_pci_get_guest_features(QVirtioDevice *d) |
141 | { | |
142 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
b4ba67d9 | 143 | return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES); |
f294b029 MM |
144 | } |
145 | ||
46e0cf76 MM |
146 | static uint8_t qvirtio_pci_get_status(QVirtioDevice *d) |
147 | { | |
148 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
b4ba67d9 | 149 | return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS); |
46e0cf76 MM |
150 | } |
151 | ||
152 | static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status) | |
153 | { | |
154 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
b4ba67d9 | 155 | qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS, status); |
46e0cf76 MM |
156 | } |
157 | ||
58368113 | 158 | static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) |
bf3c63d2 MM |
159 | { |
160 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
58368113 MM |
161 | QVirtQueuePCI *vqpci = (QVirtQueuePCI *)vq; |
162 | uint32_t data; | |
163 | ||
164 | if (dev->pdev->msix_enabled) { | |
165 | g_assert_cmpint(vqpci->msix_entry, !=, -1); | |
166 | if (qpci_msix_masked(dev->pdev, vqpci->msix_entry)) { | |
167 | /* No ISR checking should be done if masked, but read anyway */ | |
168 | return qpci_msix_pending(dev->pdev, vqpci->msix_entry); | |
169 | } else { | |
170 | data = readl(vqpci->msix_addr); | |
1e34cf96 MM |
171 | if (data == vqpci->msix_data) { |
172 | writel(vqpci->msix_addr, 0); | |
173 | return true; | |
174 | } else { | |
175 | return false; | |
176 | } | |
58368113 MM |
177 | } |
178 | } else { | |
b4ba67d9 | 179 | return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_ISR) & 1; |
58368113 MM |
180 | } |
181 | } | |
182 | ||
183 | static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d) | |
184 | { | |
185 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
186 | uint32_t data; | |
187 | ||
188 | if (dev->pdev->msix_enabled) { | |
189 | g_assert_cmpint(dev->config_msix_entry, !=, -1); | |
190 | if (qpci_msix_masked(dev->pdev, dev->config_msix_entry)) { | |
191 | /* No ISR checking should be done if masked, but read anyway */ | |
192 | return qpci_msix_pending(dev->pdev, dev->config_msix_entry); | |
193 | } else { | |
194 | data = readl(dev->config_msix_addr); | |
1e34cf96 MM |
195 | if (data == dev->config_msix_data) { |
196 | writel(dev->config_msix_addr, 0); | |
197 | return true; | |
198 | } else { | |
199 | return false; | |
200 | } | |
58368113 MM |
201 | } |
202 | } else { | |
b4ba67d9 | 203 | return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_ISR) & 2; |
58368113 | 204 | } |
bf3c63d2 MM |
205 | } |
206 | ||
207 | static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index) | |
208 | { | |
209 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
b4ba67d9 | 210 | qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_SEL, index); |
bf3c63d2 MM |
211 | } |
212 | ||
213 | static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d) | |
214 | { | |
215 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
b4ba67d9 | 216 | return qpci_io_readw(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NUM); |
bf3c63d2 MM |
217 | } |
218 | ||
219 | static void qvirtio_pci_set_queue_address(QVirtioDevice *d, uint32_t pfn) | |
220 | { | |
221 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
b4ba67d9 | 222 | qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_PFN, pfn); |
bf3c63d2 MM |
223 | } |
224 | ||
225 | static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d, | |
226 | QGuestAllocator *alloc, uint16_t index) | |
227 | { | |
f294b029 | 228 | uint32_t feat; |
bf3c63d2 | 229 | uint64_t addr; |
58368113 | 230 | QVirtQueuePCI *vqpci; |
bf3c63d2 | 231 | |
58368113 | 232 | vqpci = g_malloc0(sizeof(*vqpci)); |
f294b029 | 233 | feat = qvirtio_pci_get_guest_features(d); |
bf3c63d2 MM |
234 | |
235 | qvirtio_pci_queue_select(d, index); | |
58368113 MM |
236 | vqpci->vq.index = index; |
237 | vqpci->vq.size = qvirtio_pci_get_queue_size(d); | |
238 | vqpci->vq.free_head = 0; | |
239 | vqpci->vq.num_free = vqpci->vq.size; | |
c75f4c06 | 240 | vqpci->vq.align = VIRTIO_PCI_VRING_ALIGN; |
ee3b850a SH |
241 | vqpci->vq.indirect = (feat & (1u << VIRTIO_RING_F_INDIRECT_DESC)) != 0; |
242 | vqpci->vq.event = (feat & (1u << VIRTIO_RING_F_EVENT_IDX)) != 0; | |
58368113 MM |
243 | |
244 | vqpci->msix_entry = -1; | |
245 | vqpci->msix_addr = 0; | |
246 | vqpci->msix_data = 0x12345678; | |
bf3c63d2 MM |
247 | |
248 | /* Check different than 0 */ | |
58368113 | 249 | g_assert_cmpint(vqpci->vq.size, !=, 0); |
bf3c63d2 MM |
250 | |
251 | /* Check power of 2 */ | |
58368113 | 252 | g_assert_cmpint(vqpci->vq.size & (vqpci->vq.size - 1), ==, 0); |
bf3c63d2 | 253 | |
c75f4c06 SH |
254 | addr = guest_alloc(alloc, qvring_size(vqpci->vq.size, |
255 | VIRTIO_PCI_VRING_ALIGN)); | |
58368113 | 256 | qvring_init(alloc, &vqpci->vq, addr); |
c75f4c06 | 257 | qvirtio_pci_set_queue_address(d, vqpci->vq.desc / VIRTIO_PCI_VRING_ALIGN); |
bf3c63d2 | 258 | |
58368113 | 259 | return &vqpci->vq; |
bf3c63d2 MM |
260 | } |
261 | ||
f1d3b991 SH |
262 | static void qvirtio_pci_virtqueue_cleanup(QVirtQueue *vq, |
263 | QGuestAllocator *alloc) | |
264 | { | |
265 | QVirtQueuePCI *vqpci = container_of(vq, QVirtQueuePCI, vq); | |
266 | ||
267 | guest_free(alloc, vq->desc); | |
268 | g_free(vqpci); | |
269 | } | |
270 | ||
bf3c63d2 MM |
271 | static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) |
272 | { | |
273 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
b4ba67d9 | 274 | qpci_io_writew(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NOTIFY, vq->index); |
bf3c63d2 MM |
275 | } |
276 | ||
46e0cf76 MM |
277 | const QVirtioBus qvirtio_pci = { |
278 | .config_readb = qvirtio_pci_config_readb, | |
279 | .config_readw = qvirtio_pci_config_readw, | |
280 | .config_readl = qvirtio_pci_config_readl, | |
281 | .config_readq = qvirtio_pci_config_readq, | |
bf3c63d2 MM |
282 | .get_features = qvirtio_pci_get_features, |
283 | .set_features = qvirtio_pci_set_features, | |
f294b029 | 284 | .get_guest_features = qvirtio_pci_get_guest_features, |
46e0cf76 MM |
285 | .get_status = qvirtio_pci_get_status, |
286 | .set_status = qvirtio_pci_set_status, | |
58368113 MM |
287 | .get_queue_isr_status = qvirtio_pci_get_queue_isr_status, |
288 | .get_config_isr_status = qvirtio_pci_get_config_isr_status, | |
bf3c63d2 MM |
289 | .queue_select = qvirtio_pci_queue_select, |
290 | .get_queue_size = qvirtio_pci_get_queue_size, | |
291 | .set_queue_address = qvirtio_pci_set_queue_address, | |
292 | .virtqueue_setup = qvirtio_pci_virtqueue_setup, | |
f1d3b991 | 293 | .virtqueue_cleanup = qvirtio_pci_virtqueue_cleanup, |
bf3c63d2 | 294 | .virtqueue_kick = qvirtio_pci_virtqueue_kick, |
46e0cf76 MM |
295 | }; |
296 | ||
c4523aae MAL |
297 | static void qvirtio_pci_foreach(QPCIBus *bus, uint16_t device_type, |
298 | bool has_slot, int slot, | |
311e666a MM |
299 | void (*func)(QVirtioDevice *d, void *data), void *data) |
300 | { | |
301 | QVirtioPCIForeachData d = { .func = func, | |
302 | .device_type = device_type, | |
c4523aae MAL |
303 | .has_slot = has_slot, |
304 | .slot = slot, | |
311e666a MM |
305 | .user_data = data }; |
306 | ||
7ad1e708 | 307 | qpci_device_foreach(bus, PCI_VENDOR_ID_REDHAT_QUMRANET, -1, |
c4523aae | 308 | qvirtio_pci_foreach_callback, &d); |
311e666a MM |
309 | } |
310 | ||
311 | QVirtioPCIDevice *qvirtio_pci_device_find(QPCIBus *bus, uint16_t device_type) | |
312 | { | |
313 | QVirtioPCIDevice *dev = NULL; | |
c4523aae MAL |
314 | |
315 | qvirtio_pci_foreach(bus, device_type, false, 0, | |
316 | qvirtio_pci_assign_device, &dev); | |
317 | ||
bb37a2c0 TH |
318 | if (dev) { |
319 | dev->vdev.bus = &qvirtio_pci; | |
320 | } | |
c4523aae MAL |
321 | |
322 | return dev; | |
323 | } | |
324 | ||
325 | QVirtioPCIDevice *qvirtio_pci_device_find_slot(QPCIBus *bus, | |
326 | uint16_t device_type, int slot) | |
327 | { | |
328 | QVirtioPCIDevice *dev = NULL; | |
329 | ||
330 | qvirtio_pci_foreach(bus, device_type, true, slot, | |
331 | qvirtio_pci_assign_device, &dev); | |
311e666a | 332 | |
6b9cdf4c LV |
333 | dev->vdev.bus = &qvirtio_pci; |
334 | ||
311e666a MM |
335 | return dev; |
336 | } | |
46e0cf76 MM |
337 | |
338 | void qvirtio_pci_device_enable(QVirtioPCIDevice *d) | |
339 | { | |
340 | qpci_device_enable(d->pdev); | |
b4ba67d9 | 341 | d->bar = qpci_iomap(d->pdev, 0, NULL); |
46e0cf76 MM |
342 | } |
343 | ||
344 | void qvirtio_pci_device_disable(QVirtioPCIDevice *d) | |
345 | { | |
b4ba67d9 | 346 | qpci_iounmap(d->pdev, d->bar); |
58368113 MM |
347 | } |
348 | ||
349 | void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci, | |
350 | QGuestAllocator *alloc, uint16_t entry) | |
351 | { | |
352 | uint16_t vector; | |
353 | uint32_t control; | |
b4ba67d9 | 354 | uint64_t off; |
58368113 MM |
355 | |
356 | g_assert(d->pdev->msix_enabled); | |
b4ba67d9 | 357 | off = d->pdev->msix_table_off + (entry * 16); |
58368113 MM |
358 | |
359 | g_assert_cmpint(entry, >=, 0); | |
360 | g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev)); | |
361 | vqpci->msix_entry = entry; | |
362 | ||
363 | vqpci->msix_addr = guest_alloc(alloc, 4); | |
b4ba67d9 DG |
364 | qpci_io_writel(d->pdev, d->pdev->msix_table_bar, |
365 | off + PCI_MSIX_ENTRY_LOWER_ADDR, vqpci->msix_addr & ~0UL); | |
366 | qpci_io_writel(d->pdev, d->pdev->msix_table_bar, | |
367 | off + PCI_MSIX_ENTRY_UPPER_ADDR, | |
368 | (vqpci->msix_addr >> 32) & ~0UL); | |
369 | qpci_io_writel(d->pdev, d->pdev->msix_table_bar, | |
370 | off + PCI_MSIX_ENTRY_DATA, vqpci->msix_data); | |
371 | ||
372 | control = qpci_io_readl(d->pdev, d->pdev->msix_table_bar, | |
373 | off + PCI_MSIX_ENTRY_VECTOR_CTRL); | |
374 | qpci_io_writel(d->pdev, d->pdev->msix_table_bar, | |
375 | off + PCI_MSIX_ENTRY_VECTOR_CTRL, | |
376 | control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); | |
58368113 MM |
377 | |
378 | qvirtio_pci_queue_select(&d->vdev, vqpci->vq.index); | |
b4ba67d9 DG |
379 | qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR, entry); |
380 | vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR); | |
c75f4c06 | 381 | g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR); |
58368113 MM |
382 | } |
383 | ||
384 | void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d, | |
385 | QGuestAllocator *alloc, uint16_t entry) | |
386 | { | |
387 | uint16_t vector; | |
388 | uint32_t control; | |
b4ba67d9 | 389 | uint64_t off; |
58368113 MM |
390 | |
391 | g_assert(d->pdev->msix_enabled); | |
b4ba67d9 | 392 | off = d->pdev->msix_table_off + (entry * 16); |
58368113 MM |
393 | |
394 | g_assert_cmpint(entry, >=, 0); | |
395 | g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev)); | |
396 | d->config_msix_entry = entry; | |
397 | ||
398 | d->config_msix_data = 0x12345678; | |
399 | d->config_msix_addr = guest_alloc(alloc, 4); | |
400 | ||
b4ba67d9 DG |
401 | qpci_io_writel(d->pdev, d->pdev->msix_table_bar, |
402 | off + PCI_MSIX_ENTRY_LOWER_ADDR, d->config_msix_addr & ~0UL); | |
403 | qpci_io_writel(d->pdev, d->pdev->msix_table_bar, | |
404 | off + PCI_MSIX_ENTRY_UPPER_ADDR, | |
405 | (d->config_msix_addr >> 32) & ~0UL); | |
406 | qpci_io_writel(d->pdev, d->pdev->msix_table_bar, | |
407 | off + PCI_MSIX_ENTRY_DATA, d->config_msix_data); | |
408 | ||
409 | control = qpci_io_readl(d->pdev, d->pdev->msix_table_bar, | |
410 | off + PCI_MSIX_ENTRY_VECTOR_CTRL); | |
411 | qpci_io_writel(d->pdev, d->pdev->msix_table_bar, | |
412 | off + PCI_MSIX_ENTRY_VECTOR_CTRL, | |
413 | control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); | |
414 | ||
415 | qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR, entry); | |
416 | vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR); | |
c75f4c06 | 417 | g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR); |
46e0cf76 | 418 | } |