]>
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 | ||
10 | #include <glib.h> | |
46e0cf76 | 11 | #include <stdio.h> |
311e666a MM |
12 | #include "libqtest.h" |
13 | #include "libqos/virtio.h" | |
14 | #include "libqos/virtio-pci.h" | |
15 | #include "libqos/pci.h" | |
16 | #include "libqos/pci-pc.h" | |
bf3c63d2 MM |
17 | #include "libqos/malloc.h" |
18 | #include "libqos/malloc-pc.h" | |
311e666a MM |
19 | |
20 | #include "hw/pci/pci_regs.h" | |
21 | ||
22 | typedef struct QVirtioPCIForeachData { | |
23 | void (*func)(QVirtioDevice *d, void *data); | |
24 | uint16_t device_type; | |
25 | void *user_data; | |
26 | } QVirtioPCIForeachData; | |
27 | ||
28 | static QVirtioPCIDevice *qpcidevice_to_qvirtiodevice(QPCIDevice *pdev) | |
29 | { | |
30 | QVirtioPCIDevice *vpcidev; | |
31 | vpcidev = g_malloc0(sizeof(*vpcidev)); | |
32 | ||
33 | if (pdev) { | |
34 | vpcidev->pdev = pdev; | |
35 | vpcidev->vdev.device_type = | |
36 | qpci_config_readw(vpcidev->pdev, PCI_SUBSYSTEM_ID); | |
37 | } | |
38 | ||
58368113 MM |
39 | vpcidev->config_msix_entry = -1; |
40 | ||
311e666a MM |
41 | return vpcidev; |
42 | } | |
43 | ||
44 | static void qvirtio_pci_foreach_callback( | |
45 | QPCIDevice *dev, int devfn, void *data) | |
46 | { | |
47 | QVirtioPCIForeachData *d = data; | |
48 | QVirtioPCIDevice *vpcidev = qpcidevice_to_qvirtiodevice(dev); | |
49 | ||
50 | if (vpcidev->vdev.device_type == d->device_type) { | |
51 | d->func(&vpcidev->vdev, d->user_data); | |
52 | } else { | |
53 | g_free(vpcidev); | |
54 | } | |
55 | } | |
56 | ||
57 | static void qvirtio_pci_assign_device(QVirtioDevice *d, void *data) | |
58 | { | |
59 | QVirtioPCIDevice **vpcidev = data; | |
60 | *vpcidev = (QVirtioPCIDevice *)d; | |
61 | } | |
62 | ||
728312b8 | 63 | static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t addr) |
46e0cf76 MM |
64 | { |
65 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
728312b8 | 66 | return qpci_io_readb(dev->pdev, (void *)(uintptr_t)addr); |
46e0cf76 MM |
67 | } |
68 | ||
728312b8 | 69 | static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t addr) |
46e0cf76 MM |
70 | { |
71 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
728312b8 | 72 | return qpci_io_readw(dev->pdev, (void *)(uintptr_t)addr); |
46e0cf76 MM |
73 | } |
74 | ||
728312b8 | 75 | static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t addr) |
46e0cf76 MM |
76 | { |
77 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
728312b8 | 78 | return qpci_io_readl(dev->pdev, (void *)(uintptr_t)addr); |
46e0cf76 MM |
79 | } |
80 | ||
728312b8 | 81 | static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t addr) |
46e0cf76 MM |
82 | { |
83 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
84 | int i; | |
85 | uint64_t u64 = 0; | |
86 | ||
87 | if (qtest_big_endian()) { | |
88 | for (i = 0; i < 8; ++i) { | |
728312b8 MM |
89 | u64 |= (uint64_t)qpci_io_readb(dev->pdev, |
90 | (void *)(uintptr_t)addr + i) << (7 - i) * 8; | |
46e0cf76 MM |
91 | } |
92 | } else { | |
93 | for (i = 0; i < 8; ++i) { | |
728312b8 MM |
94 | u64 |= (uint64_t)qpci_io_readb(dev->pdev, |
95 | (void *)(uintptr_t)addr + i) << i * 8; | |
46e0cf76 MM |
96 | } |
97 | } | |
98 | ||
99 | return u64; | |
100 | } | |
101 | ||
bf3c63d2 MM |
102 | static uint32_t qvirtio_pci_get_features(QVirtioDevice *d) |
103 | { | |
104 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
50311a81 | 105 | return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_PCI_DEVICE_FEATURES); |
bf3c63d2 MM |
106 | } |
107 | ||
108 | static void qvirtio_pci_set_features(QVirtioDevice *d, uint32_t features) | |
109 | { | |
110 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
50311a81 | 111 | qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_PCI_GUEST_FEATURES, features); |
bf3c63d2 MM |
112 | } |
113 | ||
f294b029 MM |
114 | static uint32_t qvirtio_pci_get_guest_features(QVirtioDevice *d) |
115 | { | |
116 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
50311a81 | 117 | return qpci_io_readl(dev->pdev, dev->addr + QVIRTIO_PCI_GUEST_FEATURES); |
f294b029 MM |
118 | } |
119 | ||
46e0cf76 MM |
120 | static uint8_t qvirtio_pci_get_status(QVirtioDevice *d) |
121 | { | |
122 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
50311a81 | 123 | return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_PCI_DEVICE_STATUS); |
46e0cf76 MM |
124 | } |
125 | ||
126 | static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status) | |
127 | { | |
128 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
50311a81 | 129 | qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_PCI_DEVICE_STATUS, status); |
46e0cf76 MM |
130 | } |
131 | ||
58368113 | 132 | static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) |
bf3c63d2 MM |
133 | { |
134 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
58368113 MM |
135 | QVirtQueuePCI *vqpci = (QVirtQueuePCI *)vq; |
136 | uint32_t data; | |
137 | ||
138 | if (dev->pdev->msix_enabled) { | |
139 | g_assert_cmpint(vqpci->msix_entry, !=, -1); | |
140 | if (qpci_msix_masked(dev->pdev, vqpci->msix_entry)) { | |
141 | /* No ISR checking should be done if masked, but read anyway */ | |
142 | return qpci_msix_pending(dev->pdev, vqpci->msix_entry); | |
143 | } else { | |
144 | data = readl(vqpci->msix_addr); | |
1e34cf96 MM |
145 | if (data == vqpci->msix_data) { |
146 | writel(vqpci->msix_addr, 0); | |
147 | return true; | |
148 | } else { | |
149 | return false; | |
150 | } | |
58368113 MM |
151 | } |
152 | } else { | |
50311a81 | 153 | return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_PCI_ISR_STATUS) & 1; |
58368113 MM |
154 | } |
155 | } | |
156 | ||
157 | static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d) | |
158 | { | |
159 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
160 | uint32_t data; | |
161 | ||
162 | if (dev->pdev->msix_enabled) { | |
163 | g_assert_cmpint(dev->config_msix_entry, !=, -1); | |
164 | if (qpci_msix_masked(dev->pdev, dev->config_msix_entry)) { | |
165 | /* No ISR checking should be done if masked, but read anyway */ | |
166 | return qpci_msix_pending(dev->pdev, dev->config_msix_entry); | |
167 | } else { | |
168 | data = readl(dev->config_msix_addr); | |
1e34cf96 MM |
169 | if (data == dev->config_msix_data) { |
170 | writel(dev->config_msix_addr, 0); | |
171 | return true; | |
172 | } else { | |
173 | return false; | |
174 | } | |
58368113 MM |
175 | } |
176 | } else { | |
50311a81 | 177 | return qpci_io_readb(dev->pdev, dev->addr + QVIRTIO_PCI_ISR_STATUS) & 2; |
58368113 | 178 | } |
bf3c63d2 MM |
179 | } |
180 | ||
181 | static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index) | |
182 | { | |
183 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
50311a81 | 184 | qpci_io_writeb(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_SELECT, index); |
bf3c63d2 MM |
185 | } |
186 | ||
187 | static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d) | |
188 | { | |
189 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
50311a81 | 190 | return qpci_io_readw(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_SIZE); |
bf3c63d2 MM |
191 | } |
192 | ||
193 | static void qvirtio_pci_set_queue_address(QVirtioDevice *d, uint32_t pfn) | |
194 | { | |
195 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
50311a81 | 196 | qpci_io_writel(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_ADDRESS, pfn); |
bf3c63d2 MM |
197 | } |
198 | ||
199 | static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d, | |
200 | QGuestAllocator *alloc, uint16_t index) | |
201 | { | |
f294b029 | 202 | uint32_t feat; |
bf3c63d2 | 203 | uint64_t addr; |
58368113 | 204 | QVirtQueuePCI *vqpci; |
bf3c63d2 | 205 | |
58368113 | 206 | vqpci = g_malloc0(sizeof(*vqpci)); |
f294b029 | 207 | feat = qvirtio_pci_get_guest_features(d); |
bf3c63d2 MM |
208 | |
209 | qvirtio_pci_queue_select(d, index); | |
58368113 MM |
210 | vqpci->vq.index = index; |
211 | vqpci->vq.size = qvirtio_pci_get_queue_size(d); | |
212 | vqpci->vq.free_head = 0; | |
213 | vqpci->vq.num_free = vqpci->vq.size; | |
214 | vqpci->vq.align = QVIRTIO_PCI_ALIGN; | |
215 | vqpci->vq.indirect = (feat & QVIRTIO_F_RING_INDIRECT_DESC) != 0; | |
1053587c | 216 | vqpci->vq.event = (feat & QVIRTIO_F_RING_EVENT_IDX) != 0; |
58368113 MM |
217 | |
218 | vqpci->msix_entry = -1; | |
219 | vqpci->msix_addr = 0; | |
220 | vqpci->msix_data = 0x12345678; | |
bf3c63d2 MM |
221 | |
222 | /* Check different than 0 */ | |
58368113 | 223 | g_assert_cmpint(vqpci->vq.size, !=, 0); |
bf3c63d2 MM |
224 | |
225 | /* Check power of 2 */ | |
58368113 | 226 | g_assert_cmpint(vqpci->vq.size & (vqpci->vq.size - 1), ==, 0); |
bf3c63d2 | 227 | |
58368113 MM |
228 | addr = guest_alloc(alloc, qvring_size(vqpci->vq.size, QVIRTIO_PCI_ALIGN)); |
229 | qvring_init(alloc, &vqpci->vq, addr); | |
230 | qvirtio_pci_set_queue_address(d, vqpci->vq.desc / QVIRTIO_PCI_ALIGN); | |
bf3c63d2 | 231 | |
58368113 | 232 | return &vqpci->vq; |
bf3c63d2 MM |
233 | } |
234 | ||
235 | static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) | |
236 | { | |
237 | QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; | |
50311a81 | 238 | qpci_io_writew(dev->pdev, dev->addr + QVIRTIO_PCI_QUEUE_NOTIFY, vq->index); |
bf3c63d2 MM |
239 | } |
240 | ||
46e0cf76 MM |
241 | const QVirtioBus qvirtio_pci = { |
242 | .config_readb = qvirtio_pci_config_readb, | |
243 | .config_readw = qvirtio_pci_config_readw, | |
244 | .config_readl = qvirtio_pci_config_readl, | |
245 | .config_readq = qvirtio_pci_config_readq, | |
bf3c63d2 MM |
246 | .get_features = qvirtio_pci_get_features, |
247 | .set_features = qvirtio_pci_set_features, | |
f294b029 | 248 | .get_guest_features = qvirtio_pci_get_guest_features, |
46e0cf76 MM |
249 | .get_status = qvirtio_pci_get_status, |
250 | .set_status = qvirtio_pci_set_status, | |
58368113 MM |
251 | .get_queue_isr_status = qvirtio_pci_get_queue_isr_status, |
252 | .get_config_isr_status = qvirtio_pci_get_config_isr_status, | |
bf3c63d2 MM |
253 | .queue_select = qvirtio_pci_queue_select, |
254 | .get_queue_size = qvirtio_pci_get_queue_size, | |
255 | .set_queue_address = qvirtio_pci_set_queue_address, | |
256 | .virtqueue_setup = qvirtio_pci_virtqueue_setup, | |
257 | .virtqueue_kick = qvirtio_pci_virtqueue_kick, | |
46e0cf76 MM |
258 | }; |
259 | ||
311e666a MM |
260 | void qvirtio_pci_foreach(QPCIBus *bus, uint16_t device_type, |
261 | void (*func)(QVirtioDevice *d, void *data), void *data) | |
262 | { | |
263 | QVirtioPCIForeachData d = { .func = func, | |
264 | .device_type = device_type, | |
265 | .user_data = data }; | |
266 | ||
267 | qpci_device_foreach(bus, QVIRTIO_VENDOR_ID, -1, | |
268 | qvirtio_pci_foreach_callback, &d); | |
269 | } | |
270 | ||
271 | QVirtioPCIDevice *qvirtio_pci_device_find(QPCIBus *bus, uint16_t device_type) | |
272 | { | |
273 | QVirtioPCIDevice *dev = NULL; | |
274 | qvirtio_pci_foreach(bus, device_type, qvirtio_pci_assign_device, &dev); | |
275 | ||
276 | return dev; | |
277 | } | |
46e0cf76 MM |
278 | |
279 | void qvirtio_pci_device_enable(QVirtioPCIDevice *d) | |
280 | { | |
281 | qpci_device_enable(d->pdev); | |
282 | d->addr = qpci_iomap(d->pdev, 0, NULL); | |
283 | g_assert(d->addr != NULL); | |
284 | } | |
285 | ||
286 | void qvirtio_pci_device_disable(QVirtioPCIDevice *d) | |
287 | { | |
288 | qpci_iounmap(d->pdev, d->addr); | |
58368113 MM |
289 | d->addr = NULL; |
290 | } | |
291 | ||
292 | void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci, | |
293 | QGuestAllocator *alloc, uint16_t entry) | |
294 | { | |
295 | uint16_t vector; | |
296 | uint32_t control; | |
297 | void *addr; | |
298 | ||
299 | g_assert(d->pdev->msix_enabled); | |
300 | addr = d->pdev->msix_table + (entry * 16); | |
301 | ||
302 | g_assert_cmpint(entry, >=, 0); | |
303 | g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev)); | |
304 | vqpci->msix_entry = entry; | |
305 | ||
306 | vqpci->msix_addr = guest_alloc(alloc, 4); | |
307 | qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_LOWER_ADDR, | |
308 | vqpci->msix_addr & ~0UL); | |
309 | qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_UPPER_ADDR, | |
310 | (vqpci->msix_addr >> 32) & ~0UL); | |
311 | qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_DATA, vqpci->msix_data); | |
312 | ||
313 | control = qpci_io_readl(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL); | |
314 | qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL, | |
315 | control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); | |
316 | ||
317 | qvirtio_pci_queue_select(&d->vdev, vqpci->vq.index); | |
50311a81 MM |
318 | qpci_io_writew(d->pdev, d->addr + QVIRTIO_PCI_MSIX_QUEUE_VECTOR, entry); |
319 | vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_PCI_MSIX_QUEUE_VECTOR); | |
58368113 MM |
320 | g_assert_cmphex(vector, !=, QVIRTIO_MSI_NO_VECTOR); |
321 | } | |
322 | ||
323 | void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d, | |
324 | QGuestAllocator *alloc, uint16_t entry) | |
325 | { | |
326 | uint16_t vector; | |
327 | uint32_t control; | |
328 | void *addr; | |
329 | ||
330 | g_assert(d->pdev->msix_enabled); | |
331 | addr = d->pdev->msix_table + (entry * 16); | |
332 | ||
333 | g_assert_cmpint(entry, >=, 0); | |
334 | g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev)); | |
335 | d->config_msix_entry = entry; | |
336 | ||
337 | d->config_msix_data = 0x12345678; | |
338 | d->config_msix_addr = guest_alloc(alloc, 4); | |
339 | ||
340 | qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_LOWER_ADDR, | |
341 | d->config_msix_addr & ~0UL); | |
342 | qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_UPPER_ADDR, | |
343 | (d->config_msix_addr >> 32) & ~0UL); | |
344 | qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_DATA, d->config_msix_data); | |
345 | ||
346 | control = qpci_io_readl(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL); | |
347 | qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL, | |
348 | control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); | |
349 | ||
50311a81 MM |
350 | qpci_io_writew(d->pdev, d->addr + QVIRTIO_PCI_MSIX_CONF_VECTOR, entry); |
351 | vector = qpci_io_readw(d->pdev, d->addr + QVIRTIO_PCI_MSIX_CONF_VECTOR); | |
58368113 | 352 | g_assert_cmphex(vector, !=, QVIRTIO_MSI_NO_VECTOR); |
46e0cf76 | 353 | } |