]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /*- |
2 | * BSD LICENSE | |
3 | * | |
4 | * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. | |
5 | * All rights reserved. | |
6 | * | |
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * | |
11 | * * Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * * Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in | |
15 | * the documentation and/or other materials provided with the | |
16 | * distribution. | |
17 | * * Neither the name of Intel Corporation nor the names of its | |
18 | * contributors may be used to endorse or promote products derived | |
19 | * from this software without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
24 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
25 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
27 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
31 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
32 | */ | |
33 | #include <stdint.h> | |
34 | ||
35 | #ifdef RTE_EXEC_ENV_LINUXAPP | |
36 | #include <dirent.h> | |
37 | #include <fcntl.h> | |
38 | #endif | |
39 | ||
40 | #include "virtio_pci.h" | |
41 | #include "virtio_logs.h" | |
42 | #include "virtqueue.h" | |
43 | ||
44 | /* | |
45 | * Following macros are derived from linux/pci_regs.h, however, | |
46 | * we can't simply include that header here, as there is no such | |
47 | * file for non-Linux platform. | |
48 | */ | |
49 | #define PCI_CAPABILITY_LIST 0x34 | |
50 | #define PCI_CAP_ID_VNDR 0x09 | |
51 | ||
52 | /* | |
53 | * The remaining space is defined by each driver as the per-driver | |
54 | * configuration space. | |
55 | */ | |
56 | #define VIRTIO_PCI_CONFIG(hw) (((hw)->use_msix) ? 24 : 20) | |
57 | ||
58 | static inline int | |
59 | check_vq_phys_addr_ok(struct virtqueue *vq) | |
60 | { | |
61 | /* Virtio PCI device VIRTIO_PCI_QUEUE_PF register is 32bit, | |
62 | * and only accepts 32 bit page frame number. | |
63 | * Check if the allocated physical memory exceeds 16TB. | |
64 | */ | |
65 | if ((vq->vq_ring_mem + vq->vq_ring_size - 1) >> | |
66 | (VIRTIO_PCI_QUEUE_ADDR_SHIFT + 32)) { | |
67 | PMD_INIT_LOG(ERR, "vring address shouldn't be above 16TB!"); | |
68 | return 0; | |
69 | } | |
70 | ||
71 | return 1; | |
72 | } | |
73 | ||
74 | /* | |
75 | * Since we are in legacy mode: | |
76 | * http://ozlabs.org/~rusty/virtio-spec/virtio-0.9.5.pdf | |
77 | * | |
78 | * "Note that this is possible because while the virtio header is PCI (i.e. | |
79 | * little) endian, the device-specific region is encoded in the native endian of | |
80 | * the guest (where such distinction is applicable)." | |
81 | * | |
82 | * For powerpc which supports both, qemu supposes that cpu is big endian and | |
83 | * enforces this for the virtio-net stuff. | |
84 | */ | |
85 | static void | |
86 | legacy_read_dev_config(struct virtio_hw *hw, size_t offset, | |
87 | void *dst, int length) | |
88 | { | |
89 | #ifdef RTE_ARCH_PPC_64 | |
90 | int size; | |
91 | ||
92 | while (length > 0) { | |
93 | if (length >= 4) { | |
94 | size = 4; | |
95 | rte_eal_pci_ioport_read(&hw->io, dst, size, | |
96 | VIRTIO_PCI_CONFIG(hw) + offset); | |
97 | *(uint32_t *)dst = rte_be_to_cpu_32(*(uint32_t *)dst); | |
98 | } else if (length >= 2) { | |
99 | size = 2; | |
100 | rte_eal_pci_ioport_read(&hw->io, dst, size, | |
101 | VIRTIO_PCI_CONFIG(hw) + offset); | |
102 | *(uint16_t *)dst = rte_be_to_cpu_16(*(uint16_t *)dst); | |
103 | } else { | |
104 | size = 1; | |
105 | rte_eal_pci_ioport_read(&hw->io, dst, size, | |
106 | VIRTIO_PCI_CONFIG(hw) + offset); | |
107 | } | |
108 | ||
109 | dst = (char *)dst + size; | |
110 | offset += size; | |
111 | length -= size; | |
112 | } | |
113 | #else | |
114 | rte_eal_pci_ioport_read(&hw->io, dst, length, | |
115 | VIRTIO_PCI_CONFIG(hw) + offset); | |
116 | #endif | |
117 | } | |
118 | ||
119 | static void | |
120 | legacy_write_dev_config(struct virtio_hw *hw, size_t offset, | |
121 | const void *src, int length) | |
122 | { | |
123 | #ifdef RTE_ARCH_PPC_64 | |
124 | union { | |
125 | uint32_t u32; | |
126 | uint16_t u16; | |
127 | } tmp; | |
128 | int size; | |
129 | ||
130 | while (length > 0) { | |
131 | if (length >= 4) { | |
132 | size = 4; | |
133 | tmp.u32 = rte_cpu_to_be_32(*(const uint32_t *)src); | |
134 | rte_eal_pci_ioport_write(&hw->io, &tmp.u32, size, | |
135 | VIRTIO_PCI_CONFIG(hw) + offset); | |
136 | } else if (length >= 2) { | |
137 | size = 2; | |
138 | tmp.u16 = rte_cpu_to_be_16(*(const uint16_t *)src); | |
139 | rte_eal_pci_ioport_write(&hw->io, &tmp.u16, size, | |
140 | VIRTIO_PCI_CONFIG(hw) + offset); | |
141 | } else { | |
142 | size = 1; | |
143 | rte_eal_pci_ioport_write(&hw->io, src, size, | |
144 | VIRTIO_PCI_CONFIG(hw) + offset); | |
145 | } | |
146 | ||
147 | src = (const char *)src + size; | |
148 | offset += size; | |
149 | length -= size; | |
150 | } | |
151 | #else | |
152 | rte_eal_pci_ioport_write(&hw->io, src, length, | |
153 | VIRTIO_PCI_CONFIG(hw) + offset); | |
154 | #endif | |
155 | } | |
156 | ||
157 | static uint64_t | |
158 | legacy_get_features(struct virtio_hw *hw) | |
159 | { | |
160 | uint32_t dst; | |
161 | ||
162 | rte_eal_pci_ioport_read(&hw->io, &dst, 4, VIRTIO_PCI_HOST_FEATURES); | |
163 | return dst; | |
164 | } | |
165 | ||
166 | static void | |
167 | legacy_set_features(struct virtio_hw *hw, uint64_t features) | |
168 | { | |
169 | if ((features >> 32) != 0) { | |
170 | PMD_DRV_LOG(ERR, | |
171 | "only 32 bit features are allowed for legacy virtio!"); | |
172 | return; | |
173 | } | |
174 | rte_eal_pci_ioport_write(&hw->io, &features, 4, | |
175 | VIRTIO_PCI_GUEST_FEATURES); | |
176 | } | |
177 | ||
178 | static uint8_t | |
179 | legacy_get_status(struct virtio_hw *hw) | |
180 | { | |
181 | uint8_t dst; | |
182 | ||
183 | rte_eal_pci_ioport_read(&hw->io, &dst, 1, VIRTIO_PCI_STATUS); | |
184 | return dst; | |
185 | } | |
186 | ||
187 | static void | |
188 | legacy_set_status(struct virtio_hw *hw, uint8_t status) | |
189 | { | |
190 | rte_eal_pci_ioport_write(&hw->io, &status, 1, VIRTIO_PCI_STATUS); | |
191 | } | |
192 | ||
193 | static void | |
194 | legacy_reset(struct virtio_hw *hw) | |
195 | { | |
196 | legacy_set_status(hw, VIRTIO_CONFIG_STATUS_RESET); | |
197 | } | |
198 | ||
199 | static uint8_t | |
200 | legacy_get_isr(struct virtio_hw *hw) | |
201 | { | |
202 | uint8_t dst; | |
203 | ||
204 | rte_eal_pci_ioport_read(&hw->io, &dst, 1, VIRTIO_PCI_ISR); | |
205 | return dst; | |
206 | } | |
207 | ||
208 | /* Enable one vector (0) for Link State Intrerrupt */ | |
209 | static uint16_t | |
210 | legacy_set_config_irq(struct virtio_hw *hw, uint16_t vec) | |
211 | { | |
212 | uint16_t dst; | |
213 | ||
214 | rte_eal_pci_ioport_write(&hw->io, &vec, 2, VIRTIO_MSI_CONFIG_VECTOR); | |
215 | rte_eal_pci_ioport_read(&hw->io, &dst, 2, VIRTIO_MSI_CONFIG_VECTOR); | |
216 | return dst; | |
217 | } | |
218 | ||
219 | static uint16_t | |
220 | legacy_get_queue_num(struct virtio_hw *hw, uint16_t queue_id) | |
221 | { | |
222 | uint16_t dst; | |
223 | ||
224 | rte_eal_pci_ioport_write(&hw->io, &queue_id, 2, VIRTIO_PCI_QUEUE_SEL); | |
225 | rte_eal_pci_ioport_read(&hw->io, &dst, 2, VIRTIO_PCI_QUEUE_NUM); | |
226 | return dst; | |
227 | } | |
228 | ||
229 | static int | |
230 | legacy_setup_queue(struct virtio_hw *hw, struct virtqueue *vq) | |
231 | { | |
232 | uint32_t src; | |
233 | ||
234 | if (!check_vq_phys_addr_ok(vq)) | |
235 | return -1; | |
236 | ||
237 | rte_eal_pci_ioport_write(&hw->io, &vq->vq_queue_index, 2, | |
238 | VIRTIO_PCI_QUEUE_SEL); | |
239 | src = vq->vq_ring_mem >> VIRTIO_PCI_QUEUE_ADDR_SHIFT; | |
240 | rte_eal_pci_ioport_write(&hw->io, &src, 4, VIRTIO_PCI_QUEUE_PFN); | |
241 | ||
242 | return 0; | |
243 | } | |
244 | ||
245 | static void | |
246 | legacy_del_queue(struct virtio_hw *hw, struct virtqueue *vq) | |
247 | { | |
248 | uint32_t src = 0; | |
249 | ||
250 | rte_eal_pci_ioport_write(&hw->io, &vq->vq_queue_index, 2, | |
251 | VIRTIO_PCI_QUEUE_SEL); | |
252 | rte_eal_pci_ioport_write(&hw->io, &src, 4, VIRTIO_PCI_QUEUE_PFN); | |
253 | } | |
254 | ||
255 | static void | |
256 | legacy_notify_queue(struct virtio_hw *hw, struct virtqueue *vq) | |
257 | { | |
258 | rte_eal_pci_ioport_write(&hw->io, &vq->vq_queue_index, 2, | |
259 | VIRTIO_PCI_QUEUE_NOTIFY); | |
260 | } | |
261 | ||
262 | #ifdef RTE_EXEC_ENV_LINUXAPP | |
263 | static int | |
264 | legacy_virtio_has_msix(const struct rte_pci_addr *loc) | |
265 | { | |
266 | DIR *d; | |
267 | char dirname[PATH_MAX]; | |
268 | ||
269 | snprintf(dirname, sizeof(dirname), | |
270 | "%s/" PCI_PRI_FMT "/msi_irqs", pci_get_sysfs_path(), | |
271 | loc->domain, loc->bus, loc->devid, loc->function); | |
272 | ||
273 | d = opendir(dirname); | |
274 | if (d) | |
275 | closedir(d); | |
276 | ||
277 | return d != NULL; | |
278 | } | |
279 | #else | |
280 | static int | |
281 | legacy_virtio_has_msix(const struct rte_pci_addr *loc __rte_unused) | |
282 | { | |
283 | /* nic_uio does not enable interrupts, return 0 (false). */ | |
284 | return 0; | |
285 | } | |
286 | #endif | |
287 | ||
288 | static int | |
289 | legacy_virtio_resource_init(struct rte_pci_device *pci_dev, | |
290 | struct virtio_hw *hw, uint32_t *dev_flags) | |
291 | { | |
292 | if (rte_eal_pci_ioport_map(pci_dev, 0, &hw->io) < 0) | |
293 | return -1; | |
294 | ||
295 | if (pci_dev->intr_handle.type != RTE_INTR_HANDLE_UNKNOWN) | |
296 | *dev_flags |= RTE_ETH_DEV_INTR_LSC; | |
297 | else | |
298 | *dev_flags &= ~RTE_ETH_DEV_INTR_LSC; | |
299 | ||
300 | return 0; | |
301 | } | |
302 | ||
303 | static const struct virtio_pci_ops legacy_ops = { | |
304 | .read_dev_cfg = legacy_read_dev_config, | |
305 | .write_dev_cfg = legacy_write_dev_config, | |
306 | .reset = legacy_reset, | |
307 | .get_status = legacy_get_status, | |
308 | .set_status = legacy_set_status, | |
309 | .get_features = legacy_get_features, | |
310 | .set_features = legacy_set_features, | |
311 | .get_isr = legacy_get_isr, | |
312 | .set_config_irq = legacy_set_config_irq, | |
313 | .get_queue_num = legacy_get_queue_num, | |
314 | .setup_queue = legacy_setup_queue, | |
315 | .del_queue = legacy_del_queue, | |
316 | .notify_queue = legacy_notify_queue, | |
317 | }; | |
318 | ||
319 | ||
320 | static inline uint8_t | |
321 | io_read8(uint8_t *addr) | |
322 | { | |
323 | return *(volatile uint8_t *)addr; | |
324 | } | |
325 | ||
326 | static inline void | |
327 | io_write8(uint8_t val, uint8_t *addr) | |
328 | { | |
329 | *(volatile uint8_t *)addr = val; | |
330 | } | |
331 | ||
332 | static inline uint16_t | |
333 | io_read16(uint16_t *addr) | |
334 | { | |
335 | return *(volatile uint16_t *)addr; | |
336 | } | |
337 | ||
338 | static inline void | |
339 | io_write16(uint16_t val, uint16_t *addr) | |
340 | { | |
341 | *(volatile uint16_t *)addr = val; | |
342 | } | |
343 | ||
344 | static inline uint32_t | |
345 | io_read32(uint32_t *addr) | |
346 | { | |
347 | return *(volatile uint32_t *)addr; | |
348 | } | |
349 | ||
350 | static inline void | |
351 | io_write32(uint32_t val, uint32_t *addr) | |
352 | { | |
353 | *(volatile uint32_t *)addr = val; | |
354 | } | |
355 | ||
356 | static inline void | |
357 | io_write64_twopart(uint64_t val, uint32_t *lo, uint32_t *hi) | |
358 | { | |
359 | io_write32(val & ((1ULL << 32) - 1), lo); | |
360 | io_write32(val >> 32, hi); | |
361 | } | |
362 | ||
363 | static void | |
364 | modern_read_dev_config(struct virtio_hw *hw, size_t offset, | |
365 | void *dst, int length) | |
366 | { | |
367 | int i; | |
368 | uint8_t *p; | |
369 | uint8_t old_gen, new_gen; | |
370 | ||
371 | do { | |
372 | old_gen = io_read8(&hw->common_cfg->config_generation); | |
373 | ||
374 | p = dst; | |
375 | for (i = 0; i < length; i++) | |
376 | *p++ = io_read8((uint8_t *)hw->dev_cfg + offset + i); | |
377 | ||
378 | new_gen = io_read8(&hw->common_cfg->config_generation); | |
379 | } while (old_gen != new_gen); | |
380 | } | |
381 | ||
382 | static void | |
383 | modern_write_dev_config(struct virtio_hw *hw, size_t offset, | |
384 | const void *src, int length) | |
385 | { | |
386 | int i; | |
387 | const uint8_t *p = src; | |
388 | ||
389 | for (i = 0; i < length; i++) | |
390 | io_write8(*p++, (uint8_t *)hw->dev_cfg + offset + i); | |
391 | } | |
392 | ||
393 | static uint64_t | |
394 | modern_get_features(struct virtio_hw *hw) | |
395 | { | |
396 | uint32_t features_lo, features_hi; | |
397 | ||
398 | io_write32(0, &hw->common_cfg->device_feature_select); | |
399 | features_lo = io_read32(&hw->common_cfg->device_feature); | |
400 | ||
401 | io_write32(1, &hw->common_cfg->device_feature_select); | |
402 | features_hi = io_read32(&hw->common_cfg->device_feature); | |
403 | ||
404 | return ((uint64_t)features_hi << 32) | features_lo; | |
405 | } | |
406 | ||
407 | static void | |
408 | modern_set_features(struct virtio_hw *hw, uint64_t features) | |
409 | { | |
410 | io_write32(0, &hw->common_cfg->guest_feature_select); | |
411 | io_write32(features & ((1ULL << 32) - 1), | |
412 | &hw->common_cfg->guest_feature); | |
413 | ||
414 | io_write32(1, &hw->common_cfg->guest_feature_select); | |
415 | io_write32(features >> 32, | |
416 | &hw->common_cfg->guest_feature); | |
417 | } | |
418 | ||
419 | static uint8_t | |
420 | modern_get_status(struct virtio_hw *hw) | |
421 | { | |
422 | return io_read8(&hw->common_cfg->device_status); | |
423 | } | |
424 | ||
425 | static void | |
426 | modern_set_status(struct virtio_hw *hw, uint8_t status) | |
427 | { | |
428 | io_write8(status, &hw->common_cfg->device_status); | |
429 | } | |
430 | ||
431 | static void | |
432 | modern_reset(struct virtio_hw *hw) | |
433 | { | |
434 | modern_set_status(hw, VIRTIO_CONFIG_STATUS_RESET); | |
435 | modern_get_status(hw); | |
436 | } | |
437 | ||
438 | static uint8_t | |
439 | modern_get_isr(struct virtio_hw *hw) | |
440 | { | |
441 | return io_read8(hw->isr); | |
442 | } | |
443 | ||
444 | static uint16_t | |
445 | modern_set_config_irq(struct virtio_hw *hw, uint16_t vec) | |
446 | { | |
447 | io_write16(vec, &hw->common_cfg->msix_config); | |
448 | return io_read16(&hw->common_cfg->msix_config); | |
449 | } | |
450 | ||
451 | static uint16_t | |
452 | modern_get_queue_num(struct virtio_hw *hw, uint16_t queue_id) | |
453 | { | |
454 | io_write16(queue_id, &hw->common_cfg->queue_select); | |
455 | return io_read16(&hw->common_cfg->queue_size); | |
456 | } | |
457 | ||
458 | static int | |
459 | modern_setup_queue(struct virtio_hw *hw, struct virtqueue *vq) | |
460 | { | |
461 | uint64_t desc_addr, avail_addr, used_addr; | |
462 | uint16_t notify_off; | |
463 | ||
464 | if (!check_vq_phys_addr_ok(vq)) | |
465 | return -1; | |
466 | ||
467 | desc_addr = vq->vq_ring_mem; | |
468 | avail_addr = desc_addr + vq->vq_nentries * sizeof(struct vring_desc); | |
469 | used_addr = RTE_ALIGN_CEIL(avail_addr + offsetof(struct vring_avail, | |
470 | ring[vq->vq_nentries]), | |
471 | VIRTIO_PCI_VRING_ALIGN); | |
472 | ||
473 | io_write16(vq->vq_queue_index, &hw->common_cfg->queue_select); | |
474 | ||
475 | io_write64_twopart(desc_addr, &hw->common_cfg->queue_desc_lo, | |
476 | &hw->common_cfg->queue_desc_hi); | |
477 | io_write64_twopart(avail_addr, &hw->common_cfg->queue_avail_lo, | |
478 | &hw->common_cfg->queue_avail_hi); | |
479 | io_write64_twopart(used_addr, &hw->common_cfg->queue_used_lo, | |
480 | &hw->common_cfg->queue_used_hi); | |
481 | ||
482 | notify_off = io_read16(&hw->common_cfg->queue_notify_off); | |
483 | vq->notify_addr = (void *)((uint8_t *)hw->notify_base + | |
484 | notify_off * hw->notify_off_multiplier); | |
485 | ||
486 | io_write16(1, &hw->common_cfg->queue_enable); | |
487 | ||
488 | PMD_INIT_LOG(DEBUG, "queue %u addresses:", vq->vq_queue_index); | |
489 | PMD_INIT_LOG(DEBUG, "\t desc_addr: %" PRIx64, desc_addr); | |
490 | PMD_INIT_LOG(DEBUG, "\t aval_addr: %" PRIx64, avail_addr); | |
491 | PMD_INIT_LOG(DEBUG, "\t used_addr: %" PRIx64, used_addr); | |
492 | PMD_INIT_LOG(DEBUG, "\t notify addr: %p (notify offset: %u)", | |
493 | vq->notify_addr, notify_off); | |
494 | ||
495 | return 0; | |
496 | } | |
497 | ||
498 | static void | |
499 | modern_del_queue(struct virtio_hw *hw, struct virtqueue *vq) | |
500 | { | |
501 | io_write16(vq->vq_queue_index, &hw->common_cfg->queue_select); | |
502 | ||
503 | io_write64_twopart(0, &hw->common_cfg->queue_desc_lo, | |
504 | &hw->common_cfg->queue_desc_hi); | |
505 | io_write64_twopart(0, &hw->common_cfg->queue_avail_lo, | |
506 | &hw->common_cfg->queue_avail_hi); | |
507 | io_write64_twopart(0, &hw->common_cfg->queue_used_lo, | |
508 | &hw->common_cfg->queue_used_hi); | |
509 | ||
510 | io_write16(0, &hw->common_cfg->queue_enable); | |
511 | } | |
512 | ||
513 | static void | |
514 | modern_notify_queue(struct virtio_hw *hw __rte_unused, struct virtqueue *vq) | |
515 | { | |
516 | io_write16(1, vq->notify_addr); | |
517 | } | |
518 | ||
519 | static const struct virtio_pci_ops modern_ops = { | |
520 | .read_dev_cfg = modern_read_dev_config, | |
521 | .write_dev_cfg = modern_write_dev_config, | |
522 | .reset = modern_reset, | |
523 | .get_status = modern_get_status, | |
524 | .set_status = modern_set_status, | |
525 | .get_features = modern_get_features, | |
526 | .set_features = modern_set_features, | |
527 | .get_isr = modern_get_isr, | |
528 | .set_config_irq = modern_set_config_irq, | |
529 | .get_queue_num = modern_get_queue_num, | |
530 | .setup_queue = modern_setup_queue, | |
531 | .del_queue = modern_del_queue, | |
532 | .notify_queue = modern_notify_queue, | |
533 | }; | |
534 | ||
535 | ||
536 | void | |
537 | vtpci_read_dev_config(struct virtio_hw *hw, size_t offset, | |
538 | void *dst, int length) | |
539 | { | |
540 | hw->vtpci_ops->read_dev_cfg(hw, offset, dst, length); | |
541 | } | |
542 | ||
543 | void | |
544 | vtpci_write_dev_config(struct virtio_hw *hw, size_t offset, | |
545 | const void *src, int length) | |
546 | { | |
547 | hw->vtpci_ops->write_dev_cfg(hw, offset, src, length); | |
548 | } | |
549 | ||
550 | uint64_t | |
551 | vtpci_negotiate_features(struct virtio_hw *hw, uint64_t host_features) | |
552 | { | |
553 | uint64_t features; | |
554 | ||
555 | /* | |
556 | * Limit negotiated features to what the driver, virtqueue, and | |
557 | * host all support. | |
558 | */ | |
559 | features = host_features & hw->guest_features; | |
560 | hw->vtpci_ops->set_features(hw, features); | |
561 | ||
562 | return features; | |
563 | } | |
564 | ||
565 | void | |
566 | vtpci_reset(struct virtio_hw *hw) | |
567 | { | |
568 | hw->vtpci_ops->set_status(hw, VIRTIO_CONFIG_STATUS_RESET); | |
569 | /* flush status write */ | |
570 | hw->vtpci_ops->get_status(hw); | |
571 | } | |
572 | ||
573 | void | |
574 | vtpci_reinit_complete(struct virtio_hw *hw) | |
575 | { | |
576 | vtpci_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER_OK); | |
577 | } | |
578 | ||
579 | void | |
580 | vtpci_set_status(struct virtio_hw *hw, uint8_t status) | |
581 | { | |
582 | if (status != VIRTIO_CONFIG_STATUS_RESET) | |
583 | status |= hw->vtpci_ops->get_status(hw); | |
584 | ||
585 | hw->vtpci_ops->set_status(hw, status); | |
586 | } | |
587 | ||
588 | uint8_t | |
589 | vtpci_get_status(struct virtio_hw *hw) | |
590 | { | |
591 | return hw->vtpci_ops->get_status(hw); | |
592 | } | |
593 | ||
594 | uint8_t | |
595 | vtpci_isr(struct virtio_hw *hw) | |
596 | { | |
597 | return hw->vtpci_ops->get_isr(hw); | |
598 | } | |
599 | ||
600 | ||
601 | /* Enable one vector (0) for Link State Intrerrupt */ | |
602 | uint16_t | |
603 | vtpci_irq_config(struct virtio_hw *hw, uint16_t vec) | |
604 | { | |
605 | return hw->vtpci_ops->set_config_irq(hw, vec); | |
606 | } | |
607 | ||
608 | static void * | |
609 | get_cfg_addr(struct rte_pci_device *dev, struct virtio_pci_cap *cap) | |
610 | { | |
611 | uint8_t bar = cap->bar; | |
612 | uint32_t length = cap->length; | |
613 | uint32_t offset = cap->offset; | |
614 | uint8_t *base; | |
615 | ||
616 | if (bar > 5) { | |
617 | PMD_INIT_LOG(ERR, "invalid bar: %u", bar); | |
618 | return NULL; | |
619 | } | |
620 | ||
621 | if (offset + length < offset) { | |
622 | PMD_INIT_LOG(ERR, "offset(%u) + length(%u) overflows", | |
623 | offset, length); | |
624 | return NULL; | |
625 | } | |
626 | ||
627 | if (offset + length > dev->mem_resource[bar].len) { | |
628 | PMD_INIT_LOG(ERR, | |
629 | "invalid cap: overflows bar space: %u > %" PRIu64, | |
630 | offset + length, dev->mem_resource[bar].len); | |
631 | return NULL; | |
632 | } | |
633 | ||
634 | base = dev->mem_resource[bar].addr; | |
635 | if (base == NULL) { | |
636 | PMD_INIT_LOG(ERR, "bar %u base addr is NULL", bar); | |
637 | return NULL; | |
638 | } | |
639 | ||
640 | return base + offset; | |
641 | } | |
642 | ||
643 | static int | |
644 | virtio_read_caps(struct rte_pci_device *dev, struct virtio_hw *hw) | |
645 | { | |
646 | uint8_t pos; | |
647 | struct virtio_pci_cap cap; | |
648 | int ret; | |
649 | ||
650 | if (rte_eal_pci_map_device(dev)) { | |
651 | PMD_INIT_LOG(DEBUG, "failed to map pci device!"); | |
652 | return -1; | |
653 | } | |
654 | ||
655 | ret = rte_eal_pci_read_config(dev, &pos, 1, PCI_CAPABILITY_LIST); | |
656 | if (ret < 0) { | |
657 | PMD_INIT_LOG(DEBUG, "failed to read pci capability list"); | |
658 | return -1; | |
659 | } | |
660 | ||
661 | while (pos) { | |
662 | ret = rte_eal_pci_read_config(dev, &cap, sizeof(cap), pos); | |
663 | if (ret < 0) { | |
664 | PMD_INIT_LOG(ERR, | |
665 | "failed to read pci cap at pos: %x", pos); | |
666 | break; | |
667 | } | |
668 | ||
669 | if (cap.cap_vndr != PCI_CAP_ID_VNDR) { | |
670 | PMD_INIT_LOG(DEBUG, | |
671 | "[%2x] skipping non VNDR cap id: %02x", | |
672 | pos, cap.cap_vndr); | |
673 | goto next; | |
674 | } | |
675 | ||
676 | PMD_INIT_LOG(DEBUG, | |
677 | "[%2x] cfg type: %u, bar: %u, offset: %04x, len: %u", | |
678 | pos, cap.cfg_type, cap.bar, cap.offset, cap.length); | |
679 | ||
680 | switch (cap.cfg_type) { | |
681 | case VIRTIO_PCI_CAP_COMMON_CFG: | |
682 | hw->common_cfg = get_cfg_addr(dev, &cap); | |
683 | break; | |
684 | case VIRTIO_PCI_CAP_NOTIFY_CFG: | |
685 | rte_eal_pci_read_config(dev, &hw->notify_off_multiplier, | |
686 | 4, pos + sizeof(cap)); | |
687 | hw->notify_base = get_cfg_addr(dev, &cap); | |
688 | break; | |
689 | case VIRTIO_PCI_CAP_DEVICE_CFG: | |
690 | hw->dev_cfg = get_cfg_addr(dev, &cap); | |
691 | break; | |
692 | case VIRTIO_PCI_CAP_ISR_CFG: | |
693 | hw->isr = get_cfg_addr(dev, &cap); | |
694 | break; | |
695 | } | |
696 | ||
697 | next: | |
698 | pos = cap.cap_next; | |
699 | } | |
700 | ||
701 | if (hw->common_cfg == NULL || hw->notify_base == NULL || | |
702 | hw->dev_cfg == NULL || hw->isr == NULL) { | |
703 | PMD_INIT_LOG(INFO, "no modern virtio pci device found."); | |
704 | return -1; | |
705 | } | |
706 | ||
707 | PMD_INIT_LOG(INFO, "found modern virtio pci device."); | |
708 | ||
709 | PMD_INIT_LOG(DEBUG, "common cfg mapped at: %p", hw->common_cfg); | |
710 | PMD_INIT_LOG(DEBUG, "device cfg mapped at: %p", hw->dev_cfg); | |
711 | PMD_INIT_LOG(DEBUG, "isr cfg mapped at: %p", hw->isr); | |
712 | PMD_INIT_LOG(DEBUG, "notify base: %p, notify off multiplier: %u", | |
713 | hw->notify_base, hw->notify_off_multiplier); | |
714 | ||
715 | return 0; | |
716 | } | |
717 | ||
718 | /* | |
719 | * Return -1: | |
720 | * if there is error mapping with VFIO/UIO. | |
721 | * if port map error when driver type is KDRV_NONE. | |
722 | * if whitelisted but driver type is KDRV_UNKNOWN. | |
723 | * Return 1 if kernel driver is managing the device. | |
724 | * Return 0 on success. | |
725 | */ | |
726 | int | |
727 | vtpci_init(struct rte_pci_device *dev, struct virtio_hw *hw, | |
728 | uint32_t *dev_flags) | |
729 | { | |
730 | hw->dev = dev; | |
731 | ||
732 | /* | |
733 | * Try if we can succeed reading virtio pci caps, which exists | |
734 | * only on modern pci device. If failed, we fallback to legacy | |
735 | * virtio handling. | |
736 | */ | |
737 | if (virtio_read_caps(dev, hw) == 0) { | |
738 | PMD_INIT_LOG(INFO, "modern virtio pci detected."); | |
739 | hw->vtpci_ops = &modern_ops; | |
740 | hw->modern = 1; | |
741 | *dev_flags |= RTE_ETH_DEV_INTR_LSC; | |
742 | return 0; | |
743 | } | |
744 | ||
745 | PMD_INIT_LOG(INFO, "trying with legacy virtio pci."); | |
746 | if (legacy_virtio_resource_init(dev, hw, dev_flags) < 0) { | |
747 | if (dev->kdrv == RTE_KDRV_UNKNOWN && | |
748 | (!dev->device.devargs || | |
749 | dev->device.devargs->type != | |
750 | RTE_DEVTYPE_WHITELISTED_PCI)) { | |
751 | PMD_INIT_LOG(INFO, | |
752 | "skip kernel managed virtio device."); | |
753 | return 1; | |
754 | } | |
755 | return -1; | |
756 | } | |
757 | ||
758 | hw->vtpci_ops = &legacy_ops; | |
759 | hw->use_msix = legacy_virtio_has_msix(&dev->addr); | |
760 | hw->modern = 0; | |
761 | ||
762 | return 0; | |
763 | } |